aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/simplefb.c108
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
30static struct fb_fix_screeninfo simplefb_fix = { 31static 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
168struct simplefb_par { 169struct 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 */
196static 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
248static 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
265static int simplefb_clocks_init(struct simplefb_par *par,
266 struct platform_device *pdev) { return 0; }
267static void simplefb_clocks_destroy(struct simplefb_par *par) { }
268#endif
269
172static int simplefb_probe(struct platform_device *pdev) 270static 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
359error_clocks:
360 simplefb_clocks_destroy(par);
257error_unmap: 361error_unmap:
258 iounmap(info->screen_base); 362 iounmap(info->screen_base);
259error_fb_release: 363error_fb_release:
@@ -264,8 +368,10 @@ error_fb_release:
264static int simplefb_remove(struct platform_device *pdev) 368static 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;