/******************************************************************************
* am335x backlight
* 本文主要分析TI的am335x处理器,backlight注册过程。
*
* Tony Liu, 2016-4-21, Shenzhen
*******************************************************************************/
kernel/arcm/arm/omap2/board-am335xevm.c
static int __init backlight_init(void)
{
int index = 0;
#if defined(CONFIG_OK335XD)
index = 0;
am335x_backlight.dev.platform_data = &am335x_backlight_data0; ------+
#elif defined(CONFIG_OK335XS) |
index = 2; |
am335x_backlight.dev.platform_data = &am335x_backlight_data2; |
#endif |
|
am33xx_register_ecap(index, &pwm_pdata[index]); -------------------|----+
platform_device_register(&am335x_backlight); | |
| | |
return 0; | | |
} +---------------------------------|-+ |
late_initcall(backlight_init); | | |
| | |
| | |
static struct platform_pwm_backlight_data am335x_backlight_data0 = { <--+ | |
.pwm_id = "ecap.0", | |
.ch = -1, | |
.lth_brightness = 21, | |
.max_brightness = AM335X_BACKLIGHT_MAX_BRIGHTNESS, | |
.dft_brightness = AM335X_BACKLIGHT_DEFAULT_BRIGHTNESS, | |
.pwm_period_ns = AM335X_PWM_PERIOD_NANO_SECONDS, | |
}; | |
| |
#define AM335X_BACKLIGHT_MAX_BRIGHTNESS 100 | |
#define AM335X_BACKLIGHT_DEFAULT_BRIGHTNESS 60 | |
| |
#define AM335X_PWM_PERIOD_NANO_SECONDS (5000 * 10 * 100) | |
| |
static struct platform_device am335x_backlight = { <---------------+ |
.name = "pwm-backlight", |
.id = -1, |
}; |
|
#define PWM_STR_LEN 10 |
int __init am33xx_register_ecap(int id, struct pwmss_platform_data *pdata) <-+
{
struct platform_device *pdev;
struct omap_hwmod *oh;
char *oh_name = "ecap";
char dev_name[PWM_STR_LEN];
sprintf(dev_name, "ecap.%d", id);
//查找链表中是否有同名的设备的寄存器信息
oh = omap_hwmod_lookup(dev_name); -------------------+
if (!oh) { |
pr_err("Could not look up %s hwmod\n", dev_name); |
return -ENODEV; |
} |
//注册设备 |
pdev = omap_device_build(oh_name, id, oh, pdata, ----------|---+
sizeof(*pdata), NULL, 0, 0); | |
| |
if (IS_ERR(pdev)) { | |
WARN(1, "Can't build omap_device for %s:%s.\n", | |
dev_name, oh->name); | |
return PTR_ERR(pdev); | |
} | |
return 0; | |
} | |
//查找设备注册时的链表中是否有设备 | |
struct omap_hwmod *omap_hwmod_lookup(const char *name) <-------+ |
{ |
struct omap_hwmod *oh; |
|
if (!name) |
return NULL; |
|
oh = _lookup(name); ----+ |
| |
return oh; | |
} | |
V |
static struct omap_hwmod *_lookup(const char *name) |
{ |
struct omap_hwmod *oh, *temp_oh; |
|
oh = NULL; |
//查找 |
list_for_each_entry(temp_oh, &omap_hwmod_list, node) { |
if (!strcmp(name, temp_oh->name)) { |
oh = temp_oh; |
break; |
} |
} +-----------------------------------------+
|
return oh; |
} |
V
struct platform_device *omap_device_build(const char *pdev_name, int pdev_id,
struct omap_hwmod *oh, void *pdata,
int pdata_len,
struct omap_device_pm_latency *pm_lats,
int pm_lats_cnt, int is_early_device)
{
struct omap_hwmod *ohs[] = { oh };
if (!oh)
return ERR_PTR(-EINVAL);
return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,
pdata_len, pm_lats, pm_lats_cnt,
is_early_device);
} |
V
struct platform_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
struct omap_hwmod **ohs, int oh_cnt,
void *pdata, int pdata_len,
struct omap_device_pm_latency *pm_lats,
int pm_lats_cnt, int is_early_device)
{
int ret = -ENOMEM;
struct platform_device *pdev;
struct omap_device *od;
if (!ohs || oh_cnt == 0 || !pdev_name)
return ERR_PTR(-EINVAL);
if (!pdata && pdata_len > 0)
return ERR_PTR(-EINVAL);
pdev = platform_device_alloc(pdev_name, pdev_id);
if (!pdev) {
ret = -ENOMEM;
goto odbs_exit;
}
/* Set the dev_name early to allow dev_xxx in omap_device_alloc */
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, "%s", pdev->name);
od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt);
if (!od)
goto odbs_exit1;
ret = platform_device_add_data(pdev, pdata, pdata_len);
if (ret)
goto odbs_exit2;
if (is_early_device)
ret = omap_early_device_register(pdev);
else
ret = omap_device_register(pdev);
if (ret)
goto odbs_exit2;
return pdev;
odbs_exit2:
omap_device_delete(od);
odbs_exit1:
platform_device_put(pdev);
odbs_exit:
pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);
return ERR_PTR(ret);
}
//驱动注册
kernel/driver/video/backlight/pwm_bl.c
static int __init pwm_backlight_init(void)
{
return platform_driver_register(&pwm_backlight_driver);
} |
V
static struct platform_driver pwm_backlight_driver = {
.driver = {
.name = "pwm-backlight",
.owner = THIS_MODULE,
},
.probe = pwm_backlight_probe, --------------+
.remove = pwm_backlight_remove, |
.suspend = pwm_backlight_suspend, |
.resume = pwm_backlight_resume, |
}; |
|
static int pwm_backlight_probe(struct platform_device *pdev) <---+
{
struct backlight_properties props;
struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
struct backlight_device *bl;
struct pwm_bl_data *pb;
int ret;
if (!data) {
dev_err(&pdev->dev, "failed to find platform data\n");
return -EINVAL;
}
if (data->init) {
ret = data->init(&pdev->dev);
if (ret < 0)
return ret;
}
pb = kzalloc(sizeof(*pb), GFP_KERNEL);
if (!pb) {
dev_err(&pdev->dev, "no memory for state\n");
ret = -ENOMEM;
goto err_alloc;
}
pb->period = data->pwm_period_ns;
pb->notify = data->notify;
pb->notify_after = data->notify_after;
pb->check_fb = data->check_fb;
pb->lth_brightness = data->lth_brightness *
(data->pwm_period_ns / data->max_brightness);
pb->dev = &pdev->dev;
pb->pwm = pwm_request(data->pwm_id, data->ch, "backlight");
if (IS_ERR(pb->pwm)) {
dev_err(&pdev->dev, "unable to request PWM for backlight\n");
ret = PTR_ERR(pb->pwm);
goto err_pwm;
} else
dev_dbg(&pdev->dev, "got pwm for backlight\n");
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = data->max_brightness;
bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, ---+
&pwm_backlight_ops, &props); |
----------------------------------------------|--+
if (IS_ERR(bl)) { | |
dev_err(&pdev->dev, "failed to register backlight\n"); | |
ret = PTR_ERR(bl); | |
goto err_bl; | |
} | |
| |
bl->props.brightness = data->dft_brightness; | |
backlight_update_status(bl); | |
| |
platform_set_drvdata(pdev, bl); | |
return 0; | |
| |
err_bl: | |
pwm_release(pb->pwm); | |
err_pwm: | |
kfree(pb); | |
err_alloc: | |
if (data->exit) | |
data->exit(&pdev->dev); | |
return ret; | |
} | |
| |
struct backlight_device *backlight_device_register(const char *name, <-----+ |
struct device *parent, void *devdata, const struct backlight_ops *ops, |
const struct backlight_properties *props) |
{ |
struct backlight_device *new_bd; |
int rc; |
|
pr_debug("backlight_device_register: name=%s\n", name); |
|
new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); |
if (!new_bd) |
return ERR_PTR(-ENOMEM); |
|
mutex_init(&new_bd->update_lock); |
mutex_init(&new_bd->ops_lock); |
|
new_bd->dev.class = backlight_class; |
new_bd->dev.parent = parent; |
new_bd->dev.release = bl_device_release; |
dev_set_name(&new_bd->dev, name); |
dev_set_drvdata(&new_bd->dev, devdata); |
|
/* Set default properties */ |
if (props) { |
memcpy(&new_bd->props, props, |
sizeof(struct backlight_properties)); |
if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) { |
WARN(1, "%s: invalid backlight type", name); |
new_bd->props.type = BACKLIGHT_RAW; |
} |
} else { |
new_bd->props.type = BACKLIGHT_RAW; |
} |
|
rc = device_register(&new_bd->dev); |
if (rc) { |
kfree(new_bd); |
return ERR_PTR(rc); |
} |
|
rc = backlight_register_fb(new_bd); |
if (rc) { |
device_unregister(&new_bd->dev); |
return ERR_PTR(rc); |
} |
|
new_bd->ops = ops; |
|
#ifdef CONFIG_PMAC_BACKLIGHT |
mutex_lock(&pmac_backlight_mutex); |
if (!pmac_backlight) |
pmac_backlight = new_bd; |
mutex_unlock(&pmac_backlight_mutex); |
#endif |
|
return new_bd; |
} |
|
static const struct backlight_ops pwm_backlight_ops = { <---------------+
.update_status = pwm_backlight_update_status, -----------+
.get_brightness = pwm_backlight_get_brightness, |
.check_fb = pwm_backlight_check_fb, |
}; |
//每次设置pwm都会调用下面的函数 |
static int pwm_backlight_update_status(struct backlight_device *bl) <-----+
{
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
int brightness = bl->props.brightness;
int max = bl->props.max_brightness;
if (bl->props.power != FB_BLANK_UNBLANK)
brightness = 0;
if (bl->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0;
if (pb->notify)
brightness = pb->notify(pb->dev, brightness);
if (brightness == 0) {
pwm_set_duty_ns(pb->pwm, 0);
pwm_stop(pb->pwm);
} else {
brightness = pb->lth_brightness +
(brightness * (pb->period - pb->lth_brightness) / max);
pwm_set_period_ns(pb->pwm, pb->period);
pwm_set_duty_ns(pb->pwm, brightness);
pwm_start(pb->pwm);
}
if (pb->notify_after)
pb->notify_after(pb->dev, brightness);
return 0;
}