diff options
-rw-r--r-- | drivers/video/fbdev/simplefb.c | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index cdcf1fe46eae..d19294635a42 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/platform_data/simplefb.h> | 27 | #include <linux/platform_data/simplefb.h> |
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/clk-provider.h> | ||
29 | 30 | ||
30 | static struct fb_fix_screeninfo simplefb_fix = { | 31 | static struct fb_fix_screeninfo simplefb_fix = { |
31 | .id = "simple", | 32 | .id = "simple", |
@@ -167,8 +168,105 @@ static int simplefb_parse_pd(struct platform_device *pdev, | |||
167 | 168 | ||
168 | struct simplefb_par { | 169 | struct simplefb_par { |
169 | u32 palette[PSEUDO_PALETTE_SIZE]; | 170 | u32 palette[PSEUDO_PALETTE_SIZE]; |
171 | #ifdef CONFIG_OF | ||
172 | int clk_count; | ||
173 | struct clk **clks; | ||
174 | #endif | ||
170 | }; | 175 | }; |
171 | 176 | ||
177 | #ifdef CONFIG_OF | ||
178 | /* | ||
179 | * Clock handling code. | ||
180 | * | ||
181 | * Here we handle the clocks property of our "simple-framebuffer" dt node. | ||
182 | * This is necessary so that we can make sure that any clocks needed by | ||
183 | * the display engine that the bootloader set up for us (and for which it | ||
184 | * provided a simplefb dt node), stay up, for the life of the simplefb | ||
185 | * driver. | ||
186 | * | ||
187 | * When the driver unloads, we cleanly disable, and then release the clocks. | ||
188 | * | ||
189 | * We only complain about errors here, no action is taken as the most likely | ||
190 | * error can only happen due to a mismatch between the bootloader which set | ||
191 | * up simplefb, and the clock definitions in the device tree. Chances are | ||
192 | * that there are no adverse effects, and if there are, a clean teardown of | ||
193 | * the fb probe will not help us much either. So just complain and carry on, | ||
194 | * and hope that the user actually gets a working fb at the end of things. | ||
195 | */ | ||
196 | static int simplefb_clocks_init(struct simplefb_par *par, | ||
197 | struct platform_device *pdev) | ||
198 | { | ||
199 | struct device_node *np = pdev->dev.of_node; | ||
200 | struct clk *clock; | ||
201 | int i, ret; | ||
202 | |||
203 | if (dev_get_platdata(&pdev->dev) || !np) | ||
204 | return 0; | ||
205 | |||
206 | par->clk_count = of_clk_get_parent_count(np); | ||
207 | if (par->clk_count <= 0) | ||
208 | return 0; | ||
209 | |||
210 | par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); | ||
211 | if (!par->clks) | ||
212 | return -ENOMEM; | ||
213 | |||
214 | for (i = 0; i < par->clk_count; i++) { | ||
215 | clock = of_clk_get(np, i); | ||
216 | if (IS_ERR(clock)) { | ||
217 | if (PTR_ERR(clock) == -EPROBE_DEFER) { | ||
218 | while (--i >= 0) { | ||
219 | if (par->clks[i]) | ||
220 | clk_put(par->clks[i]); | ||
221 | } | ||
222 | kfree(par->clks); | ||
223 | return -EPROBE_DEFER; | ||
224 | } | ||
225 | dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", | ||
226 | __func__, i, PTR_ERR(clock)); | ||
227 | continue; | ||
228 | } | ||
229 | par->clks[i] = clock; | ||
230 | } | ||
231 | |||
232 | for (i = 0; i < par->clk_count; i++) { | ||
233 | if (par->clks[i]) { | ||
234 | ret = clk_prepare_enable(par->clks[i]); | ||
235 | if (ret) { | ||
236 | dev_err(&pdev->dev, | ||
237 | "%s: failed to enable clock %d: %d\n", | ||
238 | __func__, i, ret); | ||
239 | clk_put(par->clks[i]); | ||
240 | par->clks[i] = NULL; | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static void simplefb_clocks_destroy(struct simplefb_par *par) | ||
249 | { | ||
250 | int i; | ||
251 | |||
252 | if (!par->clks) | ||
253 | return; | ||
254 | |||
255 | for (i = 0; i < par->clk_count; i++) { | ||
256 | if (par->clks[i]) { | ||
257 | clk_disable_unprepare(par->clks[i]); | ||
258 | clk_put(par->clks[i]); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | kfree(par->clks); | ||
263 | } | ||
264 | #else | ||
265 | static int simplefb_clocks_init(struct simplefb_par *par, | ||
266 | struct platform_device *pdev) { return 0; } | ||
267 | static void simplefb_clocks_destroy(struct simplefb_par *par) { } | ||
268 | #endif | ||
269 | |||
172 | static int simplefb_probe(struct platform_device *pdev) | 270 | static int simplefb_probe(struct platform_device *pdev) |
173 | { | 271 | { |
174 | int ret; | 272 | int ret; |
@@ -236,6 +334,10 @@ static int simplefb_probe(struct platform_device *pdev) | |||
236 | } | 334 | } |
237 | info->pseudo_palette = par->palette; | 335 | info->pseudo_palette = par->palette; |
238 | 336 | ||
337 | ret = simplefb_clocks_init(par, pdev); | ||
338 | if (ret < 0) | ||
339 | goto error_unmap; | ||
340 | |||
239 | dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", | 341 | dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", |
240 | info->fix.smem_start, info->fix.smem_len, | 342 | info->fix.smem_start, info->fix.smem_len, |
241 | info->screen_base); | 343 | info->screen_base); |
@@ -247,13 +349,15 @@ static int simplefb_probe(struct platform_device *pdev) | |||
247 | ret = register_framebuffer(info); | 349 | ret = register_framebuffer(info); |
248 | if (ret < 0) { | 350 | if (ret < 0) { |
249 | dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); | 351 | dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); |
250 | goto error_unmap; | 352 | goto error_clocks; |
251 | } | 353 | } |
252 | 354 | ||
253 | dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); | 355 | dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); |
254 | 356 | ||
255 | return 0; | 357 | return 0; |
256 | 358 | ||
359 | error_clocks: | ||
360 | simplefb_clocks_destroy(par); | ||
257 | error_unmap: | 361 | error_unmap: |
258 | iounmap(info->screen_base); | 362 | iounmap(info->screen_base); |
259 | error_fb_release: | 363 | error_fb_release: |
@@ -264,8 +368,10 @@ error_fb_release: | |||
264 | static int simplefb_remove(struct platform_device *pdev) | 368 | static int simplefb_remove(struct platform_device *pdev) |
265 | { | 369 | { |
266 | struct fb_info *info = platform_get_drvdata(pdev); | 370 | struct fb_info *info = platform_get_drvdata(pdev); |
371 | struct simplefb_par *par = info->par; | ||
267 | 372 | ||
268 | unregister_framebuffer(info); | 373 | unregister_framebuffer(info); |
374 | simplefb_clocks_destroy(par); | ||
269 | framebuffer_release(info); | 375 | framebuffer_release(info); |
270 | 376 | ||
271 | return 0; | 377 | return 0; |