Commit | Line | Data |
---|---|---|
9d200153 SM |
1 | /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/time.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/spinlock.h> | |
25 | #include <linux/delay.h> | |
26 | #include <mach/hardware.h> | |
27 | #include <asm/io.h> | |
28 | ||
29 | #include <asm/system.h> | |
30 | #include <asm/mach-types.h> | |
31 | #include <linux/semaphore.h> | |
32 | #include <linux/uaccess.h> | |
33 | #include <linux/clk.h> | |
34 | #include <linux/platform_device.h> | |
35 | ||
36 | #include "msm_fb.h" | |
37 | #include "mddihosti.h" | |
38 | #include "mddihost.h" | |
39 | #include <mach/gpio.h> | |
40 | #include <mach/clk.h> | |
41 | ||
42 | static int mddi_probe(struct platform_device *pdev); | |
43 | static int mddi_remove(struct platform_device *pdev); | |
44 | ||
45 | static int mddi_off(struct platform_device *pdev); | |
46 | static int mddi_on(struct platform_device *pdev); | |
47 | ||
48 | static int mddi_suspend(struct platform_device *pdev, pm_message_t state); | |
49 | static int mddi_resume(struct platform_device *pdev); | |
50 | ||
51 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
52 | static void mddi_early_suspend(struct early_suspend *h); | |
53 | static void mddi_early_resume(struct early_suspend *h); | |
54 | #endif | |
55 | ||
56 | static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; | |
57 | static int pdev_list_cnt; | |
58 | static struct clk *mddi_clk; | |
59 | static struct clk *mddi_pclk; | |
60 | static struct mddi_platform_data *mddi_pdata; | |
61 | ||
62 | static struct platform_driver mddi_driver = { | |
63 | .probe = mddi_probe, | |
64 | .remove = mddi_remove, | |
65 | #ifndef CONFIG_HAS_EARLYSUSPEND | |
66 | #ifdef CONFIG_PM | |
67 | .suspend = mddi_suspend, | |
68 | .resume = mddi_resume, | |
69 | #endif | |
70 | #endif | |
71 | .suspend_late = NULL, | |
72 | .resume_early = NULL, | |
73 | .shutdown = NULL, | |
74 | .driver = { | |
75 | .name = "mddi", | |
76 | }, | |
77 | }; | |
78 | ||
79 | extern int int_mddi_pri_flag; | |
80 | ||
81 | static int mddi_off(struct platform_device *pdev) | |
82 | { | |
83 | int ret = 0; | |
84 | ||
85 | ret = panel_next_off(pdev); | |
86 | ||
87 | if (mddi_pdata && mddi_pdata->mddi_power_save) | |
88 | mddi_pdata->mddi_power_save(0); | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static int mddi_on(struct platform_device *pdev) | |
94 | { | |
95 | int ret = 0; | |
96 | u32 clk_rate; | |
97 | struct msm_fb_data_type *mfd; | |
98 | ||
99 | mfd = platform_get_drvdata(pdev); | |
100 | ||
101 | if (mddi_pdata && mddi_pdata->mddi_power_save) | |
102 | mddi_pdata->mddi_power_save(1); | |
103 | ||
104 | clk_rate = mfd->fbi->var.pixclock; | |
105 | clk_rate = min(clk_rate, mfd->panel_info.clk_max); | |
106 | ||
107 | if (mddi_pdata && | |
108 | mddi_pdata->mddi_sel_clk && | |
109 | mddi_pdata->mddi_sel_clk(&clk_rate)) | |
110 | printk(KERN_ERR | |
111 | "%s: can't select mddi io clk targate rate = %d\n", | |
112 | __func__, clk_rate); | |
113 | ||
114 | if (clk_set_min_rate(mddi_clk, clk_rate) < 0) | |
115 | printk(KERN_ERR "%s: clk_set_min_rate failed\n", | |
116 | __func__); | |
117 | ||
118 | ret = panel_next_on(pdev); | |
119 | ||
120 | return ret; | |
121 | } | |
122 | ||
123 | static int mddi_resource_initialized; | |
124 | ||
125 | static int mddi_probe(struct platform_device *pdev) | |
126 | { | |
127 | struct msm_fb_data_type *mfd; | |
128 | struct platform_device *mdp_dev = NULL; | |
129 | struct msm_fb_panel_data *pdata = NULL; | |
130 | int rc; | |
131 | resource_size_t size ; | |
132 | u32 clk_rate; | |
133 | ||
134 | if ((pdev->id == 0) && (pdev->num_resources >= 0)) { | |
135 | mddi_pdata = pdev->dev.platform_data; | |
136 | ||
137 | size = resource_size(&pdev->resource[0]); | |
138 | msm_pmdh_base = ioremap(pdev->resource[0].start, size); | |
139 | ||
140 | MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n", | |
141 | pdev->resource[0].start, (int) msm_pmdh_base); | |
142 | ||
143 | if (unlikely(!msm_pmdh_base)) | |
144 | return -ENOMEM; | |
145 | ||
146 | if (mddi_pdata && mddi_pdata->mddi_power_save) | |
147 | mddi_pdata->mddi_power_save(1); | |
148 | ||
149 | mddi_resource_initialized = 1; | |
150 | return 0; | |
151 | } | |
152 | ||
153 | if (!mddi_resource_initialized) | |
154 | return -EPERM; | |
155 | ||
156 | mfd = platform_get_drvdata(pdev); | |
157 | ||
158 | if (!mfd) | |
159 | return -ENODEV; | |
160 | ||
161 | if (mfd->key != MFD_KEY) | |
162 | return -EINVAL; | |
163 | ||
164 | if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) | |
165 | return -ENOMEM; | |
166 | ||
167 | mdp_dev = platform_device_alloc("mdp", pdev->id); | |
168 | if (!mdp_dev) | |
169 | return -ENOMEM; | |
170 | ||
171 | /* | |
172 | * link to the latest pdev | |
173 | */ | |
174 | mfd->pdev = mdp_dev; | |
175 | mfd->dest = DISPLAY_LCD; | |
176 | ||
177 | /* | |
178 | * alloc panel device data | |
179 | */ | |
180 | if (platform_device_add_data | |
181 | (mdp_dev, pdev->dev.platform_data, | |
182 | sizeof(struct msm_fb_panel_data))) { | |
183 | printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n"); | |
184 | platform_device_put(mdp_dev); | |
185 | return -ENOMEM; | |
186 | } | |
187 | /* | |
188 | * data chain | |
189 | */ | |
190 | pdata = mdp_dev->dev.platform_data; | |
191 | pdata->on = mddi_on; | |
192 | pdata->off = mddi_off; | |
193 | pdata->next = pdev; | |
194 | ||
195 | /* | |
196 | * get/set panel specific fb info | |
197 | */ | |
198 | mfd->panel_info = pdata->panel_info; | |
199 | mfd->fb_imgType = MDP_RGB_565; | |
200 | ||
201 | clk_rate = mfd->panel_info.clk_max; | |
202 | if (mddi_pdata && | |
203 | mddi_pdata->mddi_sel_clk && | |
204 | mddi_pdata->mddi_sel_clk(&clk_rate)) | |
205 | printk(KERN_ERR | |
206 | "%s: can't select mddi io clk targate rate = %d\n", | |
207 | __func__, clk_rate); | |
208 | ||
209 | if (clk_set_max_rate(mddi_clk, clk_rate) < 0) | |
210 | printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__); | |
211 | mfd->panel_info.clk_rate = mfd->panel_info.clk_min; | |
212 | ||
213 | /* | |
214 | * set driver data | |
215 | */ | |
216 | platform_set_drvdata(mdp_dev, mfd); | |
217 | ||
218 | /* | |
219 | * register in mdp driver | |
220 | */ | |
221 | rc = platform_device_add(mdp_dev); | |
222 | if (rc) | |
223 | goto mddi_probe_err; | |
224 | ||
225 | pdev_list[pdev_list_cnt++] = pdev; | |
226 | ||
227 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
228 | mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; | |
229 | mfd->mddi_early_suspend.suspend = mddi_early_suspend; | |
230 | mfd->mddi_early_suspend.resume = mddi_early_resume; | |
231 | register_early_suspend(&mfd->mddi_early_suspend); | |
232 | #endif | |
233 | ||
234 | return 0; | |
235 | ||
236 | mddi_probe_err: | |
237 | platform_device_put(mdp_dev); | |
238 | return rc; | |
239 | } | |
240 | ||
241 | static int mddi_pad_ctrl; | |
242 | static int mddi_power_locked; | |
243 | static int mddi_is_in_suspend; | |
244 | ||
245 | void mddi_disable(int lock) | |
246 | { | |
247 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
248 | ||
249 | if (mddi_power_locked) | |
250 | return; | |
251 | ||
252 | if (lock) | |
253 | mddi_power_locked = 1; | |
254 | ||
255 | if (mddi_host_timer.function) | |
256 | del_timer_sync(&mddi_host_timer); | |
257 | ||
258 | mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL); | |
259 | mddi_host_reg_out(PAD_CTL, 0x0); | |
260 | ||
261 | if (clk_set_min_rate(mddi_clk, 0) < 0) | |
262 | printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__); | |
263 | ||
264 | clk_disable(mddi_clk); | |
265 | if (mddi_pclk) | |
266 | clk_disable(mddi_pclk); | |
267 | disable_irq(INT_MDDI_PRI); | |
268 | ||
269 | if (mddi_pdata && mddi_pdata->mddi_power_save) | |
270 | mddi_pdata->mddi_power_save(0); | |
271 | } | |
272 | ||
273 | static int mddi_suspend(struct platform_device *pdev, pm_message_t state) | |
274 | { | |
275 | if (mddi_is_in_suspend) | |
276 | return 0; | |
277 | ||
278 | mddi_is_in_suspend = 1; | |
279 | mddi_disable(0); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static int mddi_resume(struct platform_device *pdev) | |
284 | { | |
285 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
286 | ||
287 | if (!mddi_is_in_suspend) | |
288 | return 0; | |
289 | ||
290 | mddi_is_in_suspend = 0; | |
291 | ||
292 | if (mddi_power_locked) | |
293 | return 0; | |
294 | ||
295 | enable_irq(INT_MDDI_PRI); | |
296 | clk_enable(mddi_clk); | |
297 | if (mddi_pclk) | |
298 | clk_enable(mddi_pclk); | |
299 | mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl); | |
300 | ||
301 | if (mddi_host_timer.function) | |
302 | mddi_host_timer_service(0); | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
308 | static void mddi_early_suspend(struct early_suspend *h) | |
309 | { | |
310 | pm_message_t state; | |
311 | struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, | |
312 | mddi_early_suspend); | |
313 | ||
314 | state.event = PM_EVENT_SUSPEND; | |
315 | mddi_suspend(mfd->pdev, state); | |
316 | } | |
317 | ||
318 | static void mddi_early_resume(struct early_suspend *h) | |
319 | { | |
320 | struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, | |
321 | mddi_early_suspend); | |
322 | mddi_resume(mfd->pdev); | |
323 | } | |
324 | #endif | |
325 | ||
326 | static int mddi_remove(struct platform_device *pdev) | |
327 | { | |
328 | if (mddi_host_timer.function) | |
329 | del_timer_sync(&mddi_host_timer); | |
330 | ||
331 | iounmap(msm_pmdh_base); | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | static int mddi_register_driver(void) | |
337 | { | |
338 | return platform_driver_register(&mddi_driver); | |
339 | } | |
340 | ||
341 | static int __init mddi_driver_init(void) | |
342 | { | |
343 | int ret; | |
344 | ||
345 | mddi_clk = clk_get(NULL, "mddi_clk"); | |
346 | if (IS_ERR(mddi_clk)) { | |
347 | printk(KERN_ERR "can't find mddi_clk \n"); | |
348 | return PTR_ERR(mddi_clk); | |
349 | } | |
350 | clk_enable(mddi_clk); | |
351 | ||
352 | mddi_pclk = clk_get(NULL, "mddi_pclk"); | |
353 | if (IS_ERR(mddi_pclk)) | |
354 | mddi_pclk = NULL; | |
355 | else | |
356 | clk_enable(mddi_pclk); | |
357 | ||
358 | ret = mddi_register_driver(); | |
359 | if (ret) { | |
360 | clk_disable(mddi_clk); | |
361 | clk_put(mddi_clk); | |
362 | if (mddi_pclk) { | |
363 | clk_disable(mddi_pclk); | |
364 | clk_put(mddi_pclk); | |
365 | } | |
366 | printk(KERN_ERR "mddi_register_driver() failed!\n"); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | mddi_init(); | |
371 | ||
372 | return ret; | |
373 | } | |
374 | ||
375 | module_init(mddi_driver_init); |