am335x backlight
/****************************************************************************** * 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; }