443 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/video/omap2/dss/sdi.c4 *5 * Copyright (C) 2009 Nokia Corporation6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>7 */8 9#define DSS_SUBSYS_NAME "SDI"10 11#include <linux/kernel.h>12#include <linux/delay.h>13#include <linux/err.h>14#include <linux/regulator/consumer.h>15#include <linux/export.h>16#include <linux/platform_device.h>17#include <linux/string.h>18#include <linux/of.h>19#include <linux/component.h>20 21#include <video/omapfb_dss.h>22#include "dss.h"23 24static struct {25 struct platform_device *pdev;26 27 bool update_enabled;28 struct regulator *vdds_sdi_reg;29 30 struct dss_lcd_mgr_config mgr_config;31 struct omap_video_timings timings;32 int datapairs;33 34 struct omap_dss_device output;35 36 bool port_initialized;37} sdi;38 39struct sdi_clk_calc_ctx {40 unsigned long pck_min, pck_max;41 42 unsigned long fck;43 struct dispc_clock_info dispc_cinfo;44};45 46static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,47 unsigned long pck, void *data)48{49 struct sdi_clk_calc_ctx *ctx = data;50 51 ctx->dispc_cinfo.lck_div = lckd;52 ctx->dispc_cinfo.pck_div = pckd;53 ctx->dispc_cinfo.lck = lck;54 ctx->dispc_cinfo.pck = pck;55 56 return true;57}58 59static bool dpi_calc_dss_cb(unsigned long fck, void *data)60{61 struct sdi_clk_calc_ctx *ctx = data;62 63 ctx->fck = fck;64 65 return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,66 dpi_calc_dispc_cb, ctx);67}68 69static int sdi_calc_clock_div(unsigned long pclk,70 unsigned long *fck,71 struct dispc_clock_info *dispc_cinfo)72{73 int i;74 struct sdi_clk_calc_ctx ctx;75 76 /*77 * DSS fclk gives us very few possibilities, so finding a good pixel78 * clock may not be possible. We try multiple times to find the clock,79 * each time widening the pixel clock range we look for, up to80 * +/- 1MHz.81 */82 83 for (i = 0; i < 10; ++i) {84 bool ok;85 86 memset(&ctx, 0, sizeof(ctx));87 if (pclk > 1000 * i * i * i)88 ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu);89 else90 ctx.pck_min = 0;91 ctx.pck_max = pclk + 1000 * i * i * i;92 93 ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx);94 if (ok) {95 *fck = ctx.fck;96 *dispc_cinfo = ctx.dispc_cinfo;97 return 0;98 }99 }100 101 return -EINVAL;102}103 104static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)105{106 struct omap_overlay_manager *mgr = sdi.output.manager;107 108 sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;109 110 sdi.mgr_config.stallmode = false;111 sdi.mgr_config.fifohandcheck = false;112 113 sdi.mgr_config.video_port_width = 24;114 sdi.mgr_config.lcden_sig_polarity = 1;115 116 dss_mgr_set_lcd_config(mgr, &sdi.mgr_config);117}118 119static int sdi_display_enable(struct omap_dss_device *dssdev)120{121 struct omap_dss_device *out = &sdi.output;122 struct omap_video_timings *t = &sdi.timings;123 unsigned long fck;124 struct dispc_clock_info dispc_cinfo;125 unsigned long pck;126 int r;127 128 if (out->manager == NULL) {129 DSSERR("failed to enable display: no output/manager\n");130 return -ENODEV;131 }132 133 r = regulator_enable(sdi.vdds_sdi_reg);134 if (r)135 goto err_reg_enable;136 137 r = dispc_runtime_get();138 if (r)139 goto err_get_dispc;140 141 /* 15.5.9.1.2 */142 t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;143 t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;144 145 r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo);146 if (r)147 goto err_calc_clock_div;148 149 sdi.mgr_config.clock_info = dispc_cinfo;150 151 pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;152 153 if (pck != t->pixelclock) {154 DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",155 t->pixelclock, pck);156 157 t->pixelclock = pck;158 }159 160 161 dss_mgr_set_timings(out->manager, t);162 163 r = dss_set_fck_rate(fck);164 if (r)165 goto err_set_dss_clock_div;166 167 sdi_config_lcd_manager(dssdev);168 169 /*170 * LCLK and PCLK divisors are located in shadow registers, and we171 * normally write them to DISPC registers when enabling the output.172 * However, SDI uses pck-free as source clock for its PLL, and pck-free173 * is affected by the divisors. And as we need the PLL before enabling174 * the output, we need to write the divisors early.175 *176 * It seems just writing to the DISPC register is enough, and we don't177 * need to care about the shadow register mechanism for pck-free. The178 * exact reason for this is unknown.179 */180 dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info);181 182 dss_sdi_init(sdi.datapairs);183 r = dss_sdi_enable();184 if (r)185 goto err_sdi_enable;186 mdelay(2);187 188 r = dss_mgr_enable(out->manager);189 if (r)190 goto err_mgr_enable;191 192 return 0;193 194err_mgr_enable:195 dss_sdi_disable();196err_sdi_enable:197err_set_dss_clock_div:198err_calc_clock_div:199 dispc_runtime_put();200err_get_dispc:201 regulator_disable(sdi.vdds_sdi_reg);202err_reg_enable:203 return r;204}205 206static void sdi_display_disable(struct omap_dss_device *dssdev)207{208 struct omap_overlay_manager *mgr = sdi.output.manager;209 210 dss_mgr_disable(mgr);211 212 dss_sdi_disable();213 214 dispc_runtime_put();215 216 regulator_disable(sdi.vdds_sdi_reg);217}218 219static void sdi_set_timings(struct omap_dss_device *dssdev,220 struct omap_video_timings *timings)221{222 sdi.timings = *timings;223}224 225static void sdi_get_timings(struct omap_dss_device *dssdev,226 struct omap_video_timings *timings)227{228 *timings = sdi.timings;229}230 231static int sdi_check_timings(struct omap_dss_device *dssdev,232 struct omap_video_timings *timings)233{234 struct omap_overlay_manager *mgr = sdi.output.manager;235 236 if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))237 return -EINVAL;238 239 if (timings->pixelclock == 0)240 return -EINVAL;241 242 return 0;243}244 245static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)246{247 sdi.datapairs = datapairs;248}249 250static int sdi_init_regulator(void)251{252 struct regulator *vdds_sdi;253 254 if (sdi.vdds_sdi_reg)255 return 0;256 257 vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");258 if (IS_ERR(vdds_sdi)) {259 if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER)260 DSSERR("can't get VDDS_SDI regulator\n");261 return PTR_ERR(vdds_sdi);262 }263 264 sdi.vdds_sdi_reg = vdds_sdi;265 266 return 0;267}268 269static int sdi_connect(struct omap_dss_device *dssdev,270 struct omap_dss_device *dst)271{272 struct omap_overlay_manager *mgr;273 int r;274 275 r = sdi_init_regulator();276 if (r)277 return r;278 279 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);280 if (!mgr)281 return -ENODEV;282 283 r = dss_mgr_connect(mgr, dssdev);284 if (r)285 return r;286 287 r = omapdss_output_set_device(dssdev, dst);288 if (r) {289 DSSERR("failed to connect output to new device: %s\n",290 dst->name);291 dss_mgr_disconnect(mgr, dssdev);292 return r;293 }294 295 return 0;296}297 298static void sdi_disconnect(struct omap_dss_device *dssdev,299 struct omap_dss_device *dst)300{301 WARN_ON(dst != dssdev->dst);302 303 if (dst != dssdev->dst)304 return;305 306 omapdss_output_unset_device(dssdev);307 308 if (dssdev->manager)309 dss_mgr_disconnect(dssdev->manager, dssdev);310}311 312static const struct omapdss_sdi_ops sdi_ops = {313 .connect = sdi_connect,314 .disconnect = sdi_disconnect,315 316 .enable = sdi_display_enable,317 .disable = sdi_display_disable,318 319 .check_timings = sdi_check_timings,320 .set_timings = sdi_set_timings,321 .get_timings = sdi_get_timings,322 323 .set_datapairs = sdi_set_datapairs,324};325 326static void sdi_init_output(struct platform_device *pdev)327{328 struct omap_dss_device *out = &sdi.output;329 330 out->dev = &pdev->dev;331 out->id = OMAP_DSS_OUTPUT_SDI;332 out->output_type = OMAP_DISPLAY_TYPE_SDI;333 out->name = "sdi.0";334 out->dispc_channel = OMAP_DSS_CHANNEL_LCD;335 /* We have SDI only on OMAP3, where it's on port 1 */336 out->port_num = 1;337 out->ops.sdi = &sdi_ops;338 out->owner = THIS_MODULE;339 340 omapdss_register_output(out);341}342 343static void sdi_uninit_output(struct platform_device *pdev)344{345 struct omap_dss_device *out = &sdi.output;346 347 omapdss_unregister_output(out);348}349 350static int sdi_bind(struct device *dev, struct device *master, void *data)351{352 struct platform_device *pdev = to_platform_device(dev);353 354 sdi.pdev = pdev;355 356 sdi_init_output(pdev);357 358 return 0;359}360 361static void sdi_unbind(struct device *dev, struct device *master, void *data)362{363 struct platform_device *pdev = to_platform_device(dev);364 365 sdi_uninit_output(pdev);366}367 368static const struct component_ops sdi_component_ops = {369 .bind = sdi_bind,370 .unbind = sdi_unbind,371};372 373static int sdi_probe(struct platform_device *pdev)374{375 return component_add(&pdev->dev, &sdi_component_ops);376}377 378static void sdi_remove(struct platform_device *pdev)379{380 component_del(&pdev->dev, &sdi_component_ops);381}382 383static struct platform_driver omap_sdi_driver = {384 .probe = sdi_probe,385 .remove = sdi_remove,386 .driver = {387 .name = "omapdss_sdi",388 .suppress_bind_attrs = true,389 },390};391 392int __init sdi_init_platform_driver(void)393{394 return platform_driver_register(&omap_sdi_driver);395}396 397void sdi_uninit_platform_driver(void)398{399 platform_driver_unregister(&omap_sdi_driver);400}401 402int sdi_init_port(struct platform_device *pdev, struct device_node *port)403{404 struct device_node *ep;405 u32 datapairs;406 int r;407 408 ep = omapdss_of_get_next_endpoint(port, NULL);409 if (!ep)410 return 0;411 412 r = of_property_read_u32(ep, "datapairs", &datapairs);413 if (r) {414 DSSERR("failed to parse datapairs\n");415 goto err_datapairs;416 }417 418 sdi.datapairs = datapairs;419 420 of_node_put(ep);421 422 sdi.pdev = pdev;423 424 sdi_init_output(pdev);425 426 sdi.port_initialized = true;427 428 return 0;429 430err_datapairs:431 of_node_put(ep);432 433 return r;434}435 436void sdi_uninit_port(struct device_node *port)437{438 if (!sdi.port_initialized)439 return;440 441 sdi_uninit_output(sdi.pdev);442}443