~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/video/backlight/atmel-pwm-bl.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2008 Atmel Corporation
 
3
 *
 
4
 * Backlight driver using Atmel PWM peripheral.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify it
 
7
 * under the terms of the GNU General Public License version 2 as published by
 
8
 * the Free Software Foundation.
 
9
 */
 
10
#include <linux/init.h>
 
11
#include <linux/kernel.h>
 
12
#include <linux/module.h>
 
13
#include <linux/platform_device.h>
 
14
#include <linux/fb.h>
 
15
#include <linux/clk.h>
 
16
#include <linux/gpio.h>
 
17
#include <linux/backlight.h>
 
18
#include <linux/atmel_pwm.h>
 
19
#include <linux/atmel-pwm-bl.h>
 
20
#include <linux/slab.h>
 
21
 
 
22
struct atmel_pwm_bl {
 
23
        const struct atmel_pwm_bl_platform_data *pdata;
 
24
        struct backlight_device                 *bldev;
 
25
        struct platform_device                  *pdev;
 
26
        struct pwm_channel                      pwmc;
 
27
        int                                     gpio_on;
 
28
};
 
29
 
 
30
static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
 
31
{
 
32
        struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
 
33
        int intensity = bd->props.brightness;
 
34
        int pwm_duty;
 
35
 
 
36
        if (bd->props.power != FB_BLANK_UNBLANK)
 
37
                intensity = 0;
 
38
        if (bd->props.fb_blank != FB_BLANK_UNBLANK)
 
39
                intensity = 0;
 
40
 
 
41
        if (pwmbl->pdata->pwm_active_low)
 
42
                pwm_duty = pwmbl->pdata->pwm_duty_min + intensity;
 
43
        else
 
44
                pwm_duty = pwmbl->pdata->pwm_duty_max - intensity;
 
45
 
 
46
        if (pwm_duty > pwmbl->pdata->pwm_duty_max)
 
47
                pwm_duty = pwmbl->pdata->pwm_duty_max;
 
48
        if (pwm_duty < pwmbl->pdata->pwm_duty_min)
 
49
                pwm_duty = pwmbl->pdata->pwm_duty_min;
 
50
 
 
51
        if (!intensity) {
 
52
                if (pwmbl->gpio_on != -1) {
 
53
                        gpio_set_value(pwmbl->gpio_on,
 
54
                                        0 ^ pwmbl->pdata->on_active_low);
 
55
                }
 
56
                pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
 
57
                pwm_channel_disable(&pwmbl->pwmc);
 
58
        } else {
 
59
                pwm_channel_enable(&pwmbl->pwmc);
 
60
                pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
 
61
                if (pwmbl->gpio_on != -1) {
 
62
                        gpio_set_value(pwmbl->gpio_on,
 
63
                                        1 ^ pwmbl->pdata->on_active_low);
 
64
                }
 
65
        }
 
66
 
 
67
        return 0;
 
68
}
 
69
 
 
70
static int atmel_pwm_bl_get_intensity(struct backlight_device *bd)
 
71
{
 
72
        struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
 
73
        u8 intensity;
 
74
 
 
75
        if (pwmbl->pdata->pwm_active_low) {
 
76
                intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) -
 
77
                        pwmbl->pdata->pwm_duty_min;
 
78
        } else {
 
79
                intensity = pwmbl->pdata->pwm_duty_max -
 
80
                        pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY);
 
81
        }
 
82
 
 
83
        return intensity;
 
84
}
 
85
 
 
86
static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
 
87
{
 
88
        unsigned long pwm_rate = pwmbl->pwmc.mck;
 
89
        unsigned long prescale = DIV_ROUND_UP(pwm_rate,
 
90
                        (pwmbl->pdata->pwm_frequency *
 
91
                         pwmbl->pdata->pwm_compare_max)) - 1;
 
92
 
 
93
        /*
 
94
         * Prescale must be power of two and maximum 0xf in size because of
 
95
         * hardware limit. PWM speed will be:
 
96
         *      PWM module clock speed / (2 ^ prescale).
 
97
         */
 
98
        prescale = fls(prescale);
 
99
        if (prescale > 0xf)
 
100
                prescale = 0xf;
 
101
 
 
102
        pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale);
 
103
        pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY,
 
104
                        pwmbl->pdata->pwm_duty_min +
 
105
                        pwmbl->bldev->props.brightness);
 
106
        pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD,
 
107
                        pwmbl->pdata->pwm_compare_max);
 
108
 
 
109
        dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver "
 
110
                        "(%lu Hz)\n", pwmbl->pwmc.mck /
 
111
                        pwmbl->pdata->pwm_compare_max /
 
112
                        (1 << prescale));
 
113
 
 
114
        return pwm_channel_enable(&pwmbl->pwmc);
 
115
}
 
116
 
 
117
static const struct backlight_ops atmel_pwm_bl_ops = {
 
118
        .get_brightness = atmel_pwm_bl_get_intensity,
 
119
        .update_status  = atmel_pwm_bl_set_intensity,
 
120
};
 
121
 
 
122
static int atmel_pwm_bl_probe(struct platform_device *pdev)
 
123
{
 
124
        struct backlight_properties props;
 
125
        const struct atmel_pwm_bl_platform_data *pdata;
 
126
        struct backlight_device *bldev;
 
127
        struct atmel_pwm_bl *pwmbl;
 
128
        int retval;
 
129
 
 
130
        pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL);
 
131
        if (!pwmbl)
 
132
                return -ENOMEM;
 
133
 
 
134
        pwmbl->pdev = pdev;
 
135
 
 
136
        pdata = pdev->dev.platform_data;
 
137
        if (!pdata) {
 
138
                retval = -ENODEV;
 
139
                goto err_free_mem;
 
140
        }
 
141
 
 
142
        if (pdata->pwm_compare_max < pdata->pwm_duty_max ||
 
143
                        pdata->pwm_duty_min > pdata->pwm_duty_max ||
 
144
                        pdata->pwm_frequency == 0) {
 
145
                retval = -EINVAL;
 
146
                goto err_free_mem;
 
147
        }
 
148
 
 
149
        pwmbl->pdata = pdata;
 
150
        pwmbl->gpio_on = pdata->gpio_on;
 
151
 
 
152
        retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc);
 
153
        if (retval)
 
154
                goto err_free_mem;
 
155
 
 
156
        if (pwmbl->gpio_on != -1) {
 
157
                retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl");
 
158
                if (retval) {
 
159
                        pwmbl->gpio_on = -1;
 
160
                        goto err_free_pwm;
 
161
                }
 
162
 
 
163
                /* Turn display off by default. */
 
164
                retval = gpio_direction_output(pwmbl->gpio_on,
 
165
                                0 ^ pdata->on_active_low);
 
166
                if (retval)
 
167
                        goto err_free_gpio;
 
168
        }
 
169
 
 
170
        memset(&props, 0, sizeof(struct backlight_properties));
 
171
        props.type = BACKLIGHT_RAW;
 
172
        props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min;
 
173
        bldev = backlight_device_register("atmel-pwm-bl", &pdev->dev, pwmbl,
 
174
                                          &atmel_pwm_bl_ops, &props);
 
175
        if (IS_ERR(bldev)) {
 
176
                retval = PTR_ERR(bldev);
 
177
                goto err_free_gpio;
 
178
        }
 
179
 
 
180
        pwmbl->bldev = bldev;
 
181
 
 
182
        platform_set_drvdata(pdev, pwmbl);
 
183
 
 
184
        /* Power up the backlight by default at middle intesity. */
 
185
        bldev->props.power = FB_BLANK_UNBLANK;
 
186
        bldev->props.brightness = bldev->props.max_brightness / 2;
 
187
 
 
188
        retval = atmel_pwm_bl_init_pwm(pwmbl);
 
189
        if (retval)
 
190
                goto err_free_bl_dev;
 
191
 
 
192
        atmel_pwm_bl_set_intensity(bldev);
 
193
 
 
194
        return 0;
 
195
 
 
196
err_free_bl_dev:
 
197
        platform_set_drvdata(pdev, NULL);
 
198
        backlight_device_unregister(bldev);
 
199
err_free_gpio:
 
200
        if (pwmbl->gpio_on != -1)
 
201
                gpio_free(pwmbl->gpio_on);
 
202
err_free_pwm:
 
203
        pwm_channel_free(&pwmbl->pwmc);
 
204
err_free_mem:
 
205
        kfree(pwmbl);
 
206
        return retval;
 
207
}
 
208
 
 
209
static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
 
210
{
 
211
        struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
 
212
 
 
213
        if (pwmbl->gpio_on != -1) {
 
214
                gpio_set_value(pwmbl->gpio_on, 0);
 
215
                gpio_free(pwmbl->gpio_on);
 
216
        }
 
217
        pwm_channel_disable(&pwmbl->pwmc);
 
218
        pwm_channel_free(&pwmbl->pwmc);
 
219
        backlight_device_unregister(pwmbl->bldev);
 
220
        platform_set_drvdata(pdev, NULL);
 
221
        kfree(pwmbl);
 
222
 
 
223
        return 0;
 
224
}
 
225
 
 
226
static struct platform_driver atmel_pwm_bl_driver = {
 
227
        .driver = {
 
228
                .name = "atmel-pwm-bl",
 
229
        },
 
230
        /* REVISIT add suspend() and resume() */
 
231
        .remove = __exit_p(atmel_pwm_bl_remove),
 
232
};
 
233
 
 
234
static int __init atmel_pwm_bl_init(void)
 
235
{
 
236
        return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe);
 
237
}
 
238
module_init(atmel_pwm_bl_init);
 
239
 
 
240
static void __exit atmel_pwm_bl_exit(void)
 
241
{
 
242
        platform_driver_unregister(&atmel_pwm_bl_driver);
 
243
}
 
244
module_exit(atmel_pwm_bl_exit);
 
245
 
 
246
MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
 
247
MODULE_DESCRIPTION("Atmel PWM backlight driver");
 
248
MODULE_LICENSE("GPL");