2 * linux/drivers/video/omap2/dss/sdi.c
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
20 #define DSS_SUBSYS_NAME "SDI"
22 #include <linux/kernel.h>
23 #include <linux/delay.h>
24 #include <linux/err.h>
25 #include <linux/regulator/consumer.h>
26 #include <linux/export.h>
27 #include <linux/platform_device.h>
28 #include <linux/string.h>
30 #include <video/omapdss.h>
34 struct platform_device
*pdev
;
37 struct regulator
*vdds_sdi_reg
;
39 struct dss_lcd_mgr_config mgr_config
;
40 struct omap_video_timings timings
;
43 struct omap_dss_device output
;
46 struct sdi_clk_calc_ctx
{
47 unsigned long pck_min
, pck_max
;
49 struct dss_clock_info dss_cinfo
;
50 struct dispc_clock_info dispc_cinfo
;
53 static bool dpi_calc_dispc_cb(int lckd
, int pckd
, unsigned long lck
,
54 unsigned long pck
, void *data
)
56 struct sdi_clk_calc_ctx
*ctx
= data
;
58 ctx
->dispc_cinfo
.lck_div
= lckd
;
59 ctx
->dispc_cinfo
.pck_div
= pckd
;
60 ctx
->dispc_cinfo
.lck
= lck
;
61 ctx
->dispc_cinfo
.pck
= pck
;
66 static bool dpi_calc_dss_cb(int fckd
, unsigned long fck
, void *data
)
68 struct sdi_clk_calc_ctx
*ctx
= data
;
70 ctx
->dss_cinfo
.fck
= fck
;
71 ctx
->dss_cinfo
.fck_div
= fckd
;
73 return dispc_div_calc(fck
, ctx
->pck_min
, ctx
->pck_max
,
74 dpi_calc_dispc_cb
, ctx
);
77 static int sdi_calc_clock_div(unsigned long pclk
,
78 struct dss_clock_info
*dss_cinfo
,
79 struct dispc_clock_info
*dispc_cinfo
)
82 struct sdi_clk_calc_ctx ctx
;
85 * DSS fclk gives us very few possibilities, so finding a good pixel
86 * clock may not be possible. We try multiple times to find the clock,
87 * each time widening the pixel clock range we look for, up to
91 for (i
= 0; i
< 10; ++i
) {
94 memset(&ctx
, 0, sizeof(ctx
));
95 if (pclk
> 1000 * i
* i
* i
)
96 ctx
.pck_min
= max(pclk
- 1000 * i
* i
* i
, 0lu);
99 ctx
.pck_max
= pclk
+ 1000 * i
* i
* i
;
101 ok
= dss_div_calc(ctx
.pck_min
, dpi_calc_dss_cb
, &ctx
);
103 *dss_cinfo
= ctx
.dss_cinfo
;
104 *dispc_cinfo
= ctx
.dispc_cinfo
;
112 static void sdi_config_lcd_manager(struct omap_dss_device
*dssdev
)
114 struct omap_overlay_manager
*mgr
= sdi
.output
.manager
;
116 sdi
.mgr_config
.io_pad_mode
= DSS_IO_PAD_MODE_BYPASS
;
118 sdi
.mgr_config
.stallmode
= false;
119 sdi
.mgr_config
.fifohandcheck
= false;
121 sdi
.mgr_config
.video_port_width
= 24;
122 sdi
.mgr_config
.lcden_sig_polarity
= 1;
124 dss_mgr_set_lcd_config(mgr
, &sdi
.mgr_config
);
127 static int sdi_display_enable(struct omap_dss_device
*dssdev
)
129 struct omap_dss_device
*out
= &sdi
.output
;
130 struct omap_video_timings
*t
= &sdi
.timings
;
131 struct dss_clock_info dss_cinfo
;
132 struct dispc_clock_info dispc_cinfo
;
136 if (out
== NULL
|| out
->manager
== NULL
) {
137 DSSERR("failed to enable display: no output/manager\n");
141 r
= regulator_enable(sdi
.vdds_sdi_reg
);
145 r
= dispc_runtime_get();
150 t
->data_pclk_edge
= OMAPDSS_DRIVE_SIG_RISING_EDGE
;
151 t
->sync_pclk_edge
= OMAPDSS_DRIVE_SIG_RISING_EDGE
;
153 r
= sdi_calc_clock_div(t
->pixel_clock
* 1000, &dss_cinfo
, &dispc_cinfo
);
155 goto err_calc_clock_div
;
157 sdi
.mgr_config
.clock_info
= dispc_cinfo
;
159 pck
= dss_cinfo
.fck
/ dispc_cinfo
.lck_div
/ dispc_cinfo
.pck_div
/ 1000;
161 if (pck
!= t
->pixel_clock
) {
162 DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
164 t
->pixel_clock
, pck
);
166 t
->pixel_clock
= pck
;
170 dss_mgr_set_timings(out
->manager
, t
);
172 r
= dss_set_clock_div(&dss_cinfo
);
174 goto err_set_dss_clock_div
;
176 sdi_config_lcd_manager(dssdev
);
179 * LCLK and PCLK divisors are located in shadow registers, and we
180 * normally write them to DISPC registers when enabling the output.
181 * However, SDI uses pck-free as source clock for its PLL, and pck-free
182 * is affected by the divisors. And as we need the PLL before enabling
183 * the output, we need to write the divisors early.
185 * It seems just writing to the DISPC register is enough, and we don't
186 * need to care about the shadow register mechanism for pck-free. The
187 * exact reason for this is unknown.
189 dispc_mgr_set_clock_div(out
->manager
->id
, &sdi
.mgr_config
.clock_info
);
191 dss_sdi_init(sdi
.datapairs
);
192 r
= dss_sdi_enable();
197 r
= dss_mgr_enable(out
->manager
);
206 err_set_dss_clock_div
:
210 regulator_disable(sdi
.vdds_sdi_reg
);
215 static void sdi_display_disable(struct omap_dss_device
*dssdev
)
217 struct omap_overlay_manager
*mgr
= sdi
.output
.manager
;
219 dss_mgr_disable(mgr
);
225 regulator_disable(sdi
.vdds_sdi_reg
);
228 static void sdi_set_timings(struct omap_dss_device
*dssdev
,
229 struct omap_video_timings
*timings
)
231 sdi
.timings
= *timings
;
234 static void sdi_get_timings(struct omap_dss_device
*dssdev
,
235 struct omap_video_timings
*timings
)
237 *timings
= sdi
.timings
;
240 static int sdi_check_timings(struct omap_dss_device
*dssdev
,
241 struct omap_video_timings
*timings
)
243 struct omap_overlay_manager
*mgr
= sdi
.output
.manager
;
245 if (mgr
&& !dispc_mgr_timings_ok(mgr
->id
, timings
))
248 if (timings
->pixel_clock
== 0)
254 static void sdi_set_datapairs(struct omap_dss_device
*dssdev
, int datapairs
)
256 sdi
.datapairs
= datapairs
;
259 static int sdi_init_regulator(void)
261 struct regulator
*vdds_sdi
;
263 if (sdi
.vdds_sdi_reg
)
266 vdds_sdi
= devm_regulator_get(&sdi
.pdev
->dev
, "vdds_sdi");
267 if (IS_ERR(vdds_sdi
)) {
268 DSSERR("can't get VDDS_SDI regulator\n");
269 return PTR_ERR(vdds_sdi
);
272 sdi
.vdds_sdi_reg
= vdds_sdi
;
277 static int sdi_connect(struct omap_dss_device
*dssdev
,
278 struct omap_dss_device
*dst
)
280 struct omap_overlay_manager
*mgr
;
283 r
= sdi_init_regulator();
287 mgr
= omap_dss_get_overlay_manager(dssdev
->dispc_channel
);
291 r
= dss_mgr_connect(mgr
, dssdev
);
295 r
= omapdss_output_set_device(dssdev
, dst
);
297 DSSERR("failed to connect output to new device: %s\n",
299 dss_mgr_disconnect(mgr
, dssdev
);
306 static void sdi_disconnect(struct omap_dss_device
*dssdev
,
307 struct omap_dss_device
*dst
)
309 WARN_ON(dst
!= dssdev
->dst
);
311 if (dst
!= dssdev
->dst
)
314 omapdss_output_unset_device(dssdev
);
317 dss_mgr_disconnect(dssdev
->manager
, dssdev
);
320 static const struct omapdss_sdi_ops sdi_ops
= {
321 .connect
= sdi_connect
,
322 .disconnect
= sdi_disconnect
,
324 .enable
= sdi_display_enable
,
325 .disable
= sdi_display_disable
,
327 .check_timings
= sdi_check_timings
,
328 .set_timings
= sdi_set_timings
,
329 .get_timings
= sdi_get_timings
,
331 .set_datapairs
= sdi_set_datapairs
,
334 static void sdi_init_output(struct platform_device
*pdev
)
336 struct omap_dss_device
*out
= &sdi
.output
;
338 out
->dev
= &pdev
->dev
;
339 out
->id
= OMAP_DSS_OUTPUT_SDI
;
340 out
->output_type
= OMAP_DISPLAY_TYPE_SDI
;
342 out
->dispc_channel
= OMAP_DSS_CHANNEL_LCD
;
343 out
->ops
.sdi
= &sdi_ops
;
344 out
->owner
= THIS_MODULE
;
346 omapdss_register_output(out
);
349 static void __exit
sdi_uninit_output(struct platform_device
*pdev
)
351 struct omap_dss_device
*out
= &sdi
.output
;
353 omapdss_unregister_output(out
);
356 static int omap_sdi_probe(struct platform_device
*pdev
)
360 sdi_init_output(pdev
);
365 static int __exit
omap_sdi_remove(struct platform_device
*pdev
)
367 sdi_uninit_output(pdev
);
372 static struct platform_driver omap_sdi_driver
= {
373 .probe
= omap_sdi_probe
,
374 .remove
= __exit_p(omap_sdi_remove
),
376 .name
= "omapdss_sdi",
377 .owner
= THIS_MODULE
,
381 int __init
sdi_init_platform_driver(void)
383 return platform_driver_register(&omap_sdi_driver
);
386 void __exit
sdi_uninit_platform_driver(void)
388 platform_driver_unregister(&omap_sdi_driver
);