aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2012-08-10 09:50:51 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2012-09-07 13:02:05 -0400
commitee144e645a081daad5de1ccac77f0a0e98e6a67b (patch)
tree183396b4aab719b9bece60190885a3d8e5e99676 /drivers/video/omap2
parentbc63f3044155fe90e76c9bb6dd2538b586ee02bc (diff)
OMAPDSS: DSI: calculate dsi clock
Currently the way to configure clocks related to DSI (both DSI and DISPC clocks) happens via omapdss platform data. The reason for this is that configuring the DSS clocks is a very complex problem, and it's impossible for the SW to know requirements about things like interference. However, for general cases it should be fine to calculate the dividers for clocks in the SW. The calculated clocks are probably not perfect, but should work. This patch adds support to calculate the dividers when using DSI command mode panels. The panel gives the required DDR clock rate and LP clock rate, and the DSI driver configures itself and DISPC accordingly. This patch is somewhat ugly, though. The code does its job by modifying the platform data where the clock dividers would be if the board file gave them. This is not how it's going to be in the future, but allows us to have quite simple patch and keep the backward compatibility. It also allows the developer to still give the exact dividers from the board file when there's need for that, as long as the panel driver does not override them. There are also other areas for improvement. For example, it would be better if the panel driver could ask for a DSI clock in a certain range, as, at least command mode panels, the panel can work fine with many different clock speeds. While the patch is not perfect, it allows us to remove the hardcoded clock dividers from the board file, making it easier to bring up a new panel and to use device tree from omapdss. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video/omap2')
-rw-r--r--drivers/video/omap2/displays/panel-taal.c6
-rw-r--r--drivers/video/omap2/dss/dsi.c126
2 files changed, 132 insertions, 0 deletions
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 77aed0e51f9b..ddda96a52d06 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -1065,6 +1065,12 @@ static int taal_power_on(struct omap_dss_device *dssdev)
1065 omapdss_dsi_set_pixel_format(dssdev, OMAP_DSS_DSI_FMT_RGB888); 1065 omapdss_dsi_set_pixel_format(dssdev, OMAP_DSS_DSI_FMT_RGB888);
1066 omapdss_dsi_set_operation_mode(dssdev, OMAP_DSS_DSI_CMD_MODE); 1066 omapdss_dsi_set_operation_mode(dssdev, OMAP_DSS_DSI_CMD_MODE);
1067 1067
1068 r = omapdss_dsi_set_clocks(dssdev, 216000000, 10000000);
1069 if (r) {
1070 dev_err(&dssdev->dev, "failed to set HS and LP clocks\n");
1071 goto err0;
1072 }
1073
1068 r = omapdss_dsi_display_enable(dssdev); 1074 r = omapdss_dsi_display_enable(dssdev);
1069 if (r) { 1075 if (r) {
1070 dev_err(&dssdev->dev, "failed to enable DSI\n"); 1076 dev_err(&dssdev->dev, "failed to enable DSI\n");
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 96d0024ada40..340c832d21d8 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -1454,6 +1454,68 @@ found:
1454 return 0; 1454 return 0;
1455} 1455}
1456 1456
1457static int dsi_pll_calc_ddrfreq(struct platform_device *dsidev,
1458 unsigned long req_clk, struct dsi_clock_info *cinfo)
1459{
1460 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1461 struct dsi_clock_info cur, best;
1462 unsigned long dss_sys_clk, max_dss_fck, max_dsi_fck;
1463 unsigned long req_clkin4ddr;
1464
1465 DSSDBG("dsi_pll_calc_ddrfreq\n");
1466
1467 dss_sys_clk = clk_get_rate(dsi->sys_clk);
1468
1469 max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
1470 max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK);
1471
1472 memset(&best, 0, sizeof(best));
1473 memset(&cur, 0, sizeof(cur));
1474
1475 cur.clkin = dss_sys_clk;
1476
1477 req_clkin4ddr = req_clk * 4;
1478
1479 for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) {
1480 cur.fint = cur.clkin / cur.regn;
1481
1482 if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min)
1483 continue;
1484
1485 /* DSIPHY(MHz) = (2 * regm / regn) * clkin */
1486 for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) {
1487 unsigned long a, b;
1488
1489 a = 2 * cur.regm * (cur.clkin/1000);
1490 b = cur.regn;
1491 cur.clkin4ddr = a / b * 1000;
1492
1493 if (cur.clkin4ddr > 1800 * 1000 * 1000)
1494 break;
1495
1496 if (abs(cur.clkin4ddr - req_clkin4ddr) <
1497 abs(best.clkin4ddr - req_clkin4ddr)) {
1498 best = cur;
1499 DSSDBG("best %ld\n", best.clkin4ddr);
1500 }
1501
1502 if (cur.clkin4ddr == req_clkin4ddr)
1503 goto found;
1504 }
1505 }
1506found:
1507 best.regm_dispc = DIV_ROUND_UP(best.clkin4ddr, max_dss_fck);
1508 best.dsi_pll_hsdiv_dispc_clk = best.clkin4ddr / best.regm_dispc;
1509
1510 best.regm_dsi = DIV_ROUND_UP(best.clkin4ddr, max_dsi_fck);
1511 best.dsi_pll_hsdiv_dsi_clk = best.clkin4ddr / best.regm_dsi;
1512
1513 if (cinfo)
1514 *cinfo = best;
1515
1516 return 0;
1517}
1518
1457int dsi_pll_set_clock_div(struct platform_device *dsidev, 1519int dsi_pll_set_clock_div(struct platform_device *dsidev,
1458 struct dsi_clock_info *cinfo) 1520 struct dsi_clock_info *cinfo)
1459{ 1521{
@@ -4110,6 +4172,70 @@ int omapdss_dsi_configure_pins(struct omap_dss_device *dssdev,
4110} 4172}
4111EXPORT_SYMBOL(omapdss_dsi_configure_pins); 4173EXPORT_SYMBOL(omapdss_dsi_configure_pins);
4112 4174
4175int omapdss_dsi_set_clocks(struct omap_dss_device *dssdev,
4176 unsigned long ddr_clk, unsigned long lp_clk)
4177{
4178 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4179 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4180 struct dsi_clock_info cinfo;
4181 struct dispc_clock_info dispc_cinfo;
4182 unsigned lp_clk_div;
4183 unsigned long dsi_fclk;
4184 int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
4185 unsigned long pck;
4186 int r;
4187
4188 DSSDBGF("ddr_clk %lu, lp_clk %lu", ddr_clk, lp_clk);
4189
4190 mutex_lock(&dsi->lock);
4191
4192 r = dsi_pll_calc_ddrfreq(dsidev, ddr_clk, &cinfo);
4193 if (r)
4194 goto err;
4195
4196 dssdev->clocks.dsi.regn = cinfo.regn;
4197 dssdev->clocks.dsi.regm = cinfo.regm;
4198 dssdev->clocks.dsi.regm_dispc = cinfo.regm_dispc;
4199 dssdev->clocks.dsi.regm_dsi = cinfo.regm_dsi;
4200
4201
4202 dsi_fclk = cinfo.dsi_pll_hsdiv_dsi_clk;
4203 lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk * 2);
4204
4205 dssdev->clocks.dsi.lp_clk_div = lp_clk_div;
4206
4207 /* pck = TxByteClkHS * datalanes * 8 / bitsperpixel */
4208
4209 pck = cinfo.clkin4ddr / 16 * (dsi->num_lanes_used - 1) * 8 / bpp;
4210
4211 DSSDBG("finding dispc dividers for pck %lu\n", pck);
4212
4213 dispc_find_clk_divs(pck, cinfo.dsi_pll_hsdiv_dispc_clk, &dispc_cinfo);
4214
4215 dssdev->clocks.dispc.channel.lck_div = dispc_cinfo.lck_div;
4216 dssdev->clocks.dispc.channel.pck_div = dispc_cinfo.pck_div;
4217
4218
4219 dssdev->clocks.dispc.dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK;
4220
4221 dssdev->clocks.dispc.channel.lcd_clk_src =
4222 dsi->module_id == 0 ?
4223 OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
4224 OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
4225
4226 dssdev->clocks.dsi.dsi_fclk_src =
4227 dsi->module_id == 0 ?
4228 OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
4229 OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI;
4230
4231 mutex_unlock(&dsi->lock);
4232 return 0;
4233err:
4234 mutex_unlock(&dsi->lock);
4235 return r;
4236}
4237EXPORT_SYMBOL(omapdss_dsi_set_clocks);
4238
4113int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) 4239int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
4114{ 4240{
4115 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4241 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);