]>
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/moduleparam.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/mm.h> | |
24 | #include <linux/fb.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/ioport.h> | |
27 | #include <linux/device.h> | |
28 | #include <linux/dma-mapping.h> | |
29 | #include <linux/uaccess.h> | |
30 | #include <linux/workqueue.h> | |
31 | #include <linux/string.h> | |
32 | #include <linux/version.h> | |
33 | #include <linux/proc_fs.h> | |
34 | #include <linux/vmalloc.h> | |
35 | #include <linux/debugfs.h> | |
36 | ||
37 | #include "msm_fb.h" | |
38 | ||
39 | static int ebi2_lcd_probe(struct platform_device *pdev); | |
40 | static int ebi2_lcd_remove(struct platform_device *pdev); | |
41 | ||
42 | static struct platform_driver ebi2_lcd_driver = { | |
43 | .probe = ebi2_lcd_probe, | |
44 | .remove = ebi2_lcd_remove, | |
45 | .suspend = NULL, | |
46 | .suspend_late = NULL, | |
47 | .resume_early = NULL, | |
48 | .resume = NULL, | |
49 | .shutdown = NULL, | |
50 | .driver = { | |
51 | .name = "ebi2_lcd", | |
52 | }, | |
53 | }; | |
54 | ||
55 | static void *ebi2_base; | |
56 | static void *ebi2_lcd_cfg0; | |
57 | static void *ebi2_lcd_cfg1; | |
58 | static void __iomem *lcd01_base; | |
59 | static void __iomem *lcd02_base; | |
60 | static int ebi2_lcd_resource_initialized; | |
61 | ||
62 | static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; | |
63 | static int pdev_list_cnt; | |
64 | ||
65 | static int ebi2_lcd_probe(struct platform_device *pdev) | |
66 | { | |
67 | struct msm_fb_data_type *mfd; | |
68 | struct platform_device *mdp_dev = NULL; | |
69 | struct msm_fb_panel_data *pdata = NULL; | |
70 | int rc, i; | |
71 | ||
72 | if (pdev->id == 0) { | |
73 | for (i = 0; i < pdev->num_resources; i++) { | |
74 | if (!strncmp(pdev->resource[i].name, "base", 4)) { | |
75 | ebi2_base = ioremap(pdev->resource[i].start, | |
76 | pdev->resource[i].end - | |
77 | pdev->resource[i].start + 1); | |
78 | if (!ebi2_base) { | |
79 | printk(KERN_ERR | |
80 | "ebi2_base ioremap failed!\n"); | |
81 | return -ENOMEM; | |
82 | } | |
83 | ebi2_lcd_cfg0 = (void *)(ebi2_base + 0x20); | |
84 | ebi2_lcd_cfg1 = (void *)(ebi2_base + 0x24); | |
85 | } else if (!strncmp(pdev->resource[i].name, | |
86 | "lcd01", 5)) { | |
87 | lcd01_base = ioremap(pdev->resource[i].start, | |
88 | pdev->resource[i].end - | |
89 | pdev->resource[i].start + 1); | |
90 | if (!lcd01_base) { | |
91 | printk(KERN_ERR | |
92 | "lcd01_base ioremap failed!\n"); | |
93 | return -ENOMEM; | |
94 | } | |
95 | } else if (!strncmp(pdev->resource[i].name, | |
96 | "lcd02", 5)) { | |
97 | lcd02_base = ioremap(pdev->resource[i].start, | |
98 | pdev->resource[i].end - | |
99 | pdev->resource[i].start + 1); | |
100 | if (!lcd02_base) { | |
101 | printk(KERN_ERR | |
102 | "lcd02_base ioremap failed!\n"); | |
103 | return -ENOMEM; | |
104 | } | |
105 | } | |
106 | } | |
107 | ebi2_lcd_resource_initialized = 1; | |
108 | return 0; | |
109 | } | |
110 | ||
111 | if (!ebi2_lcd_resource_initialized) | |
112 | return -EPERM; | |
113 | ||
114 | mfd = platform_get_drvdata(pdev); | |
115 | ||
116 | if (!mfd) | |
117 | return -ENODEV; | |
118 | ||
119 | if (mfd->key != MFD_KEY) | |
120 | return -EINVAL; | |
121 | ||
122 | if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) | |
123 | return -ENOMEM; | |
124 | ||
125 | if (ebi2_base == NULL) | |
126 | return -ENOMEM; | |
127 | ||
128 | mdp_dev = platform_device_alloc("mdp", pdev->id); | |
129 | if (!mdp_dev) | |
130 | return -ENOMEM; | |
131 | ||
132 | /* link to the latest pdev */ | |
133 | mfd->pdev = mdp_dev; | |
134 | mfd->dest = DISPLAY_LCD; | |
135 | ||
136 | /* add panel data */ | |
137 | if (platform_device_add_data | |
138 | (mdp_dev, pdev->dev.platform_data, | |
139 | sizeof(struct msm_fb_panel_data))) { | |
140 | printk(KERN_ERR "ebi2_lcd_probe: platform_device_add_data failed!\n"); | |
141 | platform_device_put(mdp_dev); | |
142 | return -ENOMEM; | |
143 | } | |
144 | ||
145 | /* data chain */ | |
146 | pdata = mdp_dev->dev.platform_data; | |
147 | pdata->on = panel_next_on; | |
148 | pdata->off = panel_next_off; | |
149 | pdata->next = pdev; | |
150 | ||
151 | /* get/set panel specific fb info */ | |
152 | mfd->panel_info = pdata->panel_info; | |
153 | ||
154 | if (mfd->panel_info.bpp == 24) | |
155 | mfd->fb_imgType = MDP_RGB_888; | |
156 | else | |
157 | mfd->fb_imgType = MDP_RGB_565; | |
158 | ||
159 | /* config msm ebi2 lcd register */ | |
160 | if (mfd->panel_info.pdest == DISPLAY_1) { | |
161 | outp32(ebi2_base, | |
162 | (inp32(ebi2_base) & (~(EBI2_PRIM_LCD_CLR))) | | |
163 | EBI2_PRIM_LCD_SEL); | |
164 | /* | |
165 | * current design has one set of cfg0/1 register to control | |
166 | * both EBI2 channels. so, we're using the PRIM channel to | |
167 | * configure both. | |
168 | */ | |
169 | outp32(ebi2_lcd_cfg0, mfd->panel_info.wait_cycle); | |
170 | if (mfd->panel_info.bpp == 18) | |
171 | outp32(ebi2_lcd_cfg1, 0x01000000); | |
172 | else | |
173 | outp32(ebi2_lcd_cfg1, 0x0); | |
174 | } else { | |
175 | #ifdef DEBUG_EBI2_LCD | |
176 | /* | |
177 | * confliting with QCOM SURF FPGA CS. | |
178 | * OEM should enable below for their CS mapping | |
179 | */ | |
180 | outp32(ebi2_base, (inp32(ebi2_base)&(~(EBI2_SECD_LCD_CLR))) | |
181 | |EBI2_SECD_LCD_SEL); | |
182 | #endif | |
183 | } | |
184 | ||
185 | /* | |
186 | * map cs (chip select) address | |
187 | */ | |
188 | if (mfd->panel_info.pdest == DISPLAY_1) { | |
189 | mfd->cmd_port = lcd01_base; | |
190 | mfd->data_port = | |
191 | (void *)((uint32) mfd->cmd_port + EBI2_PRIM_LCD_RS_PIN); | |
192 | mfd->data_port_phys = | |
193 | (void *)(LCD_PRIM_BASE_PHYS + EBI2_PRIM_LCD_RS_PIN); | |
194 | } else { | |
195 | mfd->cmd_port = lcd01_base; | |
196 | mfd->data_port = | |
197 | (void *)((uint32) mfd->cmd_port + EBI2_SECD_LCD_RS_PIN); | |
198 | mfd->data_port_phys = | |
199 | (void *)(LCD_SECD_BASE_PHYS + EBI2_SECD_LCD_RS_PIN); | |
200 | } | |
201 | ||
202 | /* | |
203 | * set driver data | |
204 | */ | |
205 | platform_set_drvdata(mdp_dev, mfd); | |
206 | ||
207 | /* | |
208 | * register in mdp driver | |
209 | */ | |
210 | rc = platform_device_add(mdp_dev); | |
211 | if (rc) { | |
212 | goto ebi2_lcd_probe_err; | |
213 | } | |
214 | ||
215 | pdev_list[pdev_list_cnt++] = pdev; | |
216 | return 0; | |
217 | ||
218 | ebi2_lcd_probe_err: | |
219 | platform_device_put(mdp_dev); | |
220 | return rc; | |
221 | } | |
222 | ||
223 | static int ebi2_lcd_remove(struct platform_device *pdev) | |
224 | { | |
225 | struct msm_fb_data_type *mfd; | |
226 | ||
227 | mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); | |
228 | ||
229 | if (!mfd) | |
230 | return 0; | |
231 | ||
232 | if (mfd->key != MFD_KEY) | |
233 | return 0; | |
234 | ||
235 | iounmap(mfd->cmd_port); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static int ebi2_lcd_register_driver(void) | |
241 | { | |
242 | return platform_driver_register(&ebi2_lcd_driver); | |
243 | } | |
244 | ||
245 | static int __init ebi2_lcd_driver_init(void) | |
246 | { | |
247 | return ebi2_lcd_register_driver(); | |
248 | } | |
249 | ||
250 | module_init(ebi2_lcd_driver_init); |