blob: 5d636eca7bee8c0c57a1db7078832d0f9eb04bbb [file] [log] [blame]
/*
* (C) Copyright 2012
* Marvell Semiconductor <www.marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <malloc.h>
#include <mmp_disp.h>
#include <linux/types.h>
#ifndef CONFIG_PXA1U88
#include <power/pxa988_freq.h>
static unsigned int pll3_vco;
struct dsi_op_info {
unsigned int fps_max;
unsigned int fps_min;
unsigned int op_num;
unsigned int op_divider_num;
unsigned int pll3_vco_min;
unsigned int pll3_vco_max;
unsigned int pll3_divider_num;
unsigned int pll3_divider_table[6];
unsigned int pll3_op_index;
unsigned int op_table[3];
};
static struct dsi_op_info dsi_op_info_data = {
.fps_max = 70,
.fps_min = 50,
.op_num = 3,
.op_divider_num = 16,
.pll3_vco_min = 1200,
.pll3_vco_max = 2500,
.pll3_divider_num = 6,
.pll3_divider_table = {
8, 6, 4, 3, 2, 1
},
.pll3_op_index = 2,
.op_table = {
416, 624, 0
},
};
void calculate_dsi_clk(struct mmp_disp_plat_info *mi)
{
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct fb_videomode *modes = &mi->modes[0];
struct dsi_op_info *op_info;
u32 total_w, total_h, byteclk, bitclk, bitclk_max,
byteclk_max, bitclk_min, byteclk_min;
u32 op_num, dsi_divider_num, i, j;
u32 diff, diff_min, diff_count;
u32 fps_max, fps_min;
u32 def_op, temp_op;
char *cmdline;
if (!di)
return;
op_info = &dsi_op_info_data;
def_op = get_max_cpurate();
if (!def_op)
return;
/* Check current max cpu rate */
for (i = 0; i < op_info->pll3_divider_num; i++) {
if ((def_op * op_info->pll3_divider_table[i])
> op_info->pll3_vco_max)
continue;
if ((def_op * op_info->pll3_divider_table[i])
< op_info->pll3_vco_min) {
if ((def_op * op_info->pll3_divider_table[i+1])
< op_info->pll3_vco_max) {
def_op *= op_info->pll3_divider_table[i+1];
break;
}
}
}
/* Set shared cpu rate */
op_info->op_table[op_info->pll3_op_index] = def_op;
fps_max = op_info->fps_max;
fps_min = op_info->fps_min;
total_w = modes->xres + modes->left_margin +
modes->right_margin + modes->hsync_len;
total_h = modes->yres + modes->upper_margin +
modes->lower_margin + modes->vsync_len;
byteclk = ((total_w * (di->bpp >> 3)) * total_h *
modes->refresh) / di->lanes;
bitclk = byteclk << 3;
bitclk /= 1000000;
byteclk_max = ((total_w * (di->bpp >> 3)) * total_h *
fps_max) / di->lanes;
bitclk_max = byteclk_max << 3;
bitclk_max /= 1000000;
byteclk_min = ((total_w * (di->bpp >> 3)) * total_h *
fps_min) / di->lanes;
bitclk_min = byteclk_min << 3;
bitclk_min /= 1000000;
op_num = op_info->op_num;
dsi_divider_num = op_info->op_divider_num;
diff_min = bitclk;
diff_count = 0xff;
/* calculate bit clk for all ops */
for (j = 0; j < op_num; j++) {
for (i = 1; i < dsi_divider_num; i++) {
temp_op = op_info->op_table[j] / i;
if (temp_op > bitclk_min && temp_op < bitclk_max) {
diff = (temp_op > bitclk) ? (temp_op - bitclk) :
(bitclk - temp_op);
if (diff < diff_min) {
diff_min = diff;
diff_count = j;
}
}
}
}
/* If can't find the suitable bit clk, select pll3 directly */
if (diff_count == 0xff) {
/* Set the pll3 op only for dsi */
for (j = 1; j < dsi_divider_num; j++) {
if ((bitclk * j > op_info->pll3_vco_min) &&
(bitclk * j < op_info->pll3_vco_max)) {
pll3_vco = bitclk * j;
break;
}
}
}
/* If find the suitable bit clk is shared pll3 op, exit */
if (diff_count == op_info->pll3_op_index) {
pll3_vco = op_info->op_table[op_info->pll3_op_index];
return;
}
#ifdef CONFIG_CORE_1248
cmdline = malloc(COMMAND_LINE_SIZE);
strncpy(cmdline, getenv("bootargs"), COMMAND_LINE_SIZE);
sprintf(cmdline + strlen(cmdline), " pll3_vco=%d", PLL3_VCO_DEF);
setenv("bootargs", cmdline);
free(cmdline);
#else
if (pll3_vco) {
cmdline = malloc(COMMAND_LINE_SIZE);
strncpy(cmdline, getenv("bootargs"), COMMAND_LINE_SIZE);
sprintf(cmdline + strlen(cmdline), " pll3_vco=%d", pll3_vco);
setenv("bootargs", cmdline);
free(cmdline);
}
#endif
}
#else
void calculate_dsi_clk(struct mmp_disp_plat_info *mi)
{
}
#endif