1 /* linux/arch/arm/mach-s5pv310/fimdfreq.c
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
6 * EXYNOS4 - FIMD Frequency scaling support
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.
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>
32 #include <linux/device.h>
34 #include <mach/ppmu.h>
36 #include <mach/fimdfreq.h>
38 #include <plat/map-s5p.h>
40 #include <plat/devs.h>
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)
49 #define FIMDFREQ_OFF 0
51 #define DEFAULT_MAX_LOAD 1000
52 #define DEFAULT_PERIOD_TIME 100 /* us(Millisecond) */
53 #define DEFAULT_DELAY_TIME 1000 /* us(Millisecond) */
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.
59 static struct platform_device *pd_lcd0_pdev = &s5pv310_device_pd[PD_LCD0];
61 struct fimdfreq_platdata {
63 struct delayed_work work_ppmu;
64 struct delayed_work work_lowfreq;
66 struct s5pv310_ppmu_hw ppmu;
69 struct notifier_block nb_fb;
70 struct notifier_block nb_recv;
71 unsigned int curr_state;
73 enum fimdfreq_level curr_level;
74 enum fimdfreq_level prev_level;
78 * Control FIMD PPMU(Performance Monitoring Unit)
80 #define PPMU_BEVT0SEL 0
81 #define PPMU_BEVT1SEL 1
82 #define PPMU_BEVT2SEL 2
83 #define PPMU_BEVT3SEL 3
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 */
92 static void fimdfreq_stop_ppmu(struct s5pv310_ppmu_hw *ppmu)
94 s5pv310_ppmu_stop(ppmu);
95 s5pv310_ppmu_update(ppmu);
98 static void fimdfreq_start_ppmu(struct s5pv310_ppmu_hw *ppmu)
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);
106 static unsigned int fimdfreq_monitor_ppmu(struct fimdfreq_platdata *pdata)
108 struct s5pv310_ppmu_hw *ppmu = &pdata->ppmu;
109 unsigned int ppmu_load = 0;
111 fimdfreq_stop_ppmu(ppmu);
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;
119 fimdfreq_start_ppmu(ppmu);
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
130 static BLOCKING_NOTIFIER_HEAD(fimdfreq_notifier_client_list);
132 int fimdfreq_register_display(struct notifier_block *nb)
134 return blocking_notifier_chain_register(
135 &fimdfreq_notifier_client_list, nb);
137 EXPORT_SYMBOL(fimdfreq_register_display);
139 int fimdfreq_unregister_display(struct notifier_block *nb)
141 return blocking_notifier_chain_unregister(
142 &fimdfreq_notifier_client_list, nb);
144 EXPORT_SYMBOL(fimdfreq_unregister_display);
146 static int fimdfreq_send_request_to_display(unsigned long val, void *v)
148 return blocking_notifier_call_chain(
149 &fimdfreq_notifier_client_list, val, v);
153 * Routines for Report(HF/LF) transition notification to FIMDFREQ
154 * - Routine : From Notification receiver To FIMDFREQ
161 static BLOCKING_NOTIFIER_HEAD(fimdfreq_notifier_receiver_list);
163 static int fimdfreq_register_receiver(struct notifier_block *nb)
165 return blocking_notifier_chain_register(
166 &fimdfreq_notifier_receiver_list, nb);
169 static int fimdfreq_unregister_receiver(struct notifier_block *nb)
171 return blocking_notifier_chain_unregister(
172 &fimdfreq_notifier_receiver_list, nb);
175 int fimdfreq_send_request_from_modules(unsigned long val, void *v)
177 return blocking_notifier_call_chain(
178 &fimdfreq_notifier_receiver_list, val, v);
180 EXPORT_SYMBOL(fimdfreq_send_request_from_modules);
183 * Enable/Disable FIMD frequency scaling
185 static int fimdfreq_disable(struct fimdfreq_platdata *pdata, bool wq_off)
188 /* Cancel workqueue of FIMD PPMU */
189 cancel_delayed_work(&pdata->work_ppmu);
193 fimdfreq_stop_ppmu(&pdata->ppmu);
195 /* Disable clock of FIMD PPMU */
196 pdata->ppmu_clk = clk_get(NULL, "ppmu_fimd0");
197 if (IS_ERR(pdata->ppmu_clk))
199 clk_disable(pdata->ppmu_clk);
200 clk_put(pdata->ppmu_clk);
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);
207 /* Set default state of display */
208 fimdfreq_send_request_to_display(FIMDFREQ_HF, NULL);
210 /* Lastly, turn off LCD0 power domain */
211 if (!pm_runtime_suspended(&pd_lcd0_pdev->dev))
212 pm_runtime_put(&pd_lcd0_pdev->dev);
214 pdata->curr_state = FIMDFREQ_OFF;
215 pdata->prev_level = FIMDFREQ_DEFAULT;
216 pdata->curr_level = FIMDFREQ_DEFAULT;
221 static int fimdfreq_enable(struct fimdfreq_platdata *pdata, bool wq_off)
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);
227 /* Enable clock of FIMD PPMU */
228 pdata->ppmu_clk = clk_get(NULL, "ppmu_fimd0");
229 if (IS_ERR(pdata->ppmu_clk))
231 clk_enable(pdata->ppmu_clk);
232 clk_put(pdata->ppmu_clk);
234 /* Start FIMD PPMU */
235 fimdfreq_start_ppmu(&pdata->ppmu);
238 /* Schedule workqueue of FIMD PPMU */
239 schedule_delayed_work(&pdata->work_ppmu,
240 msecs_to_jiffies(pdata->period));
243 pdata->curr_state = FIMDFREQ_ON;
244 pdata->prev_level = FIMDFREQ_DEFAULT;
245 pdata->curr_level = FIMDFREQ_DEFAULT;
251 * Governor for FIMD Frequency scaling
253 static int fimdfreq_governor(struct fimdfreq_platdata *pdata,
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.
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.
266 /* Inform display client of changed frequency level */
267 /* fimdfreq_send_report_to_display(FIMDFREQ_HF, NULL); */
270 return FIMDFREQ_DEFAULT;
274 * Timer to monitor FIMD PPMU
276 static void fimdfreq_ppmu_timer(struct work_struct *work)
278 struct fimdfreq_platdata *pdata =
279 container_of(work, struct fimdfreq_platdata, work_ppmu.work);
280 unsigned int period = pdata->period;
284 if (pdata->curr_state == FIMDFREQ_ON) {
285 /* Check the value of FIMD PPMU */
286 load = fimdfreq_monitor_ppmu(pdata);
288 /* Decide FIMD frequency level by using the value FIMD PPMU */
289 level = fimdfreq_governor(pdata, load);
291 printk(KERN_DEBUG "FIMDFREQ - Load:%d\t/ Level:%d\n",
295 /* Restart delayed workqueue to check FIMD PPMU periodically */
296 schedule_delayed_work(&pdata->work_ppmu, msecs_to_jiffies(period));
302 * Timer to set display of low frequency after 1 second
304 static void fimdfreq_set_lowfreq(struct work_struct *work)
306 struct fimdfreq_platdata *pdata =
307 container_of(work, struct fimdfreq_platdata, work_lowfreq.work);
309 /* Inform display client of changed frequency level */
310 fimdfreq_send_request_to_display(FIMDFREQ_LF, NULL);
312 pdata->prev_level = pdata->curr_level;
313 pdata->curr_level = FIMDFREQ_LF;
315 printk(KERN_DEBUG "FIMDFREQ - Notify client of Low Frequency: %d\n",
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.
326 static int fimdfreq_notifier_callback(struct notifier_block *this,
327 unsigned long event, void *data)
329 struct fimdfreq_platdata *pdata
330 = container_of(this, struct fimdfreq_platdata, nb_recv);
332 if (pdata->curr_state == FIMDFREQ_OFF)
336 case FIMDFREQ_HF_FORCED:
338 if(delayed_work_pending(&pdata->work_lowfreq))
339 cancel_delayed_work(&pdata->work_lowfreq);
341 if (pdata->curr_level == event)
344 pdata->prev_level = pdata->curr_level;
345 pdata->curr_level = (enum fimdfreq_level)event;
347 /* Inform display client of changed frequency level */
348 fimdfreq_send_request_to_display(event, NULL);
351 schedule_delayed_work(&pdata->work_lowfreq,
352 msecs_to_jiffies(DEFAULT_DELAY_TIME));
354 case FIMDFREQ_DEFAULT:
362 printk(KERN_DEBUG "FIMDFREQ - Notify client of High Frequency: %ld\n",
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
373 static ssize_t show_fimdfreq_level(struct device *dev,
374 struct device_attribute *attr,
377 struct fimdfreq_platdata *pdata = dev->platform_data;
378 return sprintf(buf, "%u\n", pdata->curr_level);
381 static ssize_t show_fimdfreq_state(struct device *dev,
382 struct device_attribute *attr,
385 struct fimdfreq_platdata *pdata = dev->platform_data;
386 return sprintf(buf, "%u\n", pdata->curr_state);
389 static ssize_t store_fimdfreq_state(struct device *dev,
390 struct device_attribute *attr,
393 struct fimdfreq_platdata *pdata = dev->platform_data;
397 ret = sscanf(buf, "%u", &state);
401 if (pdata->curr_state == state)
404 if (state == FIMDFREQ_ON)
405 fimdfreq_enable(pdata, true);
407 fimdfreq_disable(pdata, true);
410 return sprintf(buf, "%u\n", pdata->curr_state);
413 static ssize_t show_fimdfreq_period(struct device *dev,
414 struct device_attribute *attr,
417 struct fimdfreq_platdata *pdata = dev->platform_data;
418 return sprintf(buf, "%ld", pdata->period);
420 static ssize_t store_fimdfreq_period(struct device *dev,
421 struct device_attribute *attr,
422 const char *buf, size_t count)
424 struct fimdfreq_platdata *pdata = dev->platform_data;
425 unsigned long period;
428 ret = sscanf(buf, "%ld", &period);
432 if (pdata->period == period)
435 if (pdata->curr_state == FIMDFREQ_OFF)
436 pdata->period = period;
438 fimdfreq_disable(pdata, true);
439 pdata->period = period;
440 fimdfreq_enable(pdata, true);
445 return sprintf(buf, "%ld\n", pdata->period);
447 FIMDFREQ_DEVICE_ATTR_RO(level);
448 FIMDFREQ_DEVICE_ATTR_RW(state);
449 FIMDFREQ_DEVICE_ATTR_RW(period);
451 static int fimdfreq_create_sysfs(struct platform_device *pdev)
455 ret = device_create_file(&pdev->dev, &dev_attr_level);
457 pr_warn("Failed to register attributes: %d\n", ret);
461 ret = device_create_file(&pdev->dev, &dev_attr_state);
463 pr_warn("Failed to register attributes: %d\n", ret);
467 ret = device_create_file(&pdev->dev, &dev_attr_period);
469 pr_warn("Failed to register attributes: %d\n", ret);
476 device_remove_file(&pdev->dev, &dev_attr_period);
478 device_remove_file(&pdev->dev, &dev_attr_state);
480 device_remove_file(&pdev->dev, &dev_attr_level);
486 * Register FIMD Frequency scaling as client to fb notifer
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.
492 static int fb_notifier_callback(struct notifier_block *this,
493 unsigned long event, void *data)
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;
501 case FB_BLANK_UNBLANK:
502 if (pdata->curr_state == FIMDFREQ_OFF)
503 fimdfreq_enable(pdata, false);
505 case FB_BLANK_POWERDOWN:
506 if (pdata->curr_state == FIMDFREQ_ON)
507 fimdfreq_disable(pdata, false);
517 * FIMD Frequency scaling
519 static int fimdfreq_probe(struct platform_device *pdev)
521 struct fimdfreq_platdata *pdata;
524 pdata = pdev->dev.platform_data;
526 pr_err("Fail to get platform data of fimdfreq\n");
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;
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");
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);
547 pr_err("Failed to get ppmu_fimd0 clock: %d\n", ret);
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);
555 pr_err("Failed to get ppmu_fimd0 clock: %d\n", ret);
559 ret = fimdfreq_create_sysfs(pdev);
561 pr_err("Failed to create sysfs: %d\n", ret);
565 pm_runtime_get_sync(&pd_lcd0_pdev->dev);
566 clk_enable(pdata->ppmu_clk);
567 fimdfreq_start_ppmu(&pdata->ppmu);
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));
578 fimdfreq_unregister_receiver(&pdata->nb_recv);
580 fb_unregister_client(&pdata->nb_fb);
582 clk_disable(pdata->ppmu_clk);
583 clk_put(pdata->ppmu_clk);
590 static int __devexit fimdfreq_remove(struct platform_device *pdev)
592 struct fimdfreq_platdata *pdata = pdev->dev.platform_data;
594 fimdfreq_unregister_receiver(&pdata->nb_recv);
595 fb_unregister_client(&pdata->nb_fb);
597 fimdfreq_disable(pdata, true);
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);
609 static int fimdfreq_suspend(struct device *dev)
611 struct fimdfreq_platdata *pdata = dev->platform_data;
613 if (pdata->curr_state == FIMDFREQ_OFF)
616 return fimdfreq_disable(pdata, true);
619 static int fimdfreq_resume(struct device *dev)
621 struct fimdfreq_platdata *pdata = dev->platform_data;
623 if (pdata->curr_state == FIMDFREQ_OFF)
626 return fimdfreq_enable(pdata, true);
629 static const struct dev_pm_ops fimdfreq_dev_pm_ops = {
630 .suspend = fimdfreq_suspend,
631 .resume = fimdfreq_resume,
634 #define FIMDFREQ_DEV_PM_OPS (&fimdfreq_dev_pm_ops)
636 #define FIMDFREQ_DEV_PM_OPS NULL
637 #endif /* CONFIG_PM */
639 static struct fimdfreq_platdata fimdfreq_pdata;
640 static struct platform_device fimdfreq_pdev = {
644 .platform_data = &fimdfreq_pdata,
648 static struct platform_driver fimdfreq_driver = {
651 .owner = THIS_MODULE,
652 .pm = FIMDFREQ_DEV_PM_OPS,
654 .probe = fimdfreq_probe,
655 .remove = __devexit_p(fimdfreq_remove),
658 static int __init fimdfreq_init(void)
662 ret = platform_device_register(&fimdfreq_pdev);
664 pr_err("Failed to register dummy register device: %d\n", ret);
668 ret = platform_driver_register(&fimdfreq_driver);
670 pr_err("Failed to register FIMD frequency scaling driver: %d\n",
678 platform_device_unregister(&fimdfreq_pdev);
682 static void __exit fimdfreq_exit(void)
684 platform_driver_unregister(&fimdfreq_driver);
685 platform_device_unregister(&fimdfreq_pdev);
688 module_init(fimdfreq_init);
689 module_exit(fimdfreq_exit);
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");