diff options
-rw-r--r-- | arch/arm/mach-mxs/include/mach/imx28-regs.h | 1 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 1 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/core.c | 7 | ||||
-rw-r--r-- | drivers/pwm/pwm-mxs.c | 174 | ||||
-rw-r--r-- | drivers/video/Kconfig | 12 | ||||
-rw-r--r-- | drivers/video/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight-pwm.c | 199 | ||||
-rw-r--r-- | drivers/video/backlight.c | 90 | ||||
-rw-r--r-- | drivers/video/stm.c | 52 | ||||
-rw-r--r-- | include/pwm.h | 2 | ||||
-rw-r--r-- | include/video/backlight.h | 20 |
13 files changed, 554 insertions, 13 deletions
diff --git a/arch/arm/mach-mxs/include/mach/imx28-regs.h b/arch/arm/mach-mxs/include/mach/imx28-regs.h index de0d882414..1a90ec2aa5 100644 --- a/arch/arm/mach-mxs/include/mach/imx28-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx28-regs.h @@ -34,6 +34,7 @@ #define IMX_WDT_BASE 0x80056000 #define IMX_I2C0_BASE 0x80058000 #define IMX_I2C1_BASE 0x8005a000 +#define IMX_PWM_BASE 0x80064000 #define IMX_TIM1_BASE 0x80068000 #define IMX_UART0_BASE 0x8006a000 #define IMX_UART1_BASE 0x8006c000 diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 77a13bc7ac..a408044264 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -158,6 +158,7 @@ int __init mx28_clocks_init(void __iomem *regs) clkdev_add_physbase(clks[uart], IMX_UART3_BASE, NULL); clkdev_add_physbase(clks[uart], IMX_UART4_BASE, NULL); clkdev_add_physbase(clks[gpmi], MXS_GPMI_BASE, NULL); + clkdev_add_physbase(clks[pwm], IMX_PWM_BASE, NULL); if (IS_ENABLED(CONFIG_DRIVER_VIDEO_STM)) clkdev_add_physbase(clks[lcdif_comp], IMX_FB_BASE, NULL); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 8324d5ef19..97c3deff10 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -22,4 +22,10 @@ config PWM_IMX help This enables PWM support for Freescale i.MX SoCs +config PWM_MXS + bool "i.MXs PWM Support" + depends on ARCH_MXS + help + This enables PWM support for Freescale i.MX23/i.MX28 SoCs + endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index dea9956c04..46865a24ee 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_PXA) += pxa_pwm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_MXS) += pwm-mxs.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index cc33dec363..360520195a 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -181,12 +181,13 @@ struct pwm_device *pwm_request(const char *devname) } EXPORT_SYMBOL_GPL(pwm_request); -static struct pwm_device *of_node_to_pwm_device(struct device_node *np) +static struct pwm_device *of_node_to_pwm_device(struct device_node *np, int id) { struct pwm_device *pwm; list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->hwdev && pwm->hwdev->device_node == np) + if (pwm->hwdev && pwm->hwdev->device_node == np && + pwm->chip->id == id) return pwm; } @@ -210,7 +211,7 @@ struct pwm_device *of_pwm_request(struct device_node *np, const char *con_id) return ERR_PTR(ret); } - pwm = of_node_to_pwm_device(args.np); + pwm = of_node_to_pwm_device(args.np, args.args[0]); if (IS_ERR(pwm)) { pr_debug("%s(): PWM chip not found\n", __func__); return pwm; diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c new file mode 100644 index 0000000000..e66744288b --- /dev/null +++ b/drivers/pwm/pwm-mxs.c @@ -0,0 +1,174 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <common.h> +#include <init.h> +#include <errno.h> +#include <io.h> +#include <pwm.h> +#include <stmp-device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <asm-generic/div64.h> + +#define SET 0x4 +#define CLR 0x8 +#define TOG 0xc + +#define PWM_CTRL 0x0 +#define PWM_ACTIVE0 0x10 +#define PWM_PERIOD0 0x20 +#define PERIOD_PERIOD(p) ((p) & 0xffff) +#define PERIOD_PERIOD_MAX 0x10000 +#define PERIOD_ACTIVE_HIGH (3 << 16) +#define PERIOD_INACTIVE_LOW (2 << 18) +#define PERIOD_CDIV(div) (((div) & 0x7) << 20) +#define PERIOD_CDIV_MAX 8 + +static const unsigned int cdiv[PERIOD_CDIV_MAX] = { + 1, 2, 4, 8, 16, 64, 256, 1024 +}; + +struct mxs_pwm; + +struct mxs_pwm_chip { + struct pwm_chip chip; + struct mxs_pwm *mxs; +}; + +struct mxs_pwm { + struct mxs_pwm_chip pwm[8]; + struct clk *clk; + void __iomem *base; +}; + +#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip) + +static int mxs_pwm_config(struct pwm_chip *chip, int duty_ns, int period_ns) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + int div = 0; + unsigned int period_cycles, duty_cycles; + unsigned long rate; + unsigned long long c; + + rate = clk_get_rate(mxs->mxs->clk); + while (1) { + c = rate / cdiv[div]; + c = c * period_ns; + do_div(c, 1000000000); + if (c < PERIOD_PERIOD_MAX) + break; + div++; + if (div >= PERIOD_CDIV_MAX) + return -EINVAL; + } + + period_cycles = c; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + writel(duty_cycles << 16, + mxs->mxs->base + PWM_ACTIVE0 + mxs->chip.id * 0x20); + writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH | + PERIOD_INACTIVE_LOW | PERIOD_CDIV(div), + mxs->mxs->base + PWM_PERIOD0 + mxs->chip.id * 0x20); + + return 0; +} + +static int mxs_pwm_enable(struct pwm_chip *chip) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + + writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + SET); + + return 0; +} + +static void mxs_pwm_disable(struct pwm_chip *chip) +{ + struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); + + writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + CLR); +} + +static struct pwm_ops mxs_pwm_ops = { + .config = mxs_pwm_config, + .enable = mxs_pwm_enable, + .disable = mxs_pwm_disable, +}; + +static int mxs_pwm_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct mxs_pwm *mxs; + int ret, i; + uint32_t npwm; + + mxs = xzalloc(sizeof(*mxs)); + + mxs->base = dev_request_mem_region(dev, 0); + if (IS_ERR(mxs->base)) + return PTR_ERR(mxs->base); + + mxs->clk = clk_get(dev, NULL); + if (IS_ERR(mxs->clk)) + return PTR_ERR(mxs->clk); + + clk_enable(mxs->clk); + + ret = stmp_reset_block(mxs->base, 0); + if (ret) + return ret; + + ret = of_property_read_u32(np, "fsl,pwm-number", &npwm); + if (ret < 0) { + dev_err(dev, "failed to get pwm number: %d\n", ret); + return ret; + } + + for (i = 0; i < npwm; i++) { + struct mxs_pwm_chip *mxspwm = &mxs->pwm[i]; + + mxspwm->chip.ops = &mxs_pwm_ops; + mxspwm->chip.devname = asprintf("pwm%d", i); + mxspwm->chip.id = i; + mxspwm->mxs = mxs; + + ret = pwmchip_add(&mxspwm->chip, dev); + if (ret < 0) { + dev_err(dev, "failed to add pwm chip %d\n", ret); + return ret; + } + } + + return 0; +} + +static const struct of_device_id mxs_pwm_dt_ids[] = { + { .compatible = "fsl,imx23-pwm", }, + { /* sentinel */ } +}; + +static struct driver_d mxs_pwm_driver = { + .name = "mxs-pwm", + .of_compatible = mxs_pwm_dt_ids, + .probe = mxs_pwm_probe, +}; + +coredevice_platform_driver(mxs_pwm_driver); + +MODULE_ALIAS("platform:mxs-pwm"); +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); +MODULE_DESCRIPTION("Freescale MXS PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f096a5456b..8e6ae99b95 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -93,4 +93,16 @@ config DRIVER_VIDEO_EDID This enabled support for reading and parsing EDID data from an attached monitor. +config DRIVER_VIDEO_BACKLIGHT + bool "Add backlight support" + help + Enable this for backlight support. + +config DRIVER_VIDEO_BACKLIGHT_PWM + bool "PWM backlight support" + depends on PWM + depends on DRIVER_VIDEO_BACKLIGHT + help + Enable this to get support for backlight devices driven by a PWM. + endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ae9f6e545e..76fad5c8e8 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_VIDEO) += fb.o obj-$(CONFIG_DRIVER_VIDEO_EDID) += edid.o obj-$(CONFIG_OFDEVICE) += of_display_timing.o +obj-$(CONFIG_DRIVER_VIDEO_BACKLIGHT) += backlight.o +obj-$(CONFIG_DRIVER_VIDEO_BACKLIGHT_PWM) += backlight-pwm.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o diff --git a/drivers/video/backlight-pwm.c b/drivers/video/backlight-pwm.c new file mode 100644 index 0000000000..91b148109b --- /dev/null +++ b/drivers/video/backlight-pwm.c @@ -0,0 +1,199 @@ +/* + * pwm backlight support for barebox + * + * (C) Copyright 2014 Sascha Hauer, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <common.h> +#include <malloc.h> +#include <init.h> +#include <video/backlight.h> +#include <pwm.h> +#include <linux/err.h> +#include <of.h> +#include <gpio.h> +#include <of_gpio.h> +#include <asm-generic/div64.h> + +struct pwm_backlight { + struct backlight_device backlight; + struct pwm_device *pwm; + uint32_t period; + unsigned int *levels; + int enable_gpio; + int enable_active_high; + int max_value; + int enabled; +}; + +static int backlight_pwm_enable(struct pwm_backlight *pwm_backlight) +{ + int ret; + + if (pwm_backlight->enabled) + return 0; + + ret = pwm_enable(pwm_backlight->pwm); + if (ret) + return ret; + + if (gpio_is_valid(pwm_backlight->enable_gpio)) { + gpio_direction_output(pwm_backlight->enable_gpio, + pwm_backlight->enable_active_high); + } + + pwm_backlight->enabled = 1; + + return 0; +} + +static int backlight_pwm_disable(struct pwm_backlight *pwm_backlight) +{ + if (!pwm_backlight->enabled) + return 0; + + if (gpio_is_valid(pwm_backlight->enable_gpio)) { + gpio_direction_output(pwm_backlight->enable_gpio, + !pwm_backlight->enable_active_high); + /* + * Only disable PWM when an enable gpio is present. + * The output of the PWM is undefined when the PWM + * is disabled. + */ + pwm_disable(pwm_backlight->pwm); + pwm_backlight->enabled = 0; + } + + return 0; +} + +static int backlight_pwm_set(struct backlight_device *backlight, + int brightness) +{ + struct pwm_backlight *pwm_backlight = container_of(backlight, + struct pwm_backlight, backlight); + unsigned long long duty = pwm_backlight->period; + unsigned int max = pwm_backlight->backlight.brightness_max; + + duty *= brightness; + do_div(duty, max); + + pwm_config(pwm_backlight->pwm, duty, pwm_backlight->period); + + if (brightness) + return backlight_pwm_enable(pwm_backlight); + else + return backlight_pwm_disable(pwm_backlight); +} + +static int pwm_backlight_parse_dt(struct device_d *dev, + struct pwm_backlight *pwm_backlight) +{ + struct device_node *node = dev->device_node; + struct property *prop; + int length; + u32 value; + int ret; + enum of_gpio_flags flags; + + if (!node) + return -ENODEV; + + /* determine the number of brightness levels */ + prop = of_find_property(node, "brightness-levels", &length); + if (!prop) + return -EINVAL; + + pwm_backlight->backlight.brightness_max = length / sizeof(u32); + + /* read brightness levels from DT property */ + if (pwm_backlight->backlight.brightness_max > 0) { + size_t size = sizeof(*pwm_backlight->levels) * + pwm_backlight->backlight.brightness_max; + + pwm_backlight->levels = xzalloc(size); + if (!pwm_backlight->levels) + return -ENOMEM; + + ret = of_property_read_u32_array(node, "brightness-levels", + pwm_backlight->levels, + pwm_backlight->backlight.brightness_max); + if (ret < 0) + return ret; + + ret = of_property_read_u32(node, "default-brightness-level", + &value); + if (ret < 0) + return ret; + + pwm_backlight->backlight.brightness_default = value; + pwm_backlight->backlight.brightness_max--; + } + + pwm_backlight->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0, &flags); + + if (gpio_is_valid(pwm_backlight->enable_gpio)) { + if (!(flags & OF_GPIO_ACTIVE_LOW)) + pwm_backlight->enable_active_high = 1; + } + + return 0; +} + +static int backlight_pwm_of_probe(struct device_d *dev) +{ + int ret; + struct pwm_backlight *pwm_backlight; + struct pwm_device *pwm; + + pwm = of_pwm_request(dev->device_node, NULL); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + pwm_backlight = xzalloc(sizeof(*pwm_backlight)); + pwm_backlight->pwm = pwm; + pwm_backlight->period = pwm_get_period(pwm); + + ret = pwm_backlight_parse_dt(dev, pwm_backlight); + if (ret) + return ret; + + pwm_backlight->period = pwm_get_period(pwm_backlight->pwm); + + pwm_backlight->backlight.brightness_set = backlight_pwm_set; + pwm_backlight->backlight.node = dev->device_node; + + ret = backlight_register(&pwm_backlight->backlight); + if (ret) + return ret; + + return 0; +} + +static struct of_device_id backlight_pwm_of_ids[] = { + { + .compatible = "pwm-backlight", + }, { + /* sentinel */ + } +}; + +static struct driver_d backlight_pwm_of_driver = { + .name = "pwm-backlight", + .probe = backlight_pwm_of_probe, + .of_compatible = DRV_OF_COMPAT(backlight_pwm_of_ids), +}; +device_platform_driver(backlight_pwm_of_driver); diff --git a/drivers/video/backlight.c b/drivers/video/backlight.c new file mode 100644 index 0000000000..ddde6f8523 --- /dev/null +++ b/drivers/video/backlight.c @@ -0,0 +1,90 @@ +#include <common.h> +#include <driver.h> +#include <linux/list.h> +#include <video/backlight.h> + +static LIST_HEAD(backlights); + +int backlight_set_brightness(struct backlight_device *bl, int brightness) +{ + int ret, step, i, num_steps; + + if (brightness > bl->brightness_max) + brightness = bl->brightness_max; + + if (brightness == bl->brightness_cur) + return 0; + + if (brightness > bl->brightness_cur) + step = 1; + else + step = -1; + + i = bl->brightness_cur; + + num_steps = abs(brightness - bl->brightness_cur); + + while (1) { + i += step; + + ret = bl->brightness_set(bl, i); + if (ret) + return ret; + + if (i == brightness) + break; + + udelay(100000 / num_steps); + } + + + bl->brightness_cur = bl->brightness = brightness; + + return ret; +} + +int backlight_set_brightness_default(struct backlight_device *bl) +{ + int ret; + + ret = backlight_set_brightness(bl, bl->brightness_default); + + return ret; +} + +static int backlight_brightness_set(struct param_d *p, void *priv) +{ + struct backlight_device *bl = priv; + + return backlight_set_brightness(bl, bl->brightness); +} + +int backlight_register(struct backlight_device *bl) +{ + int ret; + + sprintf(bl->dev.name, "backlight"); + bl->dev.id = DEVICE_ID_DYNAMIC; + + ret = register_device(&bl->dev); + if (ret) + return ret; + + dev_add_param_int(&bl->dev, "brightness", backlight_brightness_set, + NULL, &bl->brightness, "%d", bl); + + list_add_tail(&bl->list, &backlights); + + return ret; +} + +struct backlight_device *of_backlight_find(struct device_node *node) +{ + struct backlight_device *bl; + + list_for_each_entry(bl, &backlights, list) + if (bl->node == node) + return bl; + + return NULL; +} diff --git a/drivers/video/stm.c b/drivers/video/stm.c index 3c90c0dc3f..bf913f1ec6 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -317,7 +317,7 @@ static int stmfb_activate_var(struct fb_info *fb_info) size = calc_line_length(mode->xres, fb_info->bits_per_pixel) * mode->yres; - if (pdata->fixed_screen) { + if (pdata && pdata->fixed_screen) { if (pdata->fixed_screen_size < size) return -ENOMEM; fb_info->screen_base = pdata->fixed_screen; @@ -497,16 +497,37 @@ static int stmfb_probe(struct device_d *hw_dev) return PTR_ERR(fbi.clk); clk_enable(fbi.clk); + fbi.info.bits_per_pixel = 16; + /* add runtime video info */ - fbi.info.modes.modes = pdata->mode_list; - fbi.info.modes.num_modes = pdata->mode_cnt; - fbi.info.mode = &fbi.info.modes.modes[0]; - fbi.info.xres = fbi.info.mode->xres; - fbi.info.yres = fbi.info.mode->yres; - if (pdata->bits_per_pixel) - fbi.info.bits_per_pixel = pdata->bits_per_pixel; - else - fbi.info.bits_per_pixel = 16; + if (pdata) { + fbi.info.modes.modes = pdata->mode_list; + fbi.info.modes.num_modes = pdata->mode_cnt; + fbi.info.mode = &fbi.info.modes.modes[0]; + if (pdata->bits_per_pixel) + fbi.info.bits_per_pixel = pdata->bits_per_pixel; + } else { + struct display_timings *modes; + struct device_node *display; + + if (!IS_ENABLED(CONFIG_OFDEVICE) || !hw_dev->device_node) + return -EINVAL; + + display = of_parse_phandle(hw_dev->device_node, "display", 0); + if (!display) { + dev_err(hw_dev, "no display phandle\n"); + return -EINVAL; + } + + modes = of_get_display_timings(display); + if (IS_ERR(modes)) { + dev_err(hw_dev, "unable to parse display timings\n"); + return PTR_ERR(modes); + } + + fbi.info.modes.modes = modes->modes; + fbi.info.modes.num_modes = modes->num_modes; + } hw_dev->info = stmfb_info; @@ -519,9 +540,20 @@ static int stmfb_probe(struct device_d *hw_dev) return 0; } +static __maybe_unused struct of_device_id stmfb_compatible[] = { + { + .compatible = "fsl,imx23-lcdif", + }, { + .compatible = "fsl,imx28-lcdif", + }, { + /* sentinel */ + } +}; + static struct driver_d stmfb_driver = { .name = "stmfb", .probe = stmfb_probe, + .of_compatible = DRV_OF_COMPAT(stmfb_compatible), }; device_platform_driver(stmfb_driver); diff --git a/include/pwm.h b/include/pwm.h index 59d86d497d..911c760839 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -57,12 +57,14 @@ struct pwm_ops { /** * struct pwm_chip - abstract a PWM + * @id: The id of this pwm * @devname: unique identifier for this pwm * @ops: The callbacks for this PWM * @duty_ns: The duty cycle of the PWM, in nano-seconds * @period_ns: The period of the PWM, in nano-seconds */ struct pwm_chip { + int id; const char *devname; struct pwm_ops *ops; int duty_ns; diff --git a/include/video/backlight.h b/include/video/backlight.h new file mode 100644 index 0000000000..56e0341ea4 --- /dev/null +++ b/include/video/backlight.h @@ -0,0 +1,20 @@ +#ifndef __VIDEO_BACKLIGHT_H +#define __VIDEO_BACKLIGHT_H + +struct backlight_device { + int brightness; + int brightness_cur; + int brightness_max; + int brightness_default; + int (*brightness_set)(struct backlight_device *, int brightness); + struct list_head list; + struct device_d dev; + struct device_node *node; +}; + +int backlight_set_brightness(struct backlight_device *, int brightness); +int backlight_set_brightness_default(struct backlight_device *); +int backlight_register(struct backlight_device *); +struct backlight_device *of_backlight_find(struct device_node *node); + +#endif /* __VIDEO_BACKLIGHT_H */ |