Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / video / omap2 / dss / dpi.c
1 /*
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>
28 #include <linux/err.h>
29 #include <linux/errno.h>
30 #include <linux/platform_device.h>
31 #include <linux/regulator/consumer.h>
32
33 #include <plat/display.h>
34 #include <plat/cpu.h>
35
36 #include "dss.h"
37
38 static struct {
39         struct regulator *vdds_dsi_reg;
40 } dpi;
41
42 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
43 static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
44                 unsigned long *fck, int *lck_div, int *pck_div)
45 {
46         struct dsi_clock_info dsi_cinfo;
47         struct dispc_clock_info dispc_cinfo;
48         int r;
49
50         r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo,
51                         &dispc_cinfo);
52         if (r)
53                 return r;
54
55         r = dsi_pll_set_clock_div(&dsi_cinfo);
56         if (r)
57                 return r;
58
59         dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK);
60
61         r = dispc_set_clock_div(&dispc_cinfo);
62         if (r)
63                 return r;
64
65         *fck = dsi_cinfo.dsi1_pll_fclk;
66         *lck_div = dispc_cinfo.lck_div;
67         *pck_div = dispc_cinfo.pck_div;
68
69         return 0;
70 }
71 #else
72 static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req,
73                 unsigned long *fck, int *lck_div, int *pck_div)
74 {
75         struct dss_clock_info dss_cinfo;
76         struct dispc_clock_info dispc_cinfo;
77         int r;
78
79         r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
80         if (r)
81                 return r;
82
83         r = dss_set_clock_div(&dss_cinfo);
84         if (r)
85                 return r;
86
87         r = dispc_set_clock_div(&dispc_cinfo);
88         if (r)
89                 return r;
90
91         *fck = dss_cinfo.fck;
92         *lck_div = dispc_cinfo.lck_div;
93         *pck_div = dispc_cinfo.pck_div;
94
95         return 0;
96 }
97 #endif
98
99 static int dpi_set_mode(struct omap_dss_device *dssdev)
100 {
101         struct omap_video_timings *t = &dssdev->panel.timings;
102         int lck_div, pck_div;
103         unsigned long fck;
104         unsigned long pck;
105         bool is_tft;
106         int r = 0;
107
108         dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
109
110         dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
111                         dssdev->panel.acb);
112
113         is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
114
115 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
116         r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000,
117                         &fck, &lck_div, &pck_div);
118 #else
119         r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000,
120                         &fck, &lck_div, &pck_div);
121 #endif
122         if (r)
123                 goto err0;
124
125         pck = fck / lck_div / pck_div / 1000;
126
127         if (pck != t->pixel_clock) {
128                 DSSWARN("Could not find exact pixel clock. "
129                                 "Requested %d kHz, got %lu kHz\n",
130                                 t->pixel_clock, pck);
131
132                 t->pixel_clock = pck;
133         }
134
135         dispc_set_lcd_timings(t);
136
137 err0:
138         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
139         return r;
140 }
141
142 static int dpi_basic_init(struct omap_dss_device *dssdev)
143 {
144         bool is_tft;
145
146         is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
147
148         dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
149         dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT :
150                         OMAP_DSS_LCD_DISPLAY_STN);
151         dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines);
152
153         return 0;
154 }
155
156 int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
157 {
158         int r;
159
160         r = omap_dss_start_device(dssdev);
161         if (r) {
162                 DSSERR("failed to start device\n");
163                 goto err0;
164         }
165
166         if (cpu_is_omap34xx()) {
167                 r = regulator_enable(dpi.vdds_dsi_reg);
168                 if (r)
169                         goto err1;
170         }
171
172         dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
173
174         r = dpi_basic_init(dssdev);
175         if (r)
176                 goto err2;
177
178 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
179         dss_clk_enable(DSS_CLK_FCK2);
180         r = dsi_pll_init(dssdev, 0, 1);
181         if (r)
182                 goto err3;
183 #endif
184         r = dpi_set_mode(dssdev);
185         if (r)
186                 goto err4;
187
188         mdelay(2);
189
190         dssdev->manager->enable(dssdev->manager);
191
192         return 0;
193
194 err4:
195 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
196         dsi_pll_uninit();
197 err3:
198         dss_clk_disable(DSS_CLK_FCK2);
199 #endif
200 err2:
201         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
202         if (cpu_is_omap34xx())
203                 regulator_disable(dpi.vdds_dsi_reg);
204 err1:
205         omap_dss_stop_device(dssdev);
206 err0:
207         return r;
208 }
209 EXPORT_SYMBOL(omapdss_dpi_display_enable);
210
211 void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
212 {
213         dssdev->manager->disable(dssdev->manager);
214
215 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
216         dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
217         dsi_pll_uninit();
218         dss_clk_disable(DSS_CLK_FCK2);
219 #endif
220
221         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
222
223         if (cpu_is_omap34xx())
224                 regulator_disable(dpi.vdds_dsi_reg);
225
226         omap_dss_stop_device(dssdev);
227 }
228 EXPORT_SYMBOL(omapdss_dpi_display_disable);
229
230 void dpi_set_timings(struct omap_dss_device *dssdev,
231                         struct omap_video_timings *timings)
232 {
233         DSSDBG("dpi_set_timings\n");
234         dssdev->panel.timings = *timings;
235         if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
236                 dpi_set_mode(dssdev);
237                 dispc_go(OMAP_DSS_CHANNEL_LCD);
238         }
239 }
240 EXPORT_SYMBOL(dpi_set_timings);
241
242 int dpi_check_timings(struct omap_dss_device *dssdev,
243                         struct omap_video_timings *timings)
244 {
245         bool is_tft;
246         int r;
247         int lck_div, pck_div;
248         unsigned long fck;
249         unsigned long pck;
250
251         if (!dispc_lcd_timings_ok(timings))
252                 return -EINVAL;
253
254         if (timings->pixel_clock == 0)
255                 return -EINVAL;
256
257         is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
258
259 #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
260         {
261                 struct dsi_clock_info dsi_cinfo;
262                 struct dispc_clock_info dispc_cinfo;
263                 r = dsi_pll_calc_clock_div_pck(is_tft,
264                                 timings->pixel_clock * 1000,
265                                 &dsi_cinfo, &dispc_cinfo);
266
267                 if (r)
268                         return r;
269
270                 fck = dsi_cinfo.dsi1_pll_fclk;
271                 lck_div = dispc_cinfo.lck_div;
272                 pck_div = dispc_cinfo.pck_div;
273         }
274 #else
275         {
276                 struct dss_clock_info dss_cinfo;
277                 struct dispc_clock_info dispc_cinfo;
278                 r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
279                                 &dss_cinfo, &dispc_cinfo);
280
281                 if (r)
282                         return r;
283
284                 fck = dss_cinfo.fck;
285                 lck_div = dispc_cinfo.lck_div;
286                 pck_div = dispc_cinfo.pck_div;
287         }
288 #endif
289
290         pck = fck / lck_div / pck_div / 1000;
291
292         timings->pixel_clock = pck;
293
294         return 0;
295 }
296 EXPORT_SYMBOL(dpi_check_timings);
297
298 int dpi_init_display(struct omap_dss_device *dssdev)
299 {
300         DSSDBG("init_display\n");
301
302         return 0;
303 }
304
305 int dpi_init(struct platform_device *pdev)
306 {
307         if (cpu_is_omap34xx()) {
308                 dpi.vdds_dsi_reg = dss_get_vdds_dsi();
309                 if (IS_ERR(dpi.vdds_dsi_reg)) {
310                         DSSERR("can't get VDDS_DSI regulator\n");
311                         return PTR_ERR(dpi.vdds_dsi_reg);
312                 }
313         }
314
315         return 0;
316 }
317
318 void dpi_exit(void)
319 {
320 }
321