DSS2: OMAP2/3 Display Subsystem driver
[pandora-kernel.git] / drivers / video / omap2 / dss / sdi.c
1 /*
2  * linux/drivers/video/omap2/dss/sdi.c
3  *
4  * Copyright (C) 2009 Nokia Corporation
5  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6  *
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.
10  *
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
14  * more details.
15  *
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/>.
18  */
19
20 #define DSS_SUBSYS_NAME "SDI"
21
22 #include <linux/kernel.h>
23 #include <linux/clk.h>
24 #include <linux/delay.h>
25 #include <linux/err.h>
26
27 #include <mach/board.h>
28 #include <mach/display.h>
29 #include "dss.h"
30
31
32 static struct {
33         bool skip_init;
34         bool update_enabled;
35 } sdi;
36
37 static void sdi_basic_init(void)
38 {
39         dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
40
41         dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
42         dispc_set_tft_data_lines(24);
43         dispc_lcd_enable_signal_polarity(1);
44 }
45
46 static int sdi_display_enable(struct omap_display *display)
47 {
48         struct dispc_clock_info cinfo;
49         u16 lck_div, pck_div;
50         unsigned long fck;
51         struct omap_panel *panel = display->panel;
52         unsigned long pck;
53         int r;
54
55         if (display->state != OMAP_DSS_DISPLAY_DISABLED) {
56                 DSSERR("display already enabled\n");
57                 return -EINVAL;
58         }
59
60         /* In case of skip_init sdi_init has already enabled the clocks */
61         if (!sdi.skip_init)
62                 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
63
64         sdi_basic_init();
65
66         /* 15.5.9.1.2 */
67         panel->config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
68
69         dispc_set_pol_freq(panel);
70
71         if (!sdi.skip_init)
72                 r = dispc_calc_clock_div(1, panel->timings.pixel_clock * 1000,
73                                 &cinfo);
74         else
75                 r = dispc_get_clock_div(&cinfo);
76
77         if (r)
78                 goto err0;
79
80         fck = cinfo.fck;
81         lck_div = cinfo.lck_div;
82         pck_div = cinfo.pck_div;
83
84         pck = fck / lck_div / pck_div / 1000;
85
86         if (pck != panel->timings.pixel_clock) {
87                 DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
88                                 "got %lu kHz\n",
89                                 panel->timings.pixel_clock, pck);
90
91                 panel->timings.pixel_clock = pck;
92         }
93
94
95         dispc_set_lcd_timings(&panel->timings);
96
97         r = dispc_set_clock_div(&cinfo);
98         if (r)
99                 goto err1;
100
101         if (!sdi.skip_init) {
102                 dss_sdi_init(display->hw_config.u.sdi.datapairs);
103                 dss_sdi_enable();
104                 mdelay(2);
105         }
106
107         dispc_enable_lcd_out(1);
108
109         r = panel->enable(display);
110         if (r)
111                 goto err2;
112
113         display->state = OMAP_DSS_DISPLAY_ACTIVE;
114
115         sdi.skip_init = 0;
116
117         return 0;
118 err2:
119         dispc_enable_lcd_out(0);
120 err1:
121 err0:
122         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
123         return r;
124 }
125
126 static int sdi_display_resume(struct omap_display *display);
127
128 static void sdi_display_disable(struct omap_display *display)
129 {
130         if (display->state == OMAP_DSS_DISPLAY_DISABLED)
131                 return;
132
133         if (display->state == OMAP_DSS_DISPLAY_SUSPENDED)
134                 sdi_display_resume(display);
135
136         display->panel->disable(display);
137
138         dispc_enable_lcd_out(0);
139
140         dss_sdi_disable();
141
142         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
143
144         display->state = OMAP_DSS_DISPLAY_DISABLED;
145 }
146
147 static int sdi_display_suspend(struct omap_display *display)
148 {
149         if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
150                 return -EINVAL;
151
152         if (display->panel->suspend)
153                 display->panel->suspend(display);
154
155         dispc_enable_lcd_out(0);
156
157         dss_sdi_disable();
158
159         dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
160
161         display->state = OMAP_DSS_DISPLAY_SUSPENDED;
162
163         return 0;
164 }
165
166 static int sdi_display_resume(struct omap_display *display)
167 {
168         if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
169                 return -EINVAL;
170
171         dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
172
173         dss_sdi_enable();
174         mdelay(2);
175
176         dispc_enable_lcd_out(1);
177
178         if (display->panel->resume)
179                 display->panel->resume(display);
180
181         display->state = OMAP_DSS_DISPLAY_ACTIVE;
182
183         return 0;
184 }
185
186 static int sdi_display_set_update_mode(struct omap_display *display,
187                 enum omap_dss_update_mode mode)
188 {
189         if (mode == OMAP_DSS_UPDATE_MANUAL)
190                 return -EINVAL;
191
192         if (mode == OMAP_DSS_UPDATE_DISABLED) {
193                 dispc_enable_lcd_out(0);
194                 sdi.update_enabled = 0;
195         } else {
196                 dispc_enable_lcd_out(1);
197                 sdi.update_enabled = 1;
198         }
199
200         return 0;
201 }
202
203 static enum omap_dss_update_mode sdi_display_get_update_mode(
204                 struct omap_display *display)
205 {
206         return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
207                 OMAP_DSS_UPDATE_DISABLED;
208 }
209
210 static void sdi_get_timings(struct omap_display *display,
211                         struct omap_video_timings *timings)
212 {
213         *timings = display->panel->timings;
214 }
215
216 void sdi_init_display(struct omap_display *display)
217 {
218         DSSDBG("SDI init\n");
219
220         display->enable = sdi_display_enable;
221         display->disable = sdi_display_disable;
222         display->suspend = sdi_display_suspend;
223         display->resume = sdi_display_resume;
224         display->set_update_mode = sdi_display_set_update_mode;
225         display->get_update_mode = sdi_display_get_update_mode;
226         display->get_timings = sdi_get_timings;
227 }
228
229 int sdi_init(bool skip_init)
230 {
231         /* we store this for first display enable, then clear it */
232         sdi.skip_init = skip_init;
233
234         /*
235          * Enable clocks already here, otherwise there would be a toggle
236          * of them until sdi_display_enable is called.
237          */
238         if (skip_init)
239                 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
240         return 0;
241 }
242
243 void sdi_exit(void)
244 {
245 }