|
@@ -63,15 +63,29 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
|
|
|
case OMAPDSS_VER_OMAP3630:
|
|
|
case OMAPDSS_VER_AM35xx:
|
|
|
return NULL;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
|
|
|
- switch (channel) {
|
|
|
- case OMAP_DSS_CHANNEL_LCD:
|
|
|
- return dsi_get_dsidev_from_id(0);
|
|
|
- case OMAP_DSS_CHANNEL_LCD2:
|
|
|
- return dsi_get_dsidev_from_id(1);
|
|
|
+ case OMAPDSS_VER_OMAP4430_ES1:
|
|
|
+ case OMAPDSS_VER_OMAP4430_ES2:
|
|
|
+ case OMAPDSS_VER_OMAP4:
|
|
|
+ switch (channel) {
|
|
|
+ case OMAP_DSS_CHANNEL_LCD:
|
|
|
+ return dsi_get_dsidev_from_id(0);
|
|
|
+ case OMAP_DSS_CHANNEL_LCD2:
|
|
|
+ return dsi_get_dsidev_from_id(1);
|
|
|
+ default:
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ case OMAPDSS_VER_OMAP5:
|
|
|
+ switch (channel) {
|
|
|
+ case OMAP_DSS_CHANNEL_LCD:
|
|
|
+ return dsi_get_dsidev_from_id(0);
|
|
|
+ case OMAP_DSS_CHANNEL_LCD3:
|
|
|
+ return dsi_get_dsidev_from_id(1);
|
|
|
+ default:
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
default:
|
|
|
return NULL;
|
|
|
}
|
|
@@ -91,75 +105,211 @@ static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
|
|
|
+struct dpi_clk_calc_ctx {
|
|
|
+ struct platform_device *dsidev;
|
|
|
+
|
|
|
+ /* inputs */
|
|
|
+
|
|
|
+ unsigned long pck_min, pck_max;
|
|
|
+
|
|
|
+ /* outputs */
|
|
|
+
|
|
|
+ struct dsi_clock_info dsi_cinfo;
|
|
|
+ struct dss_clock_info dss_cinfo;
|
|
|
+ struct dispc_clock_info dispc_cinfo;
|
|
|
+};
|
|
|
+
|
|
|
+static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
|
|
+ unsigned long pck, void *data)
|
|
|
+{
|
|
|
+ struct dpi_clk_calc_ctx *ctx = data;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Odd dividers give us uneven duty cycle, causing problem when level
|
|
|
+ * shifted. So skip all odd dividers when the pixel clock is on the
|
|
|
+ * higher side.
|
|
|
+ */
|
|
|
+ if (ctx->pck_min >= 1000000) {
|
|
|
+ if (lckd > 1 && lckd % 2 != 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (pckd > 1 && pckd % 2 != 0)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx->dispc_cinfo.lck_div = lckd;
|
|
|
+ ctx->dispc_cinfo.pck_div = pckd;
|
|
|
+ ctx->dispc_cinfo.lck = lck;
|
|
|
+ ctx->dispc_cinfo.pck = pck;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct dpi_clk_calc_ctx *ctx = data;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Odd dividers give us uneven duty cycle, causing problem when level
|
|
|
+ * shifted. So skip all odd dividers when the pixel clock is on the
|
|
|
+ * higher side.
|
|
|
+ */
|
|
|
+ if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 1000000)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ ctx->dsi_cinfo.regm_dispc = regm_dispc;
|
|
|
+ ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
|
|
|
+
|
|
|
+ return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
|
|
|
+ dpi_calc_dispc_cb, ctx);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint,
|
|
|
+ unsigned long pll,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct dpi_clk_calc_ctx *ctx = data;
|
|
|
+
|
|
|
+ ctx->dsi_cinfo.regn = regn;
|
|
|
+ ctx->dsi_cinfo.regm = regm;
|
|
|
+ ctx->dsi_cinfo.fint = fint;
|
|
|
+ ctx->dsi_cinfo.clkin4ddr = pll;
|
|
|
+
|
|
|
+ return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min,
|
|
|
+ dpi_calc_hsdiv_cb, ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static bool dpi_calc_dss_cb(int fckd, unsigned long fck, void *data)
|
|
|
+{
|
|
|
+ struct dpi_clk_calc_ctx *ctx = data;
|
|
|
+
|
|
|
+ ctx->dss_cinfo.fck = fck;
|
|
|
+ ctx->dss_cinfo.fck_div = fckd;
|
|
|
+
|
|
|
+ return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
|
|
|
+ dpi_calc_dispc_cb, ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
|
|
|
+{
|
|
|
+ unsigned long clkin;
|
|
|
+ unsigned long pll_min, pll_max;
|
|
|
+
|
|
|
+ clkin = dsi_get_pll_clkin(dpi.dsidev);
|
|
|
+
|
|
|
+ memset(ctx, 0, sizeof(*ctx));
|
|
|
+ ctx->dsidev = dpi.dsidev;
|
|
|
+ ctx->pck_min = pck - 1000;
|
|
|
+ ctx->pck_max = pck + 1000;
|
|
|
+ ctx->dsi_cinfo.clkin = clkin;
|
|
|
+
|
|
|
+ pll_min = 0;
|
|
|
+ pll_max = 0;
|
|
|
+
|
|
|
+ return dsi_pll_calc(dpi.dsidev, clkin,
|
|
|
+ pll_min, pll_max,
|
|
|
+ dpi_calc_pll_cb, ctx);
|
|
|
+}
|
|
|
+
|
|
|
+static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * DSS fck gives us very few possibilities, so finding a good pixel
|
|
|
+ * clock may not be possible. We try multiple times to find the clock,
|
|
|
+ * each time widening the pixel clock range we look for, up to
|
|
|
+ * +/- ~15MHz.
|
|
|
+ */
|
|
|
+
|
|
|
+ for (i = 0; i < 25; ++i) {
|
|
|
+ bool ok;
|
|
|
+
|
|
|
+ memset(ctx, 0, sizeof(*ctx));
|
|
|
+ if (pck > 1000 * i * i * i)
|
|
|
+ ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
|
|
|
+ else
|
|
|
+ ctx->pck_min = 0;
|
|
|
+ ctx->pck_max = pck + 1000 * i * i * i;
|
|
|
+
|
|
|
+ ok = dss_div_calc(ctx->pck_min, dpi_calc_dss_cb, ctx);
|
|
|
+ if (ok)
|
|
|
+ return ok;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static int dpi_set_dsi_clk(enum omap_channel channel,
|
|
|
unsigned long pck_req, unsigned long *fck, int *lck_div,
|
|
|
int *pck_div)
|
|
|
{
|
|
|
- struct omap_overlay_manager *mgr = dssdev->output->manager;
|
|
|
- struct dsi_clock_info dsi_cinfo;
|
|
|
- struct dispc_clock_info dispc_cinfo;
|
|
|
+ struct dpi_clk_calc_ctx ctx;
|
|
|
int r;
|
|
|
+ bool ok;
|
|
|
|
|
|
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
|
|
|
- &dispc_cinfo);
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
+ ok = dpi_dsi_clk_calc(pck_req, &ctx);
|
|
|
+ if (!ok)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo);
|
|
|
+ r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo);
|
|
|
if (r)
|
|
|
return r;
|
|
|
|
|
|
- dss_select_lcd_clk_source(mgr->id,
|
|
|
- dpi_get_alt_clk_src(mgr->id));
|
|
|
+ dss_select_lcd_clk_source(channel,
|
|
|
+ dpi_get_alt_clk_src(channel));
|
|
|
|
|
|
- dpi.mgr_config.clock_info = dispc_cinfo;
|
|
|
+ dpi.mgr_config.clock_info = ctx.dispc_cinfo;
|
|
|
|
|
|
- *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
|
|
|
- *lck_div = dispc_cinfo.lck_div;
|
|
|
- *pck_div = dispc_cinfo.pck_div;
|
|
|
+ *fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
|
|
|
+ *lck_div = ctx.dispc_cinfo.lck_div;
|
|
|
+ *pck_div = ctx.dispc_cinfo.pck_div;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
|
|
|
- unsigned long pck_req, unsigned long *fck, int *lck_div,
|
|
|
- int *pck_div)
|
|
|
+static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck,
|
|
|
+ int *lck_div, int *pck_div)
|
|
|
{
|
|
|
- struct dss_clock_info dss_cinfo;
|
|
|
- struct dispc_clock_info dispc_cinfo;
|
|
|
+ struct dpi_clk_calc_ctx ctx;
|
|
|
int r;
|
|
|
+ bool ok;
|
|
|
|
|
|
- r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
+ ok = dpi_dss_clk_calc(pck_req, &ctx);
|
|
|
+ if (!ok)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- r = dss_set_clock_div(&dss_cinfo);
|
|
|
+ r = dss_set_clock_div(&ctx.dss_cinfo);
|
|
|
if (r)
|
|
|
return r;
|
|
|
|
|
|
- dpi.mgr_config.clock_info = dispc_cinfo;
|
|
|
+ dpi.mgr_config.clock_info = ctx.dispc_cinfo;
|
|
|
|
|
|
- *fck = dss_cinfo.fck;
|
|
|
- *lck_div = dispc_cinfo.lck_div;
|
|
|
- *pck_div = dispc_cinfo.pck_div;
|
|
|
+ *fck = ctx.dss_cinfo.fck;
|
|
|
+ *lck_div = ctx.dispc_cinfo.lck_div;
|
|
|
+ *pck_div = ctx.dispc_cinfo.pck_div;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int dpi_set_mode(struct omap_dss_device *dssdev)
|
|
|
+static int dpi_set_mode(struct omap_overlay_manager *mgr)
|
|
|
{
|
|
|
struct omap_video_timings *t = &dpi.timings;
|
|
|
- struct omap_overlay_manager *mgr = dssdev->output->manager;
|
|
|
int lck_div = 0, pck_div = 0;
|
|
|
unsigned long fck = 0;
|
|
|
unsigned long pck;
|
|
|
int r = 0;
|
|
|
|
|
|
if (dpi.dsidev)
|
|
|
- r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
|
|
|
+ r = dpi_set_dsi_clk(mgr->id, t->pixel_clock * 1000, &fck,
|
|
|
&lck_div, &pck_div);
|
|
|
else
|
|
|
- r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
|
|
|
+ r = dpi_set_dispc_clk(t->pixel_clock * 1000, &fck,
|
|
|
&lck_div, &pck_div);
|
|
|
if (r)
|
|
|
return r;
|
|
@@ -179,10 +329,8 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
|
|
|
+static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr)
|
|
|
{
|
|
|
- struct omap_overlay_manager *mgr = dssdev->output->manager;
|
|
|
-
|
|
|
dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
|
|
|
|
|
|
dpi.mgr_config.stallmode = false;
|
|
@@ -197,7 +345,7 @@ static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
|
|
|
|
|
|
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
|
|
|
{
|
|
|
- struct omap_dss_output *out = dssdev->output;
|
|
|
+ struct omap_dss_output *out = &dpi.output;
|
|
|
int r;
|
|
|
|
|
|
mutex_lock(&dpi.lock);
|
|
@@ -230,7 +378,7 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
|
|
|
if (r)
|
|
|
goto err_get_dispc;
|
|
|
|
|
|
- r = dss_dpi_select_source(dssdev->channel);
|
|
|
+ r = dss_dpi_select_source(out->manager->id);
|
|
|
if (r)
|
|
|
goto err_src_sel;
|
|
|
|
|
@@ -244,11 +392,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
|
|
|
goto err_dsi_pll_init;
|
|
|
}
|
|
|
|
|
|
- r = dpi_set_mode(dssdev);
|
|
|
+ r = dpi_set_mode(out->manager);
|
|
|
if (r)
|
|
|
goto err_set_mode;
|
|
|
|
|
|
- dpi_config_lcd_manager(dssdev);
|
|
|
+ dpi_config_lcd_manager(out->manager);
|
|
|
|
|
|
mdelay(2);
|
|
|
|
|
@@ -285,7 +433,7 @@ EXPORT_SYMBOL(omapdss_dpi_display_enable);
|
|
|
|
|
|
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
|
|
|
{
|
|
|
- struct omap_overlay_manager *mgr = dssdev->output->manager;
|
|
|
+ struct omap_overlay_manager *mgr = dpi.output.manager;
|
|
|
|
|
|
mutex_lock(&dpi.lock);
|
|
|
|
|
@@ -324,12 +472,12 @@ EXPORT_SYMBOL(omapdss_dpi_set_timings);
|
|
|
int dpi_check_timings(struct omap_dss_device *dssdev,
|
|
|
struct omap_video_timings *timings)
|
|
|
{
|
|
|
- int r;
|
|
|
- struct omap_overlay_manager *mgr = dssdev->output->manager;
|
|
|
+ struct omap_overlay_manager *mgr = dpi.output.manager;
|
|
|
int lck_div, pck_div;
|
|
|
unsigned long fck;
|
|
|
unsigned long pck;
|
|
|
- struct dispc_clock_info dispc_cinfo;
|
|
|
+ struct dpi_clk_calc_ctx ctx;
|
|
|
+ bool ok;
|
|
|
|
|
|
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
|
|
|
return -EINVAL;
|
|
@@ -338,28 +486,21 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (dpi.dsidev) {
|
|
|
- struct dsi_clock_info dsi_cinfo;
|
|
|
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
|
|
|
- timings->pixel_clock * 1000,
|
|
|
- &dsi_cinfo, &dispc_cinfo);
|
|
|
-
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
+ ok = dpi_dsi_clk_calc(timings->pixel_clock * 1000, &ctx);
|
|
|
+ if (!ok)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
|
|
|
+ fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
|
|
|
} else {
|
|
|
- struct dss_clock_info dss_cinfo;
|
|
|
- r = dss_calc_clock_div(timings->pixel_clock * 1000,
|
|
|
- &dss_cinfo, &dispc_cinfo);
|
|
|
+ ok = dpi_dss_clk_calc(timings->pixel_clock * 1000, &ctx);
|
|
|
+ if (!ok)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
-
|
|
|
- fck = dss_cinfo.fck;
|
|
|
+ fck = ctx.dss_cinfo.fck;
|
|
|
}
|
|
|
|
|
|
- lck_div = dispc_cinfo.lck_div;
|
|
|
- pck_div = dispc_cinfo.pck_div;
|
|
|
+ lck_div = ctx.dispc_cinfo.lck_div;
|
|
|
+ pck_div = ctx.dispc_cinfo.pck_div;
|
|
|
|
|
|
pck = fck / lck_div / pck_div / 1000;
|
|
|
|
|
@@ -401,6 +542,36 @@ static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Return a hardcoded channel for the DPI output. This should work for
|
|
|
+ * current use cases, but this can be later expanded to either resolve
|
|
|
+ * the channel in some more dynamic manner, or get the channel as a user
|
|
|
+ * parameter.
|
|
|
+ */
|
|
|
+static enum omap_channel dpi_get_channel(void)
|
|
|
+{
|
|
|
+ switch (omapdss_get_version()) {
|
|
|
+ case OMAPDSS_VER_OMAP24xx:
|
|
|
+ case OMAPDSS_VER_OMAP34xx_ES1:
|
|
|
+ case OMAPDSS_VER_OMAP34xx_ES3:
|
|
|
+ case OMAPDSS_VER_OMAP3630:
|
|
|
+ case OMAPDSS_VER_AM35xx:
|
|
|
+ return OMAP_DSS_CHANNEL_LCD;
|
|
|
+
|
|
|
+ case OMAPDSS_VER_OMAP4430_ES1:
|
|
|
+ case OMAPDSS_VER_OMAP4430_ES2:
|
|
|
+ case OMAPDSS_VER_OMAP4:
|
|
|
+ return OMAP_DSS_CHANNEL_LCD2;
|
|
|
+
|
|
|
+ case OMAPDSS_VER_OMAP5:
|
|
|
+ return OMAP_DSS_CHANNEL_LCD3;
|
|
|
+
|
|
|
+ default:
|
|
|
+ DSSWARN("unsupported DSS version\n");
|
|
|
+ return OMAP_DSS_CHANNEL_LCD;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int __init dpi_init_display(struct omap_dss_device *dssdev)
|
|
|
{
|
|
|
struct platform_device *dsidev;
|
|
@@ -421,12 +592,7 @@ static int __init dpi_init_display(struct omap_dss_device *dssdev)
|
|
|
dpi.vdds_dsi_reg = vdds_dsi;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
|
|
|
- * source for DPI is SoC integration detail, not something that should
|
|
|
- * be configured in the dssdev
|
|
|
- */
|
|
|
- dsidev = dpi_get_dsidev(dssdev->channel);
|
|
|
+ dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
|
|
|
|
|
|
if (dsidev && dpi_verify_dsi_pll(dsidev)) {
|
|
|
dsidev = NULL;
|
|
@@ -517,6 +683,8 @@ static void __init dpi_init_output(struct platform_device *pdev)
|
|
|
out->pdev = pdev;
|
|
|
out->id = OMAP_DSS_OUTPUT_DPI;
|
|
|
out->type = OMAP_DISPLAY_TYPE_DPI;
|
|
|
+ out->name = "dpi.0";
|
|
|
+ out->dispc_channel = dpi_get_channel();
|
|
|
|
|
|
dss_register_output(out);
|
|
|
}
|