upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / fimdfreq.c
1 /* linux/arch/arm/mach-s5pv310/fimdfreq.c
2  *
3  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com/
5  *
6  * EXYNOS4 - FIMD Frequency scaling support
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/err.h>
16 #include <linux/slab.h>
17 #include <linux/init.h>
18 #include <linux/cpu.h>
19 #include <linux/sysfs.h>
20 #include <linux/jiffies.h>
21 #include <linux/kernel_stat.h>
22 #include <linux/mutex.h>
23 #include <linux/ktime.h>
24 #include <linux/cpufreq.h>
25 #include <linux/reboot.h>
26 #include <linux/clk.h>
27 #include <linux/suspend.h>
28 #include <linux/notifier.h>
29 #include <linux/platform_device.h>
30 #include <linux/pm_runtime.h>
31 #include <linux/fb.h>
32 #include <linux/device.h>
33
34 #include <mach/ppmu.h>
35 #include <mach/map.h>
36 #include <mach/fimdfreq.h>
37
38 #include <plat/map-s5p.h>
39 #include <plat/pd.h>
40 #include <plat/devs.h>
41
42 #define FIMDFREQ_DEVICE_ATTR_RW(name)                   \
43         static DEVICE_ATTR(name, 0644,                  \
44         show_fimdfreq_##name, store_fimdfreq_##name)
45 #define FIMDFREQ_DEVICE_ATTR_RO(name)                   \
46         static DEVICE_ATTR(name, 0444, show_fimdfreq_##name, NULL)
47
48 #define FIMDFREQ_ON             1
49 #define FIMDFREQ_OFF            0
50
51 #define DEFAULT_MAX_LOAD        1000
52 #define DEFAULT_PERIOD_TIME     100             /* us(Millisecond) */
53 #define DEFAULT_DELAY_TIME      1000            /* us(Millisecond) */
54
55 /* FIMD Frequeync scaling have dependency on LCD power domain
56  * which include clock of PPMULCD0(ppmu_fimd0) because it use
57  * FIMD PPMU to check current state of FIMD.
58  */
59 static struct platform_device *pd_lcd0_pdev = &s5pv310_device_pd[PD_LCD0];
60
61 struct fimdfreq_platdata {
62         unsigned long period;
63         struct delayed_work work_ppmu;
64         struct delayed_work work_lowfreq;
65
66         struct s5pv310_ppmu_hw ppmu;
67         struct clk *ppmu_clk;
68
69         struct notifier_block nb_fb;
70         struct notifier_block nb_recv;
71         unsigned int curr_state;
72
73         enum fimdfreq_level curr_level;
74         enum fimdfreq_level prev_level;
75 };
76
77 /*
78  * Control FIMD PPMU(Performance Monitoring Unit)
79  */
80 #define PPMU_BEVT0SEL           0
81 #define PPMU_BEVT1SEL           1
82 #define PPMU_BEVT2SEL           2
83 #define PPMU_BEVT3SEL           3
84
85 #define PPMU_EVT_WO_BUSY_CYCLE_COUNT    0x1     /* Write busy cycle count */
86 #define PPMU_EVT_RW_BUSY_CYCLE_COUNT    0x2     /* RW busy cycle count */
87 #define PPMU_EVT_RO_REQUEST_COUNT       0x3     /* Read Request count */
88 #define PPMU_EVT_WO_REQUEST_COUNT       0x4     /* Write Request count */
89 #define PPMU_EVT_RO_DATA_COUNT          0x5     /* Read data count */
90 #define PPMU_EVT_WO_DATA_COUNT          0x6     /* Write data count */
91
92 static void fimdfreq_stop_ppmu(struct s5pv310_ppmu_hw *ppmu)
93 {
94         s5pv310_ppmu_stop(ppmu);
95         s5pv310_ppmu_update(ppmu);
96 }
97
98 static void fimdfreq_start_ppmu(struct s5pv310_ppmu_hw *ppmu)
99 {
100         s5pv310_ppmu_reset(ppmu);
101         s5pv310_ppmu_setevent(ppmu, PPMU_EVT_RO_DATA_COUNT, PPMU_BEVT0SEL);
102         s5pv310_ppmu_setevent(ppmu, PPMU_EVT_WO_DATA_COUNT, PPMU_BEVT1SEL);
103         s5pv310_ppmu_start(ppmu);
104 }
105
106 static unsigned int fimdfreq_monitor_ppmu(struct fimdfreq_platdata *pdata)
107 {
108         struct s5pv310_ppmu_hw *ppmu = &pdata->ppmu;
109         unsigned int ppmu_load = 0;
110
111         fimdfreq_stop_ppmu(ppmu);
112
113         if (ppmu->ccnt == 0)
114                 ppmu->ccnt = DEFAULT_MAX_LOAD;
115         ppmu_load += ppmu->count[PPMU_BEVT0SEL]; /* Read data count */
116         ppmu_load += ppmu->count[PPMU_BEVT1SEL]; /* Write data count */
117         ppmu_load = (ppmu_load * DEFAULT_MAX_LOAD) / ppmu->ccnt;
118
119         fimdfreq_start_ppmu(ppmu);
120
121         return ppmu_load;
122 }
123
124 /*
125  * Routines for FIMDFERQ transition notification to Display
126  *      - fimdfreq notifiers is used in display client to receive changed
127  *      FIMD frequency level.
128  *      - Routine: From FIMD frequency scaling To Display client
129  */
130 static BLOCKING_NOTIFIER_HEAD(fimdfreq_notifier_client_list);
131
132 int fimdfreq_register_display(struct notifier_block *nb)
133 {
134         return blocking_notifier_chain_register(
135                         &fimdfreq_notifier_client_list, nb);
136 }
137 EXPORT_SYMBOL(fimdfreq_register_display);
138
139 int fimdfreq_unregister_display(struct notifier_block *nb)
140 {
141         return blocking_notifier_chain_unregister(
142                         &fimdfreq_notifier_client_list, nb);
143 }
144 EXPORT_SYMBOL(fimdfreq_unregister_display);
145
146 static int fimdfreq_send_request_to_display(unsigned long val, void *v)
147 {
148         return blocking_notifier_call_chain(
149                         &fimdfreq_notifier_client_list, val, v);
150 }
151
152 /*
153  * Routines for Report(HF/LF) transition notification to FIMDFREQ
154  *      - Routine : From Notification receiver To FIMDFREQ
155  *              - Input Subsystem
156  *              - CPU load
157  *              - MFC
158  *              - GPU
159  *              - ...
160  */
161 static BLOCKING_NOTIFIER_HEAD(fimdfreq_notifier_receiver_list);
162
163 static int fimdfreq_register_receiver(struct notifier_block *nb)
164 {
165         return blocking_notifier_chain_register(
166                         &fimdfreq_notifier_receiver_list, nb);
167 }
168
169 static int fimdfreq_unregister_receiver(struct notifier_block *nb)
170 {
171         return blocking_notifier_chain_unregister(
172                         &fimdfreq_notifier_receiver_list, nb);
173 }
174
175 int fimdfreq_send_request_from_modules(unsigned long val, void *v)
176 {
177         return blocking_notifier_call_chain(
178                         &fimdfreq_notifier_receiver_list, val, v);
179 }
180 EXPORT_SYMBOL(fimdfreq_send_request_from_modules);
181
182 /*
183  * Enable/Disable FIMD frequency scaling
184  */
185 static int fimdfreq_disable(struct fimdfreq_platdata *pdata, bool wq_off)
186 {
187         if (wq_off) {
188                 /* Cancel workqueue of FIMD PPMU */
189                 cancel_delayed_work(&pdata->work_ppmu);
190         }
191
192         /* Stop FIMD PPMU */
193         fimdfreq_stop_ppmu(&pdata->ppmu);
194
195         /* Disable clock of FIMD PPMU */
196         pdata->ppmu_clk = clk_get(NULL, "ppmu_fimd0");
197         if (IS_ERR(pdata->ppmu_clk))
198                 return -EINVAL;
199         clk_disable(pdata->ppmu_clk);
200         clk_put(pdata->ppmu_clk);
201
202         /* Calcel workqueue which set low frequency of display
203          * if it is pending state before execution. */
204         if(delayed_work_pending(&pdata->work_lowfreq))
205                 cancel_delayed_work(&pdata->work_lowfreq);
206
207         /* Set default state of display */
208         fimdfreq_send_request_to_display(FIMDFREQ_HF, NULL);
209
210         /* Lastly, turn off LCD0 power domain */
211         if (!pm_runtime_suspended(&pd_lcd0_pdev->dev))
212                 pm_runtime_put(&pd_lcd0_pdev->dev);
213
214         pdata->curr_state = FIMDFREQ_OFF;
215         pdata->prev_level = FIMDFREQ_DEFAULT;
216         pdata->curr_level = FIMDFREQ_DEFAULT;
217
218         return 0;
219 }
220
221 static int fimdfreq_enable(struct fimdfreq_platdata *pdata, bool wq_off)
222 {
223         /* Firstly, turn on LCD0 power domain */
224         if (pm_runtime_suspended(&pd_lcd0_pdev->dev))
225                 pm_runtime_get_sync(&pd_lcd0_pdev->dev);
226
227         /* Enable clock of FIMD PPMU */
228         pdata->ppmu_clk = clk_get(NULL, "ppmu_fimd0");
229         if (IS_ERR(pdata->ppmu_clk))
230                 return -EINVAL;
231         clk_enable(pdata->ppmu_clk);
232         clk_put(pdata->ppmu_clk);
233
234         /* Start FIMD PPMU */
235         fimdfreq_start_ppmu(&pdata->ppmu);
236
237         if (wq_off) {
238                 /* Schedule workqueue of FIMD PPMU */
239                 schedule_delayed_work(&pdata->work_ppmu,
240                         msecs_to_jiffies(pdata->period));
241         }
242
243         pdata->curr_state = FIMDFREQ_ON;
244         pdata->prev_level = FIMDFREQ_DEFAULT;
245         pdata->curr_level = FIMDFREQ_DEFAULT;
246
247         return 0;
248 }
249
250 /*
251  * Governor for FIMD Frequency scaling
252  */
253 static int fimdfreq_governor(struct fimdfreq_platdata *pdata,
254                 unsigned int load)
255 {
256         /*
257          * The governor should implement the govornor policy of FIMD frequency
258          * scaling. So, the previous value of FIMD PPMU is used to decide
259          * whether LCD would need certain refresh rate.
260          *
261          * If fimd need higer refresh rate than normal state,
262          * we must call notifier chanin to raise LCD refresch rate
263          * from 30fps to 60fps.
264          */
265
266         /* Inform display client of changed frequency level */
267         /* fimdfreq_send_report_to_display(FIMDFREQ_HF, NULL); */
268         /* TODO */
269
270         return FIMDFREQ_DEFAULT;
271 }
272
273 /*
274  * Timer to monitor FIMD PPMU
275  */
276 static void fimdfreq_ppmu_timer(struct work_struct *work)
277 {
278         struct fimdfreq_platdata *pdata =
279                 container_of(work, struct fimdfreq_platdata, work_ppmu.work);
280         unsigned int period = pdata->period;
281         unsigned int load;
282         unsigned int level;
283
284         if (pdata->curr_state == FIMDFREQ_ON) {
285                 /* Check the value of FIMD PPMU */
286                 load = fimdfreq_monitor_ppmu(pdata);
287
288                 /* Decide FIMD frequency level by using the value FIMD PPMU */
289                 level = fimdfreq_governor(pdata, load);
290
291                 printk(KERN_DEBUG "FIMDFREQ - Load:%d\t/ Level:%d\n",
292                                 load, level);
293         }
294
295         /* Restart delayed workqueue to check FIMD PPMU periodically */
296         schedule_delayed_work(&pdata->work_ppmu, msecs_to_jiffies(period));
297
298         return;
299 }
300
301 /*
302  * Timer to set display of low frequency after 1 second
303  */
304 static void fimdfreq_set_lowfreq(struct work_struct *work)
305 {
306         struct fimdfreq_platdata *pdata =
307                 container_of(work, struct fimdfreq_platdata, work_lowfreq.work);
308
309         /* Inform display client of changed frequency level */
310         fimdfreq_send_request_to_display(FIMDFREQ_LF, NULL);
311
312         pdata->prev_level = pdata->curr_level;
313         pdata->curr_level = FIMDFREQ_LF;
314
315         printk(KERN_DEBUG "FIMDFREQ - Notify client of Low Frequency: %d\n",
316                         pdata->curr_level);
317
318         return;
319 }
320
321 /*
322  * FIMD frequency scaling have to call fimdfreq notifier chain
323  * to inform Display client of changed FIMD frequency level
324  * when FIMD FREQ receives the report from various modules.
325  */
326 static int fimdfreq_notifier_callback(struct notifier_block *this,
327                                         unsigned long event, void *data)
328 {
329         struct fimdfreq_platdata *pdata
330                 = container_of(this, struct fimdfreq_platdata, nb_recv);
331
332         if (pdata->curr_state == FIMDFREQ_OFF)
333                 return 0;
334
335         switch (event) {
336         case FIMDFREQ_HF_FORCED:
337         case FIMDFREQ_HF:
338                 if(delayed_work_pending(&pdata->work_lowfreq))
339                         cancel_delayed_work(&pdata->work_lowfreq);
340
341                 if (pdata->curr_level == event)
342                         return 0;
343
344                 pdata->prev_level = pdata->curr_level;
345                 pdata->curr_level = (enum fimdfreq_level)event;
346
347                 /* Inform display client of changed frequency level */
348                 fimdfreq_send_request_to_display(event, NULL);
349                 break;
350         case FIMDFREQ_LF:
351                 schedule_delayed_work(&pdata->work_lowfreq,
352                                 msecs_to_jiffies(DEFAULT_DELAY_TIME));
353                 return 0;
354         case FIMDFREQ_DEFAULT:
355         default:
356                 /* Ignore */
357                 return -EINVAL;
358         }
359
360         /* TODO */
361
362         printk(KERN_DEBUG "FIMDFREQ - Notify client of High Frequency: %ld\n",
363                         event);
364         return 0;
365 }
366
367 /*
368  * Create sysfs node
369  * - freq_level  : get current state of FIMDFREQ
370  * - freq_state  : get current state of FIMDFREQ
371  * - freq_period : set/get period of FIMD PPMU timer
372  */
373 static ssize_t show_fimdfreq_level(struct device *dev,
374                                 struct device_attribute *attr,
375                                 char *buf)
376 {
377         struct fimdfreq_platdata *pdata = dev->platform_data;
378         return sprintf(buf, "%u\n", pdata->curr_level);
379 }
380
381 static ssize_t show_fimdfreq_state(struct device *dev,
382                                 struct device_attribute *attr,
383                                 char *buf)
384 {
385         struct fimdfreq_platdata *pdata = dev->platform_data;
386         return sprintf(buf, "%u\n", pdata->curr_state);
387 }
388
389 static ssize_t store_fimdfreq_state(struct device *dev,
390                                 struct device_attribute *attr,
391                                 char *buf)
392 {
393         struct fimdfreq_platdata *pdata = dev->platform_data;
394         unsigned int state;
395         int ret;
396
397         ret = sscanf(buf, "%u", &state);
398         if (ret != 1)
399                 return -EINVAL;
400
401         if (pdata->curr_state == state)
402                 goto ret;
403
404         if (state == FIMDFREQ_ON)
405                 fimdfreq_enable(pdata, true);
406         else
407                 fimdfreq_disable(pdata, true);
408
409 ret:
410         return sprintf(buf, "%u\n", pdata->curr_state);
411 }
412
413 static ssize_t show_fimdfreq_period(struct device *dev,
414                                 struct device_attribute *attr,
415                                 char *buf)
416 {
417         struct fimdfreq_platdata *pdata = dev->platform_data;
418         return sprintf(buf, "%ld", pdata->period);
419 }
420 static ssize_t store_fimdfreq_period(struct device *dev,
421                                 struct device_attribute *attr,
422                                 const char *buf, size_t count)
423 {
424         struct fimdfreq_platdata *pdata = dev->platform_data;
425         unsigned long period;
426         int ret;
427
428         ret = sscanf(buf, "%ld", &period);
429         if (ret != 1)
430                 return -EINVAL;
431
432         if (pdata->period == period)
433                 goto ret;
434         else {
435                 if (pdata->curr_state == FIMDFREQ_OFF)
436                         pdata->period = period;
437                 else {
438                         fimdfreq_disable(pdata, true);
439                         pdata->period = period;
440                         fimdfreq_enable(pdata, true);
441                 }
442         }
443
444 ret:
445         return sprintf(buf, "%ld\n", pdata->period);
446 }
447 FIMDFREQ_DEVICE_ATTR_RO(level);
448 FIMDFREQ_DEVICE_ATTR_RW(state);
449 FIMDFREQ_DEVICE_ATTR_RW(period);
450
451 static int fimdfreq_create_sysfs(struct platform_device *pdev)
452 {
453         int ret = 0;
454
455         ret = device_create_file(&pdev->dev, &dev_attr_level);
456         if (ret < 0) {
457                 pr_warn("Failed to register attributes: %d\n", ret);
458                 goto err_level;
459         }
460
461         ret = device_create_file(&pdev->dev, &dev_attr_state);
462         if (ret < 0) {
463                 pr_warn("Failed to register attributes: %d\n", ret);
464                 goto err_state;
465         }
466
467         ret = device_create_file(&pdev->dev, &dev_attr_period);
468         if (ret < 0) {
469                 pr_warn("Failed to register attributes: %d\n", ret);
470                 goto err_period;
471         }
472
473         return 0;
474
475 err_period:
476         device_remove_file(&pdev->dev, &dev_attr_period);
477 err_state:
478         device_remove_file(&pdev->dev, &dev_attr_state);
479 err_level:
480         device_remove_file(&pdev->dev, &dev_attr_level);
481
482         return ret;
483 }
484
485 /*
486  * Register FIMD Frequency scaling as client to fb notifer
487  *
488  * This callback gets called when something important happens inside a
489  * framebuffer driver. We're looking if that important event is blanking,
490  * and if it is, we're switching g2d power as well.
491  */
492 static int fb_notifier_callback(struct notifier_block *this,
493                                  unsigned long event, void *data)
494 {
495         struct fimdfreq_platdata *pdata
496                         = container_of(this, struct fimdfreq_platdata, nb_fb);
497         struct fb_event *evdata = data;
498         int blank = *(int *)evdata->data;
499
500         switch (blank) {
501         case FB_BLANK_UNBLANK:
502                 if (pdata->curr_state == FIMDFREQ_OFF)
503                         fimdfreq_enable(pdata, false);
504                 break;
505         case FB_BLANK_POWERDOWN:
506                 if (pdata->curr_state == FIMDFREQ_ON)
507                         fimdfreq_disable(pdata, false);
508                 break;
509         default:
510                 return 0;
511         }
512
513         return 0;
514 }
515
516 /*
517  * FIMD Frequency scaling
518  */
519 static int fimdfreq_probe(struct platform_device *pdev)
520 {
521         struct fimdfreq_platdata *pdata;
522         int ret = 0;
523
524         pdata = pdev->dev.platform_data;
525         if (!pdata) {
526                 pr_err("Fail to get platform data of fimdfreq\n");
527                 ret = -ENOMEM;
528                 goto err_bad_pdata;
529         }
530
531         pdata->curr_state = FIMDFREQ_ON;
532         pdata->curr_level = FIMDFREQ_DEFAULT;
533         pdata->period = DEFAULT_PERIOD_TIME;
534         pdata->ppmu.hw_base = S5P_VA_PPMU_LCD0;
535
536         pdata->ppmu_clk = clk_get(NULL, "ppmu_fimd0");
537         if (IS_ERR(pdata->ppmu_clk)) {
538                 pr_err("Failed to get ppmu_fimd0 clock\n");
539                 ret = -EINVAL;
540                 goto err_clk;
541         }
542
543         memset(&pdata->nb_fb, 0, sizeof(pdata->nb_fb));
544         pdata->nb_fb.notifier_call = fb_notifier_callback;
545         ret = fb_register_client(&pdata->nb_fb);
546         if (ret < 0) {
547                 pr_err("Failed to get ppmu_fimd0 clock: %d\n", ret);
548                 goto err_nb_fb;
549         }
550
551         memset(&pdata->nb_recv, 0, sizeof(pdata->nb_recv));
552         pdata->nb_recv.notifier_call = fimdfreq_notifier_callback;
553         ret = fimdfreq_register_receiver(&pdata->nb_recv);
554         if (ret < 0) {
555                 pr_err("Failed to get ppmu_fimd0 clock: %d\n", ret);
556                 goto err_nb_recv;
557         }
558
559         ret = fimdfreq_create_sysfs(pdev);
560         if (ret < 0) {
561                 pr_err("Failed to create sysfs: %d\n", ret);
562                 goto err_sysfs;
563         }
564
565         pm_runtime_get_sync(&pd_lcd0_pdev->dev);
566         clk_enable(pdata->ppmu_clk);
567         fimdfreq_start_ppmu(&pdata->ppmu);
568
569         INIT_DELAYED_WORK(&pdata->work_ppmu, fimdfreq_ppmu_timer);
570         INIT_DELAYED_WORK(&pdata->work_lowfreq, fimdfreq_set_lowfreq);
571         schedule_delayed_work(&pdata->work_ppmu,
572                         msecs_to_jiffies(pdata->period));
573
574         return ret;
575
576 err_sysfs:
577 err_nb_recv:
578         fimdfreq_unregister_receiver(&pdata->nb_recv);
579 err_nb_fb:
580         fb_unregister_client(&pdata->nb_fb);
581 err_clk:
582         clk_disable(pdata->ppmu_clk);
583         clk_put(pdata->ppmu_clk);
584 err_bad_pdata:
585         kfree(pdata);
586
587         return ret;
588 }
589
590 static int __devexit fimdfreq_remove(struct platform_device *pdev)
591 {
592         struct fimdfreq_platdata *pdata = pdev->dev.platform_data;
593
594         fimdfreq_unregister_receiver(&pdata->nb_recv);
595         fb_unregister_client(&pdata->nb_fb);
596
597         fimdfreq_disable(pdata, true);
598
599         device_remove_file(&pdev->dev, &dev_attr_period);
600         device_remove_file(&pdev->dev, &dev_attr_state);
601         device_remove_file(&pdev->dev, &dev_attr_level);
602
603         kfree(pdata);
604
605         return 0;
606 }
607
608 #ifdef CONFIG_PM
609 static int fimdfreq_suspend(struct device *dev)
610 {
611         struct fimdfreq_platdata *pdata = dev->platform_data;
612
613         if (pdata->curr_state == FIMDFREQ_OFF)
614                 return 0;
615
616         return fimdfreq_disable(pdata, true);
617 }
618
619 static int fimdfreq_resume(struct device *dev)
620 {
621         struct fimdfreq_platdata *pdata = dev->platform_data;
622
623         if (pdata->curr_state == FIMDFREQ_OFF)
624                 return 0;
625
626         return fimdfreq_enable(pdata, true);
627 }
628
629 static const struct dev_pm_ops fimdfreq_dev_pm_ops = {
630         .suspend        = fimdfreq_suspend,
631         .resume         = fimdfreq_resume,
632 };
633
634 #define FIMDFREQ_DEV_PM_OPS     (&fimdfreq_dev_pm_ops)
635 #else
636 #define FIMDFREQ_DEV_PM_OPS     NULL
637 #endif  /* CONFIG_PM */
638
639 static struct fimdfreq_platdata fimdfreq_pdata;
640 static struct platform_device fimdfreq_pdev = {
641         .name   = "fimdfreq",
642         .id     = -1,
643         .dev    = {
644                 .platform_data = &fimdfreq_pdata,
645         },
646 };
647
648 static struct platform_driver fimdfreq_driver = {
649         .driver = {
650                 .name   = "fimdfreq",
651                 .owner  = THIS_MODULE,
652                 .pm     = FIMDFREQ_DEV_PM_OPS,
653         },
654         .probe          = fimdfreq_probe,
655         .remove         = __devexit_p(fimdfreq_remove),
656 };
657
658 static int __init fimdfreq_init(void)
659 {
660         int ret = 0;
661
662         ret = platform_device_register(&fimdfreq_pdev);
663         if (ret != 0) {
664                 pr_err("Failed to register dummy register device: %d\n", ret);
665                 goto err_dev;
666         }
667
668         ret = platform_driver_register(&fimdfreq_driver);
669         if (ret != 0) {
670                 pr_err("Failed to register FIMD frequency scaling driver: %d\n",
671                                 ret);
672                 goto err_driver;
673         }
674
675         return ret;
676
677 err_driver:
678         platform_device_unregister(&fimdfreq_pdev);
679 err_dev:
680         return ret;
681 }
682 static void __exit fimdfreq_exit(void)
683 {
684         platform_driver_unregister(&fimdfreq_driver);
685         platform_device_unregister(&fimdfreq_pdev);
686 }
687
688 module_init(fimdfreq_init);
689 module_exit(fimdfreq_exit);
690
691 MODULE_DESCRIPTION("FIMD DEVFREQ driver");
692 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
693 MODULE_AUTHOR("Myungjoo Ham <myungjoo.ham@samsung.com>");
694 MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
695 MODULE_LICENSE("GPL");