}
EXPORT_SYMBOL(omapdss_dsi_enable_te);
- -void omapdss_dsi_set_timings(struct omap_dss_device *dssdev,
- - struct omap_video_timings *timings)
+ +#ifdef PRINT_VERBOSE_VM_TIMINGS
+ +static void print_dsi_vm(const char *str,
+ + const struct omap_dss_dsi_videomode_timings *t)
+ +{
+ + unsigned long byteclk = t->hsclk / 4;
+ + int bl, wc, pps, tot;
+ +
+ + wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
+ + pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
+ + bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
+ + tot = bl + pps;
+ +
+ +#define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
+ +
+ + pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
+ + "%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
+ + str,
+ + byteclk,
+ + t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
+ + bl, pps, tot,
+ + TO_DSI_T(t->hss),
+ + TO_DSI_T(t->hsa),
+ + TO_DSI_T(t->hse),
+ + TO_DSI_T(t->hbp),
+ + TO_DSI_T(pps),
+ + TO_DSI_T(t->hfp),
+ +
+ + TO_DSI_T(bl),
+ + TO_DSI_T(pps),
+ +
+ + TO_DSI_T(tot));
+ +#undef TO_DSI_T
+ +}
+ +
+ +static void print_dispc_vm(const char *str, const struct omap_video_timings *t)
+ +{
+ + unsigned long pck = t->pixel_clock * 1000;
+ + int hact, bl, tot;
+ +
+ + hact = t->x_res;
+ + bl = t->hsw + t->hbp + t->hfp;
+ + tot = hact + bl;
+ +
+ +#define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
+ +
+ + pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
+ + "%u/%u/%u/%u = %u + %u = %u\n",
+ + str,
+ + pck,
+ + t->hsw, t->hbp, hact, t->hfp,
+ + bl, hact, tot,
+ + TO_DISPC_T(t->hsw),
+ + TO_DISPC_T(t->hbp),
+ + TO_DISPC_T(hact),
+ + TO_DISPC_T(t->hfp),
+ + TO_DISPC_T(bl),
+ + TO_DISPC_T(hact),
+ + TO_DISPC_T(tot));
+ +#undef TO_DISPC_T
+ +}
+ +
+ +/* note: this is not quite accurate */
+ +static void print_dsi_dispc_vm(const char *str,
+ + const struct omap_dss_dsi_videomode_timings *t)
+ +{
+ + struct omap_video_timings vm = { 0 };
+ + unsigned long byteclk = t->hsclk / 4;
+ + unsigned long pck;
+ + u64 dsi_tput;
+ + int dsi_hact, dsi_htot;
+ +
+ + dsi_tput = (u64)byteclk * t->ndl * 8;
+ + pck = (u32)div64_u64(dsi_tput, t->bitspp);
+ + dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
+ + dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
+ +
+ + vm.pixel_clock = pck / 1000;
+ + vm.hsw = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
+ + vm.hbp = div64_u64((u64)t->hbp * pck, byteclk);
+ + vm.hfp = div64_u64((u64)t->hfp * pck, byteclk);
+ + vm.x_res = t->hact;
+ +
+ + print_dispc_vm(str, &vm);
+ +}
+ +#endif /* PRINT_VERBOSE_VM_TIMINGS */
+ +
+ +static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
+ + unsigned long pck, void *data)
{
- - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ + struct dsi_clk_calc_ctx *ctx = data;
+ + struct omap_video_timings *t = &ctx->dispc_vm;
- - mutex_lock(&dsi->lock);
+ + ctx->dispc_cinfo.lck_div = lckd;
+ + ctx->dispc_cinfo.pck_div = pckd;
+ + ctx->dispc_cinfo.lck = lck;
+ + ctx->dispc_cinfo.pck = pck;
- - dsi->timings = *timings;
+ + *t = *ctx->config->timings;
+ + t->pixel_clock = pck / 1000;
+ + t->x_res = ctx->config->timings->x_res;
+ + t->y_res = ctx->config->timings->y_res;
+ + t->hsw = t->hfp = t->hbp = t->vsw = 1;
+ + t->vfp = t->vbp = 0;
- - mutex_unlock(&dsi->lock);
+ + return true;
}
- -EXPORT_SYMBOL(omapdss_dsi_set_timings);
- -void omapdss_dsi_set_size(struct omap_dss_device *dssdev, u16 w, u16 h)
+ +static bool dsi_cm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
+ + void *data)
{
- - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ + struct dsi_clk_calc_ctx *ctx = data;
- - mutex_lock(&dsi->lock);
+ + ctx->dsi_cinfo.regm_dispc = regm_dispc;
+ + ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
- - dsi->timings.x_res = w;
- - dsi->timings.y_res = h;
+ + return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max,
+ + dsi_cm_calc_dispc_cb, ctx);
+ +}
- - mutex_unlock(&dsi->lock);
+ +static bool dsi_cm_calc_pll_cb(int regn, int regm, unsigned long fint,
+ + unsigned long pll, void *data)
+ +{
+ + struct dsi_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->req_pck_min,
+ + dsi_cm_calc_hsdiv_cb, ctx);
}
- -EXPORT_SYMBOL(omapdss_dsi_set_size);
- -void omapdss_dsi_set_pixel_format(struct omap_dss_device *dssdev,
- - enum omap_dss_dsi_pixel_format fmt)
+ +static bool dsi_cm_calc(struct dsi_data *dsi,
+ + const struct omap_dss_dsi_config *cfg,
+ + struct dsi_clk_calc_ctx *ctx)
{
- - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ + unsigned long clkin;
+ + int bitspp, ndl;
+ + unsigned long pll_min, pll_max;
+ + unsigned long pck, txbyteclk;
- - mutex_lock(&dsi->lock);
+ + clkin = clk_get_rate(dsi->sys_clk);
+ + bitspp = dsi_get_pixel_size(cfg->pixel_format);
+ + ndl = dsi->num_lanes_used - 1;
+
- dsi->pix_fmt = fmt;
+ + /*
+ + * Here we should calculate minimum txbyteclk to be able to send the
+ + * frame in time, and also to handle TE. That's not very simple, though,
+ + * especially as we go to LP between each pixel packet due to HW
+ + * "feature". So let's just estimate very roughly and multiply by 1.5.
+ + */
+ + pck = cfg->timings->pixel_clock * 1000;
+ + pck = pck * 3 / 2;
+ + txbyteclk = pck * bitspp / 8 / ndl;
- dsi->pix_fmt = fmt;
- mutex_unlock(&dsi->lock);
+ + memset(ctx, 0, sizeof(*ctx));
+ + ctx->dsidev = dsi->pdev;
+ + ctx->config = cfg;
+ + ctx->req_pck_min = pck;
+ + ctx->req_pck_nom = pck;
+ + ctx->req_pck_max = pck * 3 / 2;
+ + ctx->dsi_cinfo.clkin = clkin;
+
- mutex_unlock(&dsi->lock);
+ + pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
+ + pll_max = cfg->hs_clk_max * 4;
+ +
+ + return dsi_pll_calc(dsi->pdev, clkin,
+ + pll_min, pll_max,
+ + dsi_cm_calc_pll_cb, ctx);
}
- -EXPORT_SYMBOL(omapdss_dsi_set_pixel_format);
- -void omapdss_dsi_set_operation_mode(struct omap_dss_device *dssdev,
- - enum omap_dss_dsi_mode mode)
+ +static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
{
- - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ + struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev);
+ + const struct omap_dss_dsi_config *cfg = ctx->config;
+ + int bitspp = dsi_get_pixel_size(cfg->pixel_format);
+ + int ndl = dsi->num_lanes_used - 1;
+ + unsigned long hsclk = ctx->dsi_cinfo.clkin4ddr / 4;
+ + unsigned long byteclk = hsclk / 4;
- - mutex_lock(&dsi->lock);
+ + unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
+ + int xres;
+ + int panel_htot, panel_hbl; /* pixels */
+ + int dispc_htot, dispc_hbl; /* pixels */
+ + int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
+ + int hfp, hsa, hbp;
+ + const struct omap_video_timings *req_vm;
+ + struct omap_video_timings *dispc_vm;
+ + struct omap_dss_dsi_videomode_timings *dsi_vm;
+ + u64 dsi_tput, dispc_tput;
- - dsi->mode = mode;
+ + dsi_tput = (u64)byteclk * ndl * 8;
- - mutex_unlock(&dsi->lock);
+ + req_vm = cfg->timings;
+ + req_pck_min = ctx->req_pck_min;
+ + req_pck_max = ctx->req_pck_max;
+ + req_pck_nom = ctx->req_pck_nom;
+ +
+ + dispc_pck = ctx->dispc_cinfo.pck;
+ + dispc_tput = (u64)dispc_pck * bitspp;
+ +
+ + xres = req_vm->x_res;
+ +
+ + panel_hbl = req_vm->hfp + req_vm->hbp + req_vm->hsw;
+ + panel_htot = xres + panel_hbl;
+ +
+ + dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
+ +
+ + /*
+ + * When there are no line buffers, DISPC and DSI must have the
+ + * same tput. Otherwise DISPC tput needs to be higher than DSI's.
+ + */
+ + if (dsi->line_buffer_size < xres * bitspp / 8) {
+ + if (dispc_tput != dsi_tput)
+ + return false;
+ + } else {
+ + if (dispc_tput < dsi_tput)
+ + return false;
+ + }
+ +
+ + /* DSI tput must be over the min requirement */
+ + if (dsi_tput < (u64)bitspp * req_pck_min)
+ + return false;
+ +
+ + /* When non-burst mode, DSI tput must be below max requirement. */
+ + if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
+ + if (dsi_tput > (u64)bitspp * req_pck_max)
+ + return false;
+ + }
+ +
+ + hss = DIV_ROUND_UP(4, ndl);
+ +
+ + if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
+ + if (ndl == 3 && req_vm->hsw == 0)
+ + hse = 1;
+ + else
+ + hse = DIV_ROUND_UP(4, ndl);
+ + } else {
+ + hse = 0;
+ + }
+ +
+ + /* DSI htot to match the panel's nominal pck */
+ + dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
+ +
+ + /* fail if there would be no time for blanking */
+ + if (dsi_htot < hss + hse + dsi_hact)
+ + return false;
+ +
+ + /* total DSI blanking needed to achieve panel's TL */
+ + dsi_hbl = dsi_htot - dsi_hact;
+ +
+ + /* DISPC htot to match the DSI TL */
+ + dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
+ +
+ + /* verify that the DSI and DISPC TLs are the same */
+ + if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
+ + return false;
+ +
+ + dispc_hbl = dispc_htot - xres;
+ +
+ + /* setup DSI videomode */
+ +
+ + dsi_vm = &ctx->dsi_vm;
+ + memset(dsi_vm, 0, sizeof(*dsi_vm));
+ +
+ + dsi_vm->hsclk = hsclk;
+ +
+ + dsi_vm->ndl = ndl;
+ + dsi_vm->bitspp = bitspp;
+ +
+ + if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
+ + hsa = 0;
+ + } else if (ndl == 3 && req_vm->hsw == 0) {
+ + hsa = 0;
+ + } else {
+ + hsa = div64_u64((u64)req_vm->hsw * byteclk, req_pck_nom);
+ + hsa = max(hsa - hse, 1);
+ + }
+ +
+ + hbp = div64_u64((u64)req_vm->hbp * byteclk, req_pck_nom);
+ + hbp = max(hbp, 1);
+ +
+ + hfp = dsi_hbl - (hss + hsa + hse + hbp);
+ + if (hfp < 1) {
+ + int t;
+ + /* we need to take cycles from hbp */
+ +
+ + t = 1 - hfp;
+ + hbp = max(hbp - t, 1);
+ + hfp = dsi_hbl - (hss + hsa + hse + hbp);
+ +
+ + if (hfp < 1 && hsa > 0) {
+ + /* we need to take cycles from hsa */
+ + t = 1 - hfp;
+ + hsa = max(hsa - t, 1);
+ + hfp = dsi_hbl - (hss + hsa + hse + hbp);
+ + }
+ + }
+ +
+ + if (hfp < 1)
+ + return false;
+ +
+ + dsi_vm->hss = hss;
+ + dsi_vm->hsa = hsa;
+ + dsi_vm->hse = hse;
+ + dsi_vm->hbp = hbp;
+ + dsi_vm->hact = xres;
+ + dsi_vm->hfp = hfp;
+ +
+ + dsi_vm->vsa = req_vm->vsw;
+ + dsi_vm->vbp = req_vm->vbp;
+ + dsi_vm->vact = req_vm->y_res;
+ + dsi_vm->vfp = req_vm->vfp;
+ +
+ + dsi_vm->trans_mode = cfg->trans_mode;
+ +
+ + dsi_vm->blanking_mode = 0;
+ + dsi_vm->hsa_blanking_mode = 1;
+ + dsi_vm->hfp_blanking_mode = 1;
+ + dsi_vm->hbp_blanking_mode = 1;
+ +
+ + dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on;
+ + dsi_vm->window_sync = 4;
+ +
+ + /* setup DISPC videomode */
+ +
+ + dispc_vm = &ctx->dispc_vm;
+ + *dispc_vm = *req_vm;
+ + dispc_vm->pixel_clock = dispc_pck / 1000;
+ +
+ + if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
+ + hsa = div64_u64((u64)req_vm->hsw * dispc_pck,
+ + req_pck_nom);
+ + hsa = max(hsa, 1);
+ + } else {
+ + hsa = 1;
+ + }
+ +
+ + hbp = div64_u64((u64)req_vm->hbp * dispc_pck, req_pck_nom);
+ + hbp = max(hbp, 1);
+ +
+ + hfp = dispc_hbl - hsa - hbp;
+ + if (hfp < 1) {
+ + int t;
+ + /* we need to take cycles from hbp */
+ +
+ + t = 1 - hfp;
+ + hbp = max(hbp - t, 1);
+ + hfp = dispc_hbl - hsa - hbp;
+ +
+ + if (hfp < 1) {
+ + /* we need to take cycles from hsa */
+ + t = 1 - hfp;
+ + hsa = max(hsa - t, 1);
+ + hfp = dispc_hbl - hsa - hbp;
+ + }
+ + }
+ +
+ + if (hfp < 1)
+ + return false;
+ +
+ + dispc_vm->hfp = hfp;
+ + dispc_vm->hsw = hsa;
+ + dispc_vm->hbp = hbp;
+ +
+ + return true;
+ +}
+ +
+ +
+ +static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
+ + unsigned long pck, void *data)
+ +{
+ + struct dsi_clk_calc_ctx *ctx = data;
+ +
+ + ctx->dispc_cinfo.lck_div = lckd;
+ + ctx->dispc_cinfo.pck_div = pckd;
+ + ctx->dispc_cinfo.lck = lck;
+ + ctx->dispc_cinfo.pck = pck;
+ +
+ + if (dsi_vm_calc_blanking(ctx) == false)
+ + return false;
+ +
+ +#ifdef PRINT_VERBOSE_VM_TIMINGS
+ + print_dispc_vm("dispc", &ctx->dispc_vm);
+ + print_dsi_vm("dsi ", &ctx->dsi_vm);
+ + print_dispc_vm("req ", ctx->config->timings);
+ + print_dsi_dispc_vm("act ", &ctx->dsi_vm);
+ +#endif
+ +
+ + return true;
+ +}
+ +
+ +static bool dsi_vm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
+ + void *data)
+ +{
+ + struct dsi_clk_calc_ctx *ctx = data;
+ + unsigned long pck_max;
+ +
+ + ctx->dsi_cinfo.regm_dispc = regm_dispc;
+ + ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
+ +
+ + /*
+ + * In burst mode we can let the dispc pck be arbitrarily high, but it
+ + * limits our scaling abilities. So for now, don't aim too high.
+ + */
+ +
+ + if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
+ + pck_max = ctx->req_pck_max + 10000000;
+ + else
+ + pck_max = ctx->req_pck_max;
+ +
+ + return dispc_div_calc(dispc, ctx->req_pck_min, pck_max,
+ + dsi_vm_calc_dispc_cb, ctx);
+ +}
+ +
+ +static bool dsi_vm_calc_pll_cb(int regn, int regm, unsigned long fint,
+ + unsigned long pll, void *data)
+ +{
+ + struct dsi_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->req_pck_min,
+ + dsi_vm_calc_hsdiv_cb, ctx);
+ }
-EXPORT_SYMBOL(omapdss_dsi_set_operation_mode);
+
-void omapdss_dsi_set_videomode_timings(struct omap_dss_device *dssdev,
- struct omap_dss_dsi_videomode_timings *timings)
+ +static bool dsi_vm_calc(struct dsi_data *dsi,
+ + const struct omap_dss_dsi_config *cfg,
+ + struct dsi_clk_calc_ctx *ctx)
+ +{
+ + const struct omap_video_timings *t = cfg->timings;
+ + unsigned long clkin;
+ + unsigned long pll_min;
+ + unsigned long pll_max;
+ + int ndl = dsi->num_lanes_used - 1;
+ + int bitspp = dsi_get_pixel_size(cfg->pixel_format);
+ + unsigned long byteclk_min;
+ +
+ + clkin = clk_get_rate(dsi->sys_clk);
+ +
+ + memset(ctx, 0, sizeof(*ctx));
+ + ctx->dsidev = dsi->pdev;
+ + ctx->config = cfg;
+ +
+ + ctx->dsi_cinfo.clkin = clkin;
+ +
+ + /* these limits should come from the panel driver */
+ + ctx->req_pck_min = t->pixel_clock * 1000 - 1000;
+ + ctx->req_pck_nom = t->pixel_clock * 1000;
+ + ctx->req_pck_max = t->pixel_clock * 1000 + 1000;
+ +
+ + byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
+ + pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
+ +
+ + if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
+ + pll_max = cfg->hs_clk_max * 4;
+ + } else {
+ + unsigned long byteclk_max;
+ + byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
+ + ndl * 8);
+ +
+ + pll_max = byteclk_max * 4 * 4;
+ + }
+ +
+ + return dsi_pll_calc(dsi->pdev, clkin,
+ + pll_min, pll_max,
+ + dsi_vm_calc_pll_cb, ctx);
+}
- EXPORT_SYMBOL(omapdss_dsi_set_operation_mode);
+
- void omapdss_dsi_set_videomode_timings(struct omap_dss_device *dssdev,
- struct omap_dss_dsi_videomode_timings *timings)
+ +int omapdss_dsi_set_config(struct omap_dss_device *dssdev,
+ + const struct omap_dss_dsi_config *config)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);