Merge branch 'for-linus' of git://git.infradead.org/users/eparis/selinux into for...
[pandora-kernel.git] / drivers / staging / msm / lcdc.c
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 <linux/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 #include <linux/pm_qos_params.h>
36
37 #include "msm_fb.h"
38
39 static int lcdc_probe(struct platform_device *pdev);
40 static int lcdc_remove(struct platform_device *pdev);
41
42 static int lcdc_off(struct platform_device *pdev);
43 static int lcdc_on(struct platform_device *pdev);
44
45 static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
46 static int pdev_list_cnt;
47
48 static struct clk *mdp_lcdc_pclk_clk;
49 static struct clk *mdp_lcdc_pad_pclk_clk;
50
51 int mdp_lcdc_pclk_clk_rate;
52 int mdp_lcdc_pad_pclk_clk_rate;
53
54 static struct platform_driver lcdc_driver = {
55         .probe = lcdc_probe,
56         .remove = lcdc_remove,
57         .suspend = NULL,
58         .resume = NULL,
59         .shutdown = NULL,
60         .driver = {
61                    .name = "lcdc",
62                    },
63 };
64
65 static struct lcdc_platform_data *lcdc_pdata;
66
67 static int lcdc_off(struct platform_device *pdev)
68 {
69         int ret = 0;
70
71         ret = panel_next_off(pdev);
72
73         clk_disable(mdp_lcdc_pclk_clk);
74         clk_disable(mdp_lcdc_pad_pclk_clk);
75
76         if (lcdc_pdata && lcdc_pdata->lcdc_power_save)
77                 lcdc_pdata->lcdc_power_save(0);
78
79         if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config)
80                 ret = lcdc_pdata->lcdc_gpio_config(0);
81
82 //      pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ , "lcdc",
83 //                                      PM_QOS_DEFAULT_VALUE);
84
85         return ret;
86 }
87
88 static int lcdc_on(struct platform_device *pdev)
89 {
90         int ret = 0;
91         struct msm_fb_data_type *mfd;
92         unsigned long panel_pixclock_freq , pm_qos_freq;
93
94         mfd = platform_get_drvdata(pdev);
95         panel_pixclock_freq = mfd->fbi->var.pixclock;
96
97         if (panel_pixclock_freq > 58000000)
98                 /* pm_qos_freq should be in Khz */
99                 pm_qos_freq = panel_pixclock_freq / 1000 ;
100         else
101                 pm_qos_freq = 58000;
102
103 //      pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ , "lcdc",
104 //                                              pm_qos_freq);
105         mfd = platform_get_drvdata(pdev);
106
107         clk_enable(mdp_lcdc_pclk_clk);
108         clk_enable(mdp_lcdc_pad_pclk_clk);
109
110         if (lcdc_pdata && lcdc_pdata->lcdc_power_save)
111                 lcdc_pdata->lcdc_power_save(1);
112         if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config)
113                 ret = lcdc_pdata->lcdc_gpio_config(1);
114
115         clk_set_rate(mdp_lcdc_pclk_clk, mfd->fbi->var.pixclock);
116         clk_set_rate(mdp_lcdc_pad_pclk_clk, mfd->fbi->var.pixclock);
117         mdp_lcdc_pclk_clk_rate = clk_get_rate(mdp_lcdc_pclk_clk);
118         mdp_lcdc_pad_pclk_clk_rate = clk_get_rate(mdp_lcdc_pad_pclk_clk);
119
120         ret = panel_next_on(pdev);
121         return ret;
122 }
123
124 static int lcdc_probe(struct platform_device *pdev)
125 {
126         struct msm_fb_data_type *mfd;
127         struct fb_info *fbi;
128         struct platform_device *mdp_dev = NULL;
129         struct msm_fb_panel_data *pdata = NULL;
130         int rc;
131
132         if (pdev->id == 0) {
133                 lcdc_pdata = pdev->dev.platform_data;
134                 return 0;
135         }
136
137         mfd = platform_get_drvdata(pdev);
138
139         if (!mfd)
140                 return -ENODEV;
141
142         if (mfd->key != MFD_KEY)
143                 return -EINVAL;
144
145         if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
146                 return -ENOMEM;
147
148         mdp_dev = platform_device_alloc("mdp", pdev->id);
149         if (!mdp_dev)
150                 return -ENOMEM;
151
152         /*
153          * link to the latest pdev
154          */
155         mfd->pdev = mdp_dev;
156         mfd->dest = DISPLAY_LCDC;
157
158         /*
159          * alloc panel device data
160          */
161         if (platform_device_add_data
162             (mdp_dev, pdev->dev.platform_data,
163              sizeof(struct msm_fb_panel_data))) {
164                 printk(KERN_ERR "lcdc_probe: platform_device_add_data failed!\n");
165                 platform_device_put(mdp_dev);
166                 return -ENOMEM;
167         }
168         /*
169          * data chain
170          */
171         pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
172         pdata->on = lcdc_on;
173         pdata->off = lcdc_off;
174         pdata->next = pdev;
175
176         /*
177          * get/set panel specific fb info
178          */
179         mfd->panel_info = pdata->panel_info;
180         mfd->fb_imgType = MDP_RGB_565;
181
182         fbi = mfd->fbi;
183         fbi->var.pixclock = mfd->panel_info.clk_rate;
184         fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch;
185         fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch;
186         fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch;
187         fbi->var.lower_margin = mfd->panel_info.lcdc.v_front_porch;
188         fbi->var.hsync_len = mfd->panel_info.lcdc.h_pulse_width;
189         fbi->var.vsync_len = mfd->panel_info.lcdc.v_pulse_width;
190
191         /*
192          * set driver data
193          */
194         platform_set_drvdata(mdp_dev, mfd);
195
196         /*
197          * register in mdp driver
198          */
199         rc = platform_device_add(mdp_dev);
200         if (rc)
201                 goto lcdc_probe_err;
202
203         pdev_list[pdev_list_cnt++] = pdev;
204                 return 0;
205
206 lcdc_probe_err:
207         platform_device_put(mdp_dev);
208         return rc;
209 }
210
211 static int lcdc_remove(struct platform_device *pdev)
212 {
213 //      pm_qos_remove_requirement(PM_QOS_SYSTEM_BUS_FREQ , "lcdc");
214         return 0;
215 }
216
217 static int lcdc_register_driver(void)
218 {
219         return platform_driver_register(&lcdc_driver);
220 }
221
222 static int __init lcdc_driver_init(void)
223 {
224         mdp_lcdc_pclk_clk = clk_get(NULL, "mdp_lcdc_pclk_clk");
225         if (IS_ERR(mdp_lcdc_pclk_clk)) {
226                 printk(KERN_ERR "error: can't get mdp_lcdc_pclk_clk!\n");
227                 return PTR_ERR(mdp_lcdc_pclk_clk);
228         }
229         mdp_lcdc_pad_pclk_clk = clk_get(NULL, "mdp_lcdc_pad_pclk_clk");
230         if (IS_ERR(mdp_lcdc_pad_pclk_clk)) {
231                 printk(KERN_ERR "error: can't get mdp_lcdc_pad_pclk_clk!\n");
232                 return PTR_ERR(mdp_lcdc_pad_pclk_clk);
233         }
234 //      pm_qos_add_requirement(PM_QOS_SYSTEM_BUS_FREQ , "lcdc",
235 //                              PM_QOS_DEFAULT_VALUE);
236         return lcdc_register_driver();
237 }
238
239 module_init(lcdc_driver_init);