blob: 69ce31ae2a2fe4d8037db48667eaf187f8ecd6ea [file] [log] [blame]
Tomi Valkeinen553c48c2009-08-07 13:15:50 +03001/*
2 * linux/drivers/video/omap2/dss/dpi.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "DPI"
24
25#include <linux/kernel.h>
26#include <linux/clk.h>
27#include <linux/delay.h>
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020028#include <linux/err.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030029#include <linux/errno.h>
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020030#include <linux/platform_device.h>
31#include <linux/regulator/consumer.h>
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030032
33#include <plat/display.h>
34#include <plat/cpu.h>
35
36#include "dss.h"
37
38static struct {
39 int update_enabled;
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020040 struct regulator *vdds_dsi_reg;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +030041} dpi;
42
43#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
44static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
45 unsigned long *fck, int *lck_div, int *pck_div)
46{
47 struct dsi_clock_info dsi_cinfo;
48 struct dispc_clock_info dispc_cinfo;
49 int r;
50
51 r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo,
52 &dispc_cinfo);
53 if (r)
54 return r;
55
56 r = dsi_pll_set_clock_div(&dsi_cinfo);
57 if (r)
58 return r;
59
60 dss_select_clk_source(0, 1);
61
62 r = dispc_set_clock_div(&dispc_cinfo);
63 if (r)
64 return r;
65
66 *fck = dsi_cinfo.dsi1_pll_fclk;
67 *lck_div = dispc_cinfo.lck_div;
68 *pck_div = dispc_cinfo.pck_div;
69
70 return 0;
71}
72#else
73static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req,
74 unsigned long *fck, int *lck_div, int *pck_div)
75{
76 struct dss_clock_info dss_cinfo;
77 struct dispc_clock_info dispc_cinfo;
78 int r;
79
80 r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
81 if (r)
82 return r;
83
84 r = dss_set_clock_div(&dss_cinfo);
85 if (r)
86 return r;
87
88 r = dispc_set_clock_div(&dispc_cinfo);
89 if (r)
90 return r;
91
92 *fck = dss_cinfo.fck;
93 *lck_div = dispc_cinfo.lck_div;
94 *pck_div = dispc_cinfo.pck_div;
95
96 return 0;
97}
98#endif
99
100static int dpi_set_mode(struct omap_dss_device *dssdev)
101{
102 struct omap_video_timings *t = &dssdev->panel.timings;
103 int lck_div, pck_div;
104 unsigned long fck;
105 unsigned long pck;
106 bool is_tft;
107 int r = 0;
108
109 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
110
111 dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
112 dssdev->panel.acb);
113
114 is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
115
116#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
117 r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000,
118 &fck, &lck_div, &pck_div);
119#else
120 r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000,
121 &fck, &lck_div, &pck_div);
122#endif
123 if (r)
124 goto err0;
125
126 pck = fck / lck_div / pck_div / 1000;
127
128 if (pck != t->pixel_clock) {
129 DSSWARN("Could not find exact pixel clock. "
130 "Requested %d kHz, got %lu kHz\n",
131 t->pixel_clock, pck);
132
133 t->pixel_clock = pck;
134 }
135
136 dispc_set_lcd_timings(t);
137
138err0:
139 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
140 return r;
141}
142
143static int dpi_basic_init(struct omap_dss_device *dssdev)
144{
145 bool is_tft;
146
147 is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
148
149 dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
150 dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT :
151 OMAP_DSS_LCD_DISPLAY_STN);
152 dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines);
153
154 return 0;
155}
156
157static int dpi_display_enable(struct omap_dss_device *dssdev)
158{
159 int r;
160
161 r = omap_dss_start_device(dssdev);
162 if (r) {
163 DSSERR("failed to start device\n");
164 goto err0;
165 }
166
167 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
168 DSSERR("display already enabled\n");
169 r = -EINVAL;
170 goto err1;
171 }
172
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200173 if (cpu_is_omap34xx()) {
174 r = regulator_enable(dpi.vdds_dsi_reg);
175 if (r)
176 goto err2;
177 }
178
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300179 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
180
181 r = dpi_basic_init(dssdev);
182 if (r)
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200183 goto err3;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300184
185#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
186 dss_clk_enable(DSS_CLK_FCK2);
187 r = dsi_pll_init(dssdev, 0, 1);
188 if (r)
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200189 goto err4;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300190#endif
191 r = dpi_set_mode(dssdev);
192 if (r)
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200193 goto err5;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300194
195 mdelay(2);
196
197 dispc_enable_lcd_out(1);
198
199 r = dssdev->driver->enable(dssdev);
200 if (r)
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200201 goto err6;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300202
203 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
204
205 return 0;
206
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200207err6:
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300208 dispc_enable_lcd_out(0);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200209err5:
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300210#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
211 dsi_pll_uninit();
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200212err4:
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300213 dss_clk_disable(DSS_CLK_FCK2);
214#endif
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200215err3:
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300216 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200217err2:
218 if (cpu_is_omap34xx())
219 regulator_disable(dpi.vdds_dsi_reg);
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300220err1:
221 omap_dss_stop_device(dssdev);
222err0:
223 return r;
224}
225
226static int dpi_display_resume(struct omap_dss_device *dssdev);
227
228static void dpi_display_disable(struct omap_dss_device *dssdev)
229{
230 if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
231 return;
232
233 if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
234 dpi_display_resume(dssdev);
235
236 dssdev->driver->disable(dssdev);
237
238 dispc_enable_lcd_out(0);
239
240#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
241 dss_select_clk_source(0, 0);
242 dsi_pll_uninit();
243 dss_clk_disable(DSS_CLK_FCK2);
244#endif
245
246 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
247
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200248 if (cpu_is_omap34xx())
249 regulator_disable(dpi.vdds_dsi_reg);
250
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300251 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
252
253 omap_dss_stop_device(dssdev);
254}
255
256static int dpi_display_suspend(struct omap_dss_device *dssdev)
257{
258 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
259 return -EINVAL;
260
261 DSSDBG("dpi_display_suspend\n");
262
263 if (dssdev->driver->suspend)
264 dssdev->driver->suspend(dssdev);
265
266 dispc_enable_lcd_out(0);
267
268 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
269
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200270 if (cpu_is_omap34xx())
271 regulator_disable(dpi.vdds_dsi_reg);
272
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300273 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
274
275 return 0;
276}
277
278static int dpi_display_resume(struct omap_dss_device *dssdev)
279{
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200280 int r;
281
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300282 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
283 return -EINVAL;
284
285 DSSDBG("dpi_display_resume\n");
286
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200287 if (cpu_is_omap34xx()) {
288 r = regulator_enable(dpi.vdds_dsi_reg);
289 if (r)
290 goto err0;
291 }
292
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300293 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
294
295 dispc_enable_lcd_out(1);
296
297 if (dssdev->driver->resume)
298 dssdev->driver->resume(dssdev);
299
300 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
301
302 return 0;
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200303err0:
304 return r;
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300305}
306
307static void dpi_set_timings(struct omap_dss_device *dssdev,
308 struct omap_video_timings *timings)
309{
310 DSSDBG("dpi_set_timings\n");
311 dssdev->panel.timings = *timings;
312 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
313 dpi_set_mode(dssdev);
314 dispc_go(OMAP_DSS_CHANNEL_LCD);
315 }
316}
317
318static int dpi_check_timings(struct omap_dss_device *dssdev,
319 struct omap_video_timings *timings)
320{
321 bool is_tft;
322 int r;
323 int lck_div, pck_div;
324 unsigned long fck;
325 unsigned long pck;
326
327 if (!dispc_lcd_timings_ok(timings))
328 return -EINVAL;
329
330 if (timings->pixel_clock == 0)
331 return -EINVAL;
332
333 is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
334
335#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
336 {
337 struct dsi_clock_info dsi_cinfo;
338 struct dispc_clock_info dispc_cinfo;
339 r = dsi_pll_calc_clock_div_pck(is_tft,
340 timings->pixel_clock * 1000,
341 &dsi_cinfo, &dispc_cinfo);
342
343 if (r)
344 return r;
345
346 fck = dsi_cinfo.dsi1_pll_fclk;
347 lck_div = dispc_cinfo.lck_div;
348 pck_div = dispc_cinfo.pck_div;
349 }
350#else
351 {
352 struct dss_clock_info dss_cinfo;
353 struct dispc_clock_info dispc_cinfo;
354 r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
355 &dss_cinfo, &dispc_cinfo);
356
357 if (r)
358 return r;
359
360 fck = dss_cinfo.fck;
361 lck_div = dispc_cinfo.lck_div;
362 pck_div = dispc_cinfo.pck_div;
363 }
364#endif
365
366 pck = fck / lck_div / pck_div / 1000;
367
368 timings->pixel_clock = pck;
369
370 return 0;
371}
372
373static void dpi_get_timings(struct omap_dss_device *dssdev,
374 struct omap_video_timings *timings)
375{
376 *timings = dssdev->panel.timings;
377}
378
379static int dpi_display_set_update_mode(struct omap_dss_device *dssdev,
380 enum omap_dss_update_mode mode)
381{
382 if (mode == OMAP_DSS_UPDATE_MANUAL)
383 return -EINVAL;
384
385 if (mode == OMAP_DSS_UPDATE_DISABLED) {
386 dispc_enable_lcd_out(0);
387 dpi.update_enabled = 0;
388 } else {
389 dispc_enable_lcd_out(1);
390 dpi.update_enabled = 1;
391 }
392
393 return 0;
394}
395
396static enum omap_dss_update_mode dpi_display_get_update_mode(
397 struct omap_dss_device *dssdev)
398{
399 return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
400 OMAP_DSS_UPDATE_DISABLED;
401}
402
403int dpi_init_display(struct omap_dss_device *dssdev)
404{
405 DSSDBG("init_display\n");
406
407 dssdev->enable = dpi_display_enable;
408 dssdev->disable = dpi_display_disable;
409 dssdev->suspend = dpi_display_suspend;
410 dssdev->resume = dpi_display_resume;
411 dssdev->set_timings = dpi_set_timings;
412 dssdev->check_timings = dpi_check_timings;
413 dssdev->get_timings = dpi_get_timings;
414 dssdev->set_update_mode = dpi_display_set_update_mode;
415 dssdev->get_update_mode = dpi_display_get_update_mode;
416
417 return 0;
418}
419
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200420int dpi_init(struct platform_device *pdev)
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300421{
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200422 if (cpu_is_omap34xx()) {
423 dpi.vdds_dsi_reg = dss_get_vdds_dsi();
424 if (IS_ERR(dpi.vdds_dsi_reg)) {
425 DSSERR("can't get VDDS_DSI regulator\n");
426 return PTR_ERR(dpi.vdds_dsi_reg);
427 }
428 }
429
Tomi Valkeinen553c48c2009-08-07 13:15:50 +0300430 return 0;
431}
432
433void dpi_exit(void)
434{
435}
436