diff options
| author | Takashi Iwai <tiwai@suse.de> | 2011-08-08 14:30:29 +0200 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2011-08-08 14:30:29 +0200 | 
| commit | 0a2d31b62dba9b5b92a38c67c9cc42630513662a (patch) | |
| tree | f755d74ec85248de645e10c45ed1a2ed467530f6 /drivers/video | |
| parent | 8039290a91c5dc4414093c086987a5d7738fe2fd (diff) | |
| parent | df944f66784e6d4f2f50739263a4947885d8b6ae (diff) | |
Merge branch 'fix/kconfig' into for-linus
Diffstat (limited to 'drivers/video')
67 files changed, 3719 insertions, 1456 deletions
| diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 5fc983c5b92c..cf03ad067147 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -447,6 +447,8 @@ static int clcdfb_register(struct clcd_fb *fb)  		goto out;  	} +	fb->fb.device		= &fb->dev->dev; +  	fb->fb.fix.mmio_start	= fb->dev->res.start;  	fb->fb.fix.mmio_len	= resource_size(&fb->dev->res); diff --git a/drivers/video/arcfb.c b/drivers/video/arcfb.c index 3ec4923c2d84..c22e8d39a2cb 100644 --- a/drivers/video/arcfb.c +++ b/drivers/video/arcfb.c @@ -515,11 +515,10 @@ static int __devinit arcfb_probe(struct platform_device *dev)  	/* We need a flat backing store for the Arc's  	   less-flat actual paged framebuffer */ -	if (!(videomemory = vmalloc(videomemorysize))) +	videomemory = vzalloc(videomemorysize); +	if (!videomemory)  		return retval; -	memset(videomemory, 0, videomemorysize); -  	info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev);  	if (!info)  		goto err; diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 4484c721f0f9..817ab60f7537 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -906,7 +906,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)  	if (map) {  		/* use a pre-allocated memory buffer */  		info->fix.smem_start = map->start; -		info->fix.smem_len = map->end - map->start + 1; +		info->fix.smem_len = resource_size(map);  		if (!request_mem_region(info->fix.smem_start,  					info->fix.smem_len, pdev->name)) {  			ret = -EBUSY; @@ -932,7 +932,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)  	/* LCDC registers */  	info->fix.mmio_start = regs->start; -	info->fix.mmio_len = regs->end - regs->start + 1; +	info->fix.mmio_len = resource_size(regs);  	if (!request_mem_region(info->fix.mmio_start,  				info->fix.mmio_len, pdev->name)) { diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index ebb893c49e90..44bdce4242ad 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -248,10 +248,6 @@ static int atyfb_sync(struct fb_info *info);  static int aty_init(struct fb_info *info); -#ifdef CONFIG_ATARI -static int store_video_par(char *videopar, unsigned char m64_num); -#endif -  static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);  static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); @@ -2268,11 +2264,13 @@ error:  	return;  } +#ifdef CONFIG_PCI  static void aty_bl_exit(struct backlight_device *bd)  {  	backlight_device_unregister(bd);  	printk("aty: Backlight unloaded\n");  } +#endif /* CONFIG_PCI */  #endif /* CONFIG_FB_ATY_BACKLIGHT */ @@ -2789,7 +2787,7 @@ aty_init_exit:  	return ret;  } -#ifdef CONFIG_ATARI +#if defined(CONFIG_ATARI) && !defined(MODULE)  static int __devinit store_video_par(char *video_str, unsigned char m64_num)  {  	char *p; @@ -2818,7 +2816,7 @@ static int __devinit store_video_par(char *video_str, unsigned char m64_num)  	phys_vmembase[m64_num] = 0;  	return -1;  } -#endif /* CONFIG_ATARI */ +#endif /* CONFIG_ATARI && !MODULE */  /*   * Blank the display. @@ -3460,9 +3458,10 @@ static int __devinit atyfb_setup_generic(struct pci_dev *pdev,  	raddr = addr + 0x7ff000UL;  	rrp = &pdev->resource[2]; -	if ((rrp->flags & IORESOURCE_MEM) && request_mem_region(rrp->start, rrp->end - rrp->start + 1, "atyfb")) { +	if ((rrp->flags & IORESOURCE_MEM) && +	    request_mem_region(rrp->start, resource_size(rrp), "atyfb")) {  		par->aux_start = rrp->start; -		par->aux_size = rrp->end - rrp->start + 1; +		par->aux_size = resource_size(rrp);  		raddr = rrp->start;  		PRINTKI("using auxiliary register aperture\n");  	} @@ -3552,7 +3551,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev,  	/* Reserve space */  	res_start = rp->start; -	res_size = rp->end - rp->start + 1; +	res_size = resource_size(rp);  	if (!request_mem_region(res_start, res_size, "atyfb"))  		return -EBUSY; diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index 34b2fc472fe8..01a8fde67f20 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -486,7 +486,7 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)  	}  	au1100fb_fix.mmio_start = regs_res->start; -	au1100fb_fix.mmio_len = regs_res->end - regs_res->start + 1; +	au1100fb_fix.mmio_len = resource_size(regs_res);  	if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len,  				DRIVER_NAME)) { diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 0c9373bedd1f..278aeaa92505 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -117,6 +117,14 @@ config LCD_LD9040  	  If you have an LD9040 Panel, say Y to enable its  	  control driver. +config LCD_AMS369FG06 +	tristate "AMS369FG06 AMOLED LCD Driver" +	depends on SPI && BACKLIGHT_CLASS_DEVICE +	default n +	help +	  If you have an AMS369FG06 AMOLED Panel, say Y to enable its +	  LCD control driver. +  endif # LCD_CLASS_DEVICE  # @@ -302,6 +310,18 @@ config BACKLIGHT_ADP8860  	  To compile this driver as a module, choose M here: the module will  	  be called adp8860_bl. +config BACKLIGHT_ADP8870 +	tristate "Backlight Driver for ADP8870 using WLED" +	depends on BACKLIGHT_CLASS_DEVICE && I2C +	select NEW_LEDS +	select LEDS_CLASS +	help +	  If you have a LCD backlight connected to the ADP8870, +	  say Y here to enable this driver. + +	  To compile this driver as a module, choose M here: the module will +	  be called adp8870_bl. +  config BACKLIGHT_88PM860X  	tristate "Backlight Driver for 88PM8606 using WLED"  	depends on MFD_88PM860X @@ -315,6 +335,13 @@ config BACKLIGHT_PCF50633  	  If you have a backlight driven by a NXP PCF50633 MFD, say Y here to  	  enable its driver. +config BACKLIGHT_AAT2870 +	tristate "AnalogicTech AAT2870 Backlight" +	depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE +	help +	  If you have a AnalogicTech AAT2870 say Y to enable the +	  backlight driver. +  endif # BACKLIGHT_CLASS_DEVICE  endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b9ca8490df87..fdd1fc4b2770 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_LCD_TDO24M)	   += tdo24m.o  obj-$(CONFIG_LCD_TOSA)		   += tosa_lcd.o  obj-$(CONFIG_LCD_S6E63M0)	+= s6e63m0.o  obj-$(CONFIG_LCD_LD9040)	+= ld9040.o +obj-$(CONFIG_LCD_AMS369FG06)	+= ams369fg06.o  obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o  obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o @@ -34,6 +35,8 @@ obj-$(CONFIG_BACKLIGHT_WM831X)	+= wm831x_bl.o  obj-$(CONFIG_BACKLIGHT_ADX)    += adx_bl.o  obj-$(CONFIG_BACKLIGHT_ADP5520)	+= adp5520_bl.o  obj-$(CONFIG_BACKLIGHT_ADP8860)	+= adp8860_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8870)	+= adp8870_bl.o  obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o  obj-$(CONFIG_BACKLIGHT_PCF50633)	+= pcf50633-backlight.o +obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c new file mode 100644 index 000000000000..331f1ef1dad5 --- /dev/null +++ b/drivers/video/backlight/aat2870_bl.c @@ -0,0 +1,246 @@ +/* + * linux/drivers/video/backlight/aat2870_bl.c + * + * Copyright (c) 2011, NVIDIA Corporation. + * Author: Jin Park <jinyoungp@nvidia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/mfd/aat2870.h> + +struct aat2870_bl_driver_data { +	struct platform_device *pdev; +	struct backlight_device *bd; + +	int channels; +	int max_current; +	int brightness; /* current brightness */ +}; + +static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl, +				     int brightness) +{ +	struct backlight_device *bd = aat2870_bl->bd; +	int val; + +	val = brightness * (aat2870_bl->max_current - 1); +	val /= bd->props.max_brightness; + +	return val; +} + +static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl) +{ +	struct aat2870_data *aat2870 +			= dev_get_drvdata(aat2870_bl->pdev->dev.parent); + +	return aat2870->write(aat2870, AAT2870_BL_CH_EN, +			      (u8)aat2870_bl->channels); +} + +static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl) +{ +	struct aat2870_data *aat2870 +			= dev_get_drvdata(aat2870_bl->pdev->dev.parent); + +	return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0); +} + +static int aat2870_bl_get_brightness(struct backlight_device *bd) +{ +	return bd->props.brightness; +} + +static int aat2870_bl_update_status(struct backlight_device *bd) +{ +	struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev); +	struct aat2870_data *aat2870 = +			dev_get_drvdata(aat2870_bl->pdev->dev.parent); +	int brightness = bd->props.brightness; +	int ret; + +	if ((brightness < 0) || (bd->props.max_brightness < brightness)) { +		dev_err(&bd->dev, "invalid brightness, %d\n", brightness); +		return -EINVAL; +	} + +	dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n", +		 bd->props.brightness, bd->props.power, bd->props.state); + +	if ((bd->props.power != FB_BLANK_UNBLANK) || +			(bd->props.state & BL_CORE_FBBLANK) || +			(bd->props.state & BL_CORE_SUSPENDED)) +		brightness = 0; + +	ret = aat2870->write(aat2870, AAT2870_BLM, +			     (u8)aat2870_brightness(aat2870_bl, brightness)); +	if (ret < 0) +		return ret; + +	if (brightness == 0) { +		ret = aat2870_bl_disable(aat2870_bl); +		if (ret < 0) +			return ret; +	} else if (aat2870_bl->brightness == 0) { +		ret = aat2870_bl_enable(aat2870_bl); +		if (ret < 0) +			return ret; +	} + +	aat2870_bl->brightness = brightness; + +	return 0; +} + +static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi) +{ +	return 1; +} + +static const struct backlight_ops aat2870_bl_ops = { +	.options = BL_CORE_SUSPENDRESUME, +	.get_brightness = aat2870_bl_get_brightness, +	.update_status = aat2870_bl_update_status, +	.check_fb = aat2870_bl_check_fb, +}; + +static int aat2870_bl_probe(struct platform_device *pdev) +{ +	struct aat2870_bl_platform_data *pdata = pdev->dev.platform_data; +	struct aat2870_bl_driver_data *aat2870_bl; +	struct backlight_device *bd; +	struct backlight_properties props; +	int ret = 0; + +	if (!pdata) { +		dev_err(&pdev->dev, "No platform data\n"); +		ret = -ENXIO; +		goto out; +	} + +	if (pdev->id != AAT2870_ID_BL) { +		dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id); +		ret = -EINVAL; +		goto out; +	} + +	aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL); +	if (!aat2870_bl) { +		dev_err(&pdev->dev, +			"Failed to allocate memory for aat2870 backlight\n"); +		ret = -ENOMEM; +		goto out; +	} + +	memset(&props, 0, sizeof(struct backlight_properties)); + +	props.type = BACKLIGHT_RAW; +	bd = backlight_device_register("aat2870-backlight", &pdev->dev, +				       aat2870_bl, &aat2870_bl_ops, &props); +	if (IS_ERR(bd)) { +		dev_err(&pdev->dev, +			"Failed allocate memory for backlight device\n"); +		ret = PTR_ERR(bd); +		goto out_kfree; +	} + +	aat2870_bl->pdev = pdev; +	platform_set_drvdata(pdev, aat2870_bl); + +	aat2870_bl->bd = bd; + +	if (pdata->channels > 0) +		aat2870_bl->channels = pdata->channels; +	else +		aat2870_bl->channels = AAT2870_BL_CH_ALL; + +	if (pdata->max_current > 0) +		aat2870_bl->max_current = pdata->max_current; +	else +		aat2870_bl->max_current = AAT2870_CURRENT_27_9; + +	if (pdata->max_brightness > 0) +		bd->props.max_brightness = pdata->max_brightness; +	else +		bd->props.max_brightness = 255; + +	aat2870_bl->brightness = 0; +	bd->props.power = FB_BLANK_UNBLANK; +	bd->props.brightness = bd->props.max_brightness; + +	ret = aat2870_bl_update_status(bd); +	if (ret < 0) { +		dev_err(&pdev->dev, "Failed to initialize\n"); +		goto out_bl_dev_unregister; +	} + +	return 0; + +out_bl_dev_unregister: +	backlight_device_unregister(bd); +out_kfree: +	kfree(aat2870_bl); +out: +	return ret; +} + +static int aat2870_bl_remove(struct platform_device *pdev) +{ +	struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev); +	struct backlight_device *bd = aat2870_bl->bd; + +	bd->props.power = FB_BLANK_POWERDOWN; +	bd->props.brightness = 0; +	backlight_update_status(bd); + +	backlight_device_unregister(bd); +	kfree(aat2870_bl); + +	return 0; +} + +static struct platform_driver aat2870_bl_driver = { +	.driver = { +		.name	= "aat2870-backlight", +		.owner	= THIS_MODULE, +	}, +	.probe		= aat2870_bl_probe, +	.remove		= aat2870_bl_remove, +}; + +static int __init aat2870_bl_init(void) +{ +	return platform_driver_register(&aat2870_bl_driver); +} +subsys_initcall(aat2870_bl_init); + +static void __exit aat2870_bl_exit(void) +{ +	platform_driver_unregister(&aat2870_bl_driver); +} +module_exit(aat2870_bl_exit); + +MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>"); diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index d2a96a421ffd..183b6f639852 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -722,8 +722,7 @@ static int __devinit adp8860_probe(struct i2c_client *client,  		goto out2;  	} -	bl->props.max_brightness = -		bl->props.brightness = ADP8860_MAX_BRIGHTNESS; +	bl->props.brightness = ADP8860_MAX_BRIGHTNESS;  	data->bl = bl; diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c new file mode 100644 index 000000000000..05a8832bb3eb --- /dev/null +++ b/drivers/video/backlight/adp8870_bl.c @@ -0,0 +1,1012 @@ +/* + * Backlight driver for Analog Devices ADP8870 Backlight Devices + * + * Copyright 2009-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#include <linux/i2c/adp8870.h> +#define ADP8870_EXT_FEATURES +#define ADP8870_USE_LEDS + + +#define ADP8870_MFDVID	0x00  /* Manufacturer and device ID */ +#define ADP8870_MDCR	0x01  /* Device mode and status */ +#define ADP8870_INT_STAT 0x02  /* Interrupts status */ +#define ADP8870_INT_EN	0x03  /* Interrupts enable */ +#define ADP8870_CFGR	0x04  /* Configuration register */ +#define ADP8870_BLSEL	0x05  /* Sink enable backlight or independent */ +#define ADP8870_PWMLED	0x06  /* PWM Enable Selection Register */ +#define ADP8870_BLOFF	0x07  /* Backlight off timeout */ +#define ADP8870_BLDIM	0x08  /* Backlight dim timeout */ +#define ADP8870_BLFR	0x09  /* Backlight fade in and out rates */ +#define ADP8870_BLMX1	0x0A  /* Backlight (Brightness Level 1-daylight) maximum current */ +#define ADP8870_BLDM1	0x0B  /* Backlight (Brightness Level 1-daylight) dim current */ +#define ADP8870_BLMX2	0x0C  /* Backlight (Brightness Level 2-bright) maximum current */ +#define ADP8870_BLDM2	0x0D  /* Backlight (Brightness Level 2-bright) dim current */ +#define ADP8870_BLMX3	0x0E  /* Backlight (Brightness Level 3-office) maximum current */ +#define ADP8870_BLDM3	0x0F  /* Backlight (Brightness Level 3-office) dim current */ +#define ADP8870_BLMX4	0x10  /* Backlight (Brightness Level 4-indoor) maximum current */ +#define ADP8870_BLDM4	0x11  /* Backlight (Brightness Level 4-indoor) dim current */ +#define ADP8870_BLMX5	0x12  /* Backlight (Brightness Level 5-dark) maximum current */ +#define ADP8870_BLDM5	0x13  /* Backlight (Brightness Level 5-dark) dim current */ +#define ADP8870_ISCLAW	0x1A  /* Independent sink current fade law register */ +#define ADP8870_ISCC	0x1B  /* Independent sink current control register */ +#define ADP8870_ISCT1	0x1C  /* Independent Sink Current Timer Register LED[7:5] */ +#define ADP8870_ISCT2	0x1D  /* Independent Sink Current Timer Register LED[4:1] */ +#define ADP8870_ISCF	0x1E  /* Independent sink current fade register */ +#define ADP8870_ISC1	0x1F  /* Independent Sink Current LED1 */ +#define ADP8870_ISC2	0x20  /* Independent Sink Current LED2 */ +#define ADP8870_ISC3	0x21  /* Independent Sink Current LED3 */ +#define ADP8870_ISC4	0x22  /* Independent Sink Current LED4 */ +#define ADP8870_ISC5	0x23  /* Independent Sink Current LED5 */ +#define ADP8870_ISC6	0x24  /* Independent Sink Current LED6 */ +#define ADP8870_ISC7	0x25  /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ +#define ADP8870_ISC7_L2	0x26  /* Independent Sink Current LED7 (Brightness Level 2-bright) */ +#define ADP8870_ISC7_L3	0x27  /* Independent Sink Current LED7 (Brightness Level 3-office) */ +#define ADP8870_ISC7_L4	0x28  /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ +#define ADP8870_ISC7_L5	0x29  /* Independent Sink Current LED7 (Brightness Level 5-dark) */ +#define ADP8870_CMP_CTL	0x2D  /* ALS Comparator Control Register */ +#define ADP8870_ALS1_EN	0x2E  /* Main ALS comparator level enable */ +#define ADP8870_ALS2_EN	0x2F  /* Second ALS comparator level enable */ +#define ADP8870_ALS1_STAT 0x30  /* Main ALS Comparator Status Register */ +#define ADP8870_ALS2_STAT 0x31  /* Second ALS Comparator Status Register */ +#define ADP8870_L2TRP	0x32  /* L2 comparator reference */ +#define ADP8870_L2HYS	0x33  /* L2 hysteresis */ +#define ADP8870_L3TRP	0x34  /* L3 comparator reference */ +#define ADP8870_L3HYS	0x35  /* L3 hysteresis */ +#define ADP8870_L4TRP	0x36  /* L4 comparator reference */ +#define ADP8870_L4HYS	0x37  /* L4 hysteresis */ +#define ADP8870_L5TRP	0x38  /* L5 comparator reference */ +#define ADP8870_L5HYS	0x39  /* L5 hysteresis */ +#define ADP8870_PH1LEVL	0x40  /* First phototransistor ambient light level-low byte register */ +#define ADP8870_PH1LEVH	0x41  /* First phototransistor ambient light level-high byte register */ +#define ADP8870_PH2LEVL	0x42  /* Second phototransistor ambient light level-low byte register */ +#define ADP8870_PH2LEVH	0x43  /* Second phototransistor ambient light level-high byte register */ + +#define ADP8870_MANUFID		0x3  /* Analog Devices AD8870 Manufacturer and device ID */ +#define ADP8870_DEVID(x)	((x) & 0xF) +#define ADP8870_MANID(x)	((x) >> 4) + +/* MDCR Device mode and status */ +#define D7ALSEN			(1 << 7) +#define INT_CFG			(1 << 6) +#define NSTBY			(1 << 5) +#define DIM_EN			(1 << 4) +#define GDWN_DIS		(1 << 3) +#define SIS_EN			(1 << 2) +#define CMP_AUTOEN		(1 << 1) +#define BLEN			(1 << 0) + +/* ADP8870_ALS1_EN Main ALS comparator level enable */ +#define L5_EN			(1 << 3) +#define L4_EN			(1 << 2) +#define L3_EN			(1 << 1) +#define L2_EN			(1 << 0) + +#define CFGR_BLV_SHIFT		3 +#define CFGR_BLV_MASK		0x7 +#define ADP8870_FLAG_LED_MASK	0xFF + +#define FADE_VAL(in, out)	((0xF & (in)) | ((0xF & (out)) << 4)) +#define BL_CFGR_VAL(law, blv)	((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) +#define ALS_CMPR_CFG_VAL(filt)	((0x7 & (filt)) << 1) + +struct adp8870_bl { +	struct i2c_client *client; +	struct backlight_device *bl; +	struct adp8870_led *led; +	struct adp8870_backlight_platform_data *pdata; +	struct mutex lock; +	unsigned long cached_daylight_max; +	int id; +	int revid; +	int current_brightness; +}; + +struct adp8870_led { +	struct led_classdev	cdev; +	struct work_struct	work; +	struct i2c_client	*client; +	enum led_brightness	new_brightness; +	int			id; +	int			flags; +}; + +static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) +{ +	int ret; + +	ret = i2c_smbus_read_byte_data(client, reg); +	if (ret < 0) { +		dev_err(&client->dev, "failed reading at 0x%02x\n", reg); +		return ret; +	} + +	*val = ret; +	return 0; +} + + +static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) +{ +	int ret = i2c_smbus_write_byte_data(client, reg, val); +	if (ret) +		dev_err(&client->dev, "failed to write\n"); + +	return ret; +} + +static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ +	struct adp8870_bl *data = i2c_get_clientdata(client); +	uint8_t reg_val; +	int ret; + +	mutex_lock(&data->lock); + +	ret = adp8870_read(client, reg, ®_val); + +	if (!ret && ((reg_val & bit_mask) == 0)) { +		reg_val |= bit_mask; +		ret = adp8870_write(client, reg, reg_val); +	} + +	mutex_unlock(&data->lock); +	return ret; +} + +static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) +{ +	struct adp8870_bl *data = i2c_get_clientdata(client); +	uint8_t reg_val; +	int ret; + +	mutex_lock(&data->lock); + +	ret = adp8870_read(client, reg, ®_val); + +	if (!ret && (reg_val & bit_mask)) { +		reg_val &= ~bit_mask; +		ret = adp8870_write(client, reg, reg_val); +	} + +	mutex_unlock(&data->lock); +	return ret; +} + +/* + * Independent sink / LED + */ +#if defined(ADP8870_USE_LEDS) +static void adp8870_led_work(struct work_struct *work) +{ +	struct adp8870_led *led = container_of(work, struct adp8870_led, work); +	adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, +			 led->new_brightness >> 1); +} + +static void adp8870_led_set(struct led_classdev *led_cdev, +			   enum led_brightness value) +{ +	struct adp8870_led *led; + +	led = container_of(led_cdev, struct adp8870_led, cdev); +	led->new_brightness = value; +	/* +	 * Use workqueue for IO since I2C operations can sleep. +	 */ +	schedule_work(&led->work); +} + +static int adp8870_led_setup(struct adp8870_led *led) +{ +	struct i2c_client *client = led->client; +	int ret = 0; + +	ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); +	if (ret) +		return ret; + +	ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); +	if (ret) +		return ret; + +	if (led->id > 4) +		ret = adp8870_set_bits(client, ADP8870_ISCT1, +				(led->flags & 0x3) << ((led->id - 5) * 2)); +	else +		ret = adp8870_set_bits(client, ADP8870_ISCT2, +				(led->flags & 0x3) << ((led->id - 1) * 2)); + +	return ret; +} + +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ +	struct adp8870_backlight_platform_data *pdata = +		client->dev.platform_data; +	struct adp8870_bl *data = i2c_get_clientdata(client); +	struct adp8870_led *led, *led_dat; +	struct led_info *cur_led; +	int ret, i; + + +	led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL); +	if (led == NULL) { +		dev_err(&client->dev, "failed to alloc memory\n"); +		return -ENOMEM; +	} + +	ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); +	if (ret) +		goto err_free; + +	ret = adp8870_write(client, ADP8870_ISCT1, +			(pdata->led_on_time & 0x3) << 6); +	if (ret) +		goto err_free; + +	ret = adp8870_write(client, ADP8870_ISCF, +			FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); +	if (ret) +		goto err_free; + +	for (i = 0; i < pdata->num_leds; ++i) { +		cur_led = &pdata->leds[i]; +		led_dat = &led[i]; + +		led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; + +		if (led_dat->id > 7 || led_dat->id < 1) { +			dev_err(&client->dev, "Invalid LED ID %d\n", +				led_dat->id); +			goto err; +		} + +		if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { +			dev_err(&client->dev, "LED %d used by Backlight\n", +				led_dat->id); +			goto err; +		} + +		led_dat->cdev.name = cur_led->name; +		led_dat->cdev.default_trigger = cur_led->default_trigger; +		led_dat->cdev.brightness_set = adp8870_led_set; +		led_dat->cdev.brightness = LED_OFF; +		led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; +		led_dat->client = client; +		led_dat->new_brightness = LED_OFF; +		INIT_WORK(&led_dat->work, adp8870_led_work); + +		ret = led_classdev_register(&client->dev, &led_dat->cdev); +		if (ret) { +			dev_err(&client->dev, "failed to register LED %d\n", +				led_dat->id); +			goto err; +		} + +		ret = adp8870_led_setup(led_dat); +		if (ret) { +			dev_err(&client->dev, "failed to write\n"); +			i++; +			goto err; +		} +	} + +	data->led = led; + +	return 0; + + err: +	for (i = i - 1; i >= 0; --i) { +		led_classdev_unregister(&led[i].cdev); +		cancel_work_sync(&led[i].work); +	} + + err_free: +	kfree(led); + +	return ret; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ +	struct adp8870_backlight_platform_data *pdata = +		client->dev.platform_data; +	struct adp8870_bl *data = i2c_get_clientdata(client); +	int i; + +	for (i = 0; i < pdata->num_leds; i++) { +		led_classdev_unregister(&data->led[i].cdev); +		cancel_work_sync(&data->led[i].work); +	} + +	kfree(data->led); +	return 0; +} +#else +static int __devinit adp8870_led_probe(struct i2c_client *client) +{ +	return 0; +} + +static int __devexit adp8870_led_remove(struct i2c_client *client) +{ +	return 0; +} +#endif + +static int adp8870_bl_set(struct backlight_device *bl, int brightness) +{ +	struct adp8870_bl *data = bl_get_data(bl); +	struct i2c_client *client = data->client; +	int ret = 0; + +	if (data->pdata->en_ambl_sens) { +		if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { +			/* Disable Ambient Light auto adjust */ +			ret = adp8870_clr_bits(client, ADP8870_MDCR, +					CMP_AUTOEN); +			if (ret) +				return ret; +			ret = adp8870_write(client, ADP8870_BLMX1, brightness); +			if (ret) +				return ret; +		} else { +			/* +			 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust +			 * restore daylight l1 sysfs brightness +			 */ +			ret = adp8870_write(client, ADP8870_BLMX1, +					 data->cached_daylight_max); +			if (ret) +				return ret; + +			ret = adp8870_set_bits(client, ADP8870_MDCR, +					 CMP_AUTOEN); +			if (ret) +				return ret; +		} +	} else { +		ret = adp8870_write(client, ADP8870_BLMX1, brightness); +		if (ret) +			return ret; +	} + +	if (data->current_brightness && brightness == 0) +		ret = adp8870_set_bits(client, +				ADP8870_MDCR, DIM_EN); +	else if (data->current_brightness == 0 && brightness) +		ret = adp8870_clr_bits(client, +				ADP8870_MDCR, DIM_EN); + +	if (!ret) +		data->current_brightness = brightness; + +	return ret; +} + +static int adp8870_bl_update_status(struct backlight_device *bl) +{ +	int brightness = bl->props.brightness; +	if (bl->props.power != FB_BLANK_UNBLANK) +		brightness = 0; + +	if (bl->props.fb_blank != FB_BLANK_UNBLANK) +		brightness = 0; + +	return adp8870_bl_set(bl, brightness); +} + +static int adp8870_bl_get_brightness(struct backlight_device *bl) +{ +	struct adp8870_bl *data = bl_get_data(bl); + +	return data->current_brightness; +} + +static const struct backlight_ops adp8870_bl_ops = { +	.update_status	= adp8870_bl_update_status, +	.get_brightness	= adp8870_bl_get_brightness, +}; + +static int adp8870_bl_setup(struct backlight_device *bl) +{ +	struct adp8870_bl *data = bl_get_data(bl); +	struct i2c_client *client = data->client; +	struct adp8870_backlight_platform_data *pdata = data->pdata; +	int ret = 0; + +	ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); +	if (ret) +		return ret; + +	ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); +	if (ret) +		return ret; + +	ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); +	if (ret) +		return ret; + +	ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); +	if (ret) +		return ret; + +	if (pdata->en_ambl_sens) { +		data->cached_daylight_max = pdata->l1_daylight_max; +		ret = adp8870_write(client, ADP8870_BLMX2, +						pdata->l2_bright_max); +		if (ret) +			return ret; +		ret = adp8870_write(client, ADP8870_BLDM2, +						pdata->l2_bright_dim); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_BLMX3, +						pdata->l3_office_max); +		if (ret) +			return ret; +		ret = adp8870_write(client, ADP8870_BLDM3, +						pdata->l3_office_dim); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_BLMX4, +						pdata->l4_indoor_max); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_BLDM4, +						pdata->l4_indor_dim); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_BLMX5, +						pdata->l5_dark_max); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_BLDM5, +						pdata->l5_dark_dim); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | +						L3_EN | L2_EN); +		if (ret) +			return ret; + +		ret = adp8870_write(client, ADP8870_CMP_CTL, +			ALS_CMPR_CFG_VAL(pdata->abml_filt)); +		if (ret) +			return ret; +	} + +	ret = adp8870_write(client, ADP8870_CFGR, +			BL_CFGR_VAL(pdata->bl_fade_law, 0)); +	if (ret) +		return ret; + +	ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, +			pdata->bl_fade_out)); +	if (ret) +		return ret; +	/* +	 * ADP8870 Rev0 requires GDWN_DIS bit set +	 */ + +	ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | +			(data->revid == 0 ? GDWN_DIS : 0)); + +	return ret; +} + +static ssize_t adp8870_show(struct device *dev, char *buf, int reg) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	int error; +	uint8_t reg_val; + +	mutex_lock(&data->lock); +	error = adp8870_read(data->client, reg, ®_val); +	mutex_unlock(&data->lock); + +	if (error < 0) +		return error; + +	return sprintf(buf, "%u\n", reg_val); +} + +static ssize_t adp8870_store(struct device *dev, const char *buf, +			 size_t count, int reg) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	unsigned long val; +	int ret; + +	ret = strict_strtoul(buf, 10, &val); +	if (ret) +		return ret; + +	mutex_lock(&data->lock); +	adp8870_write(data->client, reg, val); +	mutex_unlock(&data->lock); + +	return count; +} + +static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLMX5); +} + +static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLMX5); +} +static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, +			adp8870_bl_l5_dark_max_store); + + +static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLMX4); +} + +static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLMX4); +} +static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, +			adp8870_bl_l4_indoor_max_store); + + +static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLMX3); +} + +static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLMX3); +} + +static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, +			adp8870_bl_l3_office_max_store); + +static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLMX2); +} + +static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLMX2); +} +static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, +			adp8870_bl_l2_bright_max_store); + +static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLMX1); +} + +static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); +	if (ret) +		return ret; + +	return adp8870_store(dev, buf, count, ADP8870_BLMX1); +} +static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, +			adp8870_bl_l1_daylight_max_store); + +static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLDM5); +} + +static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLDM5); +} +static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, +			adp8870_bl_l5_dark_dim_store); + +static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLDM4); +} + +static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLDM4); +} +static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, +			adp8870_bl_l4_indoor_dim_store); + + +static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLDM3); +} + +static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLDM3); +} +static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, +			adp8870_bl_l3_office_dim_store); + +static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLDM2); +} + +static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLDM2); +} +static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, +			adp8870_bl_l2_bright_dim_store); + +static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	return adp8870_show(dev, buf, ADP8870_BLDM1); +} + +static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	return adp8870_store(dev, buf, count, ADP8870_BLDM1); +} +static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, +			adp8870_bl_l1_daylight_dim_store); + +#ifdef ADP8870_EXT_FEATURES +static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	int error; +	uint8_t reg_val; +	uint16_t ret_val; + +	mutex_lock(&data->lock); +	error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); +	if (error < 0) { +		mutex_unlock(&data->lock); +		return error; +	} +	ret_val = reg_val; +	error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); +	mutex_unlock(&data->lock); + +	if (error < 0) +		return error; + +	/* Return 13-bit conversion value for the first light sensor */ +	ret_val += (reg_val & 0x1F) << 8; + +	return sprintf(buf, "%u\n", ret_val); +} +static DEVICE_ATTR(ambient_light_level, 0444, +		adp8870_bl_ambient_light_level_show, NULL); + +static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	int error; +	uint8_t reg_val; + +	mutex_lock(&data->lock); +	error = adp8870_read(data->client, ADP8870_CFGR, ®_val); +	mutex_unlock(&data->lock); + +	if (error < 0) +		return error; + +	return sprintf(buf, "%u\n", +		((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); +} + +static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t count) +{ +	struct adp8870_bl *data = dev_get_drvdata(dev); +	unsigned long val; +	uint8_t reg_val; +	int ret; + +	ret = strict_strtoul(buf, 10, &val); +	if (ret) +		return ret; + +	if (val == 0) { +		/* Enable automatic ambient light sensing */ +		adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); +	} else if ((val > 0) && (val < 6)) { +		/* Disable automatic ambient light sensing */ +		adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); + +		/* Set user supplied ambient light zone */ +		mutex_lock(&data->lock); +		adp8870_read(data->client, ADP8870_CFGR, ®_val); +		reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); +		reg_val |= (val - 1) << CFGR_BLV_SHIFT; +		adp8870_write(data->client, ADP8870_CFGR, reg_val); +		mutex_unlock(&data->lock); +	} + +	return count; +} +static DEVICE_ATTR(ambient_light_zone, 0664, +		adp8870_bl_ambient_light_zone_show, +		adp8870_bl_ambient_light_zone_store); +#endif + +static struct attribute *adp8870_bl_attributes[] = { +	&dev_attr_l5_dark_max.attr, +	&dev_attr_l5_dark_dim.attr, +	&dev_attr_l4_indoor_max.attr, +	&dev_attr_l4_indoor_dim.attr, +	&dev_attr_l3_office_max.attr, +	&dev_attr_l3_office_dim.attr, +	&dev_attr_l2_bright_max.attr, +	&dev_attr_l2_bright_dim.attr, +	&dev_attr_l1_daylight_max.attr, +	&dev_attr_l1_daylight_dim.attr, +#ifdef ADP8870_EXT_FEATURES +	&dev_attr_ambient_light_level.attr, +	&dev_attr_ambient_light_zone.attr, +#endif +	NULL +}; + +static const struct attribute_group adp8870_bl_attr_group = { +	.attrs = adp8870_bl_attributes, +}; + +static int __devinit adp8870_probe(struct i2c_client *client, +					const struct i2c_device_id *id) +{ +	struct backlight_properties props; +	struct backlight_device *bl; +	struct adp8870_bl *data; +	struct adp8870_backlight_platform_data *pdata = +		client->dev.platform_data; +	uint8_t reg_val; +	int ret; + +	if (!i2c_check_functionality(client->adapter, +					I2C_FUNC_SMBUS_BYTE_DATA)) { +		dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); +		return -EIO; +	} + +	if (!pdata) { +		dev_err(&client->dev, "no platform data?\n"); +		return -EINVAL; +	} + +	ret = adp8870_read(client, ADP8870_MFDVID, ®_val); +	if (ret < 0) +		return -EIO; + +	if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { +		dev_err(&client->dev, "failed to probe\n"); +		return -ENODEV; +	} + +	data = kzalloc(sizeof(*data), GFP_KERNEL); +	if (data == NULL) +		return -ENOMEM; + +	data->revid = ADP8870_DEVID(reg_val); +	data->client = client; +	data->pdata = pdata; +	data->id = id->driver_data; +	data->current_brightness = 0; +	i2c_set_clientdata(client, data); + +	mutex_init(&data->lock); + +	memset(&props, 0, sizeof(props)); +	props.type = BACKLIGHT_RAW; +	props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; +	bl = backlight_device_register(dev_driver_string(&client->dev), +			&client->dev, data, &adp8870_bl_ops, &props); +	if (IS_ERR(bl)) { +		dev_err(&client->dev, "failed to register backlight\n"); +		ret = PTR_ERR(bl); +		goto out2; +	} + +	data->bl = bl; + +	if (pdata->en_ambl_sens) +		ret = sysfs_create_group(&bl->dev.kobj, +			&adp8870_bl_attr_group); + +	if (ret) { +		dev_err(&client->dev, "failed to register sysfs\n"); +		goto out1; +	} + +	ret = adp8870_bl_setup(bl); +	if (ret) { +		ret = -EIO; +		goto out; +	} + +	backlight_update_status(bl); + +	dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); + +	if (pdata->num_leds) +		adp8870_led_probe(client); + +	return 0; + +out: +	if (data->pdata->en_ambl_sens) +		sysfs_remove_group(&data->bl->dev.kobj, +			&adp8870_bl_attr_group); +out1: +	backlight_device_unregister(bl); +out2: +	i2c_set_clientdata(client, NULL); +	kfree(data); + +	return ret; +} + +static int __devexit adp8870_remove(struct i2c_client *client) +{ +	struct adp8870_bl *data = i2c_get_clientdata(client); + +	adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + +	if (data->led) +		adp8870_led_remove(client); + +	if (data->pdata->en_ambl_sens) +		sysfs_remove_group(&data->bl->dev.kobj, +			&adp8870_bl_attr_group); + +	backlight_device_unregister(data->bl); +	i2c_set_clientdata(client, NULL); +	kfree(data); + +	return 0; +} + +#ifdef CONFIG_PM +static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ +	adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); + +	return 0; +} + +static int adp8870_i2c_resume(struct i2c_client *client) +{ +	adp8870_set_bits(client, ADP8870_MDCR, NSTBY); + +	return 0; +} +#else +#define adp8870_i2c_suspend NULL +#define adp8870_i2c_resume NULL +#endif + +static const struct i2c_device_id adp8870_id[] = { +	{ "adp8870", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, adp8870_id); + +static struct i2c_driver adp8870_driver = { +	.driver = { +		.name = KBUILD_MODNAME, +	}, +	.probe    = adp8870_probe, +	.remove   = __devexit_p(adp8870_remove), +	.suspend = adp8870_i2c_suspend, +	.resume  = adp8870_i2c_resume, +	.id_table = adp8870_id, +}; + +static int __init adp8870_init(void) +{ +	return i2c_add_driver(&adp8870_driver); +} +module_init(adp8870_init); + +static void __exit adp8870_exit(void) +{ +	i2c_del_driver(&adp8870_driver); +} +module_exit(adp8870_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP8870 Backlight driver"); +MODULE_ALIAS("platform:adp8870-backlight"); diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c new file mode 100644 index 000000000000..9f0a491e2a05 --- /dev/null +++ b/drivers/video/backlight/ams369fg06.c @@ -0,0 +1,646 @@ +/* + * ams369fg06 AMOLED LCD panel driver. + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: Jingoo Han  <jg1.han@samsung.com> + * + * Derived from drivers/video/s6e63m0.c + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + */ + +#include <linux/wait.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/lcd.h> +#include <linux/backlight.h> + +#define SLEEPMSEC		0x1000 +#define ENDDEF			0x2000 +#define	DEFMASK			0xFF00 +#define COMMAND_ONLY		0xFE +#define DATA_ONLY		0xFF + +#define MAX_GAMMA_LEVEL		5 +#define GAMMA_TABLE_COUNT	21 + +#define MIN_BRIGHTNESS		0 +#define MAX_BRIGHTNESS		255 +#define DEFAULT_BRIGHTNESS	150 + +struct ams369fg06 { +	struct device			*dev; +	struct spi_device		*spi; +	unsigned int			power; +	struct lcd_device		*ld; +	struct backlight_device		*bd; +	struct lcd_platform_data	*lcd_pd; +}; + +static const unsigned short seq_display_on[] = { +	0x14, 0x03, +	ENDDEF, 0x0000 +}; + +static const unsigned short seq_display_off[] = { +	0x14, 0x00, +	ENDDEF, 0x0000 +}; + +static const unsigned short seq_stand_by_on[] = { +	0x1D, 0xA1, +	SLEEPMSEC, 200, +	ENDDEF, 0x0000 +}; + +static const unsigned short seq_stand_by_off[] = { +	0x1D, 0xA0, +	SLEEPMSEC, 250, +	ENDDEF, 0x0000 +}; + +static const unsigned short seq_setting[] = { +	0x31, 0x08, +	0x32, 0x14, +	0x30, 0x02, +	0x27, 0x01, +	0x12, 0x08, +	0x13, 0x08, +	0x15, 0x00, +	0x16, 0x00, + +	0xef, 0xd0, +	DATA_ONLY, 0xe8, + +	0x39, 0x44, +	0x40, 0x00, +	0x41, 0x3f, +	0x42, 0x2a, +	0x43, 0x27, +	0x44, 0x27, +	0x45, 0x1f, +	0x46, 0x44, +	0x50, 0x00, +	0x51, 0x00, +	0x52, 0x17, +	0x53, 0x24, +	0x54, 0x26, +	0x55, 0x1f, +	0x56, 0x43, +	0x60, 0x00, +	0x61, 0x3f, +	0x62, 0x2a, +	0x63, 0x25, +	0x64, 0x24, +	0x65, 0x1b, +	0x66, 0x5c, + +	0x17, 0x22, +	0x18, 0x33, +	0x19, 0x03, +	0x1a, 0x01, +	0x22, 0xa4, +	0x23, 0x00, +	0x26, 0xa0, + +	0x1d, 0xa0, +	SLEEPMSEC, 300, + +	0x14, 0x03, + +	ENDDEF, 0x0000 +}; + +/* gamma value: 2.2 */ +static const unsigned int ams369fg06_22_250[] = { +	0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44, +	0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43, +	0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c, +}; + +static const unsigned int ams369fg06_22_200[] = { +	0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e, +	0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d, +	0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53, +}; + +static const unsigned int ams369fg06_22_150[] = { +	0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37, +	0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36, +	0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a, +}; + +static const unsigned int ams369fg06_22_100[] = { +	0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f, +	0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e, +	0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f, +}; + +static const unsigned int ams369fg06_22_50[] = { +	0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24, +	0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23, +	0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31, +}; + +struct ams369fg06_gamma { +	unsigned int *gamma_22_table[MAX_GAMMA_LEVEL]; +}; + +static struct ams369fg06_gamma gamma_table = { +	.gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50, +	.gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100, +	.gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150, +	.gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200, +	.gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250, +}; + +static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data) +{ +	u16 buf[1]; +	struct spi_message msg; + +	struct spi_transfer xfer = { +		.len		= 2, +		.tx_buf		= buf, +	}; + +	buf[0] = (addr << 8) | data; + +	spi_message_init(&msg); +	spi_message_add_tail(&xfer, &msg); + +	return spi_sync(lcd->spi, &msg); +} + +static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address, +	unsigned char command) +{ +	int ret = 0; + +	if (address != DATA_ONLY) +		ret = ams369fg06_spi_write_byte(lcd, 0x70, address); +	if (command != COMMAND_ONLY) +		ret = ams369fg06_spi_write_byte(lcd, 0x72, command); + +	return ret; +} + +static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd, +	const unsigned short *wbuf) +{ +	int ret = 0, i = 0; + +	while ((wbuf[i] & DEFMASK) != ENDDEF) { +		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) { +			ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); +			if (ret) +				break; +		} else +			mdelay(wbuf[i+1]); +		i += 2; +	} + +	return ret; +} + +static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd, +	const unsigned int *gamma) +{ +	unsigned int i = 0; +	int ret = 0; + +	for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) { +		ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]); +		ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]); +		ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]); +		if (ret) { +			dev_err(lcd->dev, "failed to set gamma table.\n"); +			goto gamma_err; +		} +	} + +gamma_err: +	return ret; +} + +static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness) +{ +	int ret = 0; +	int gamma = 0; + +	if ((brightness >= 0) && (brightness <= 50)) +		gamma = 0; +	else if ((brightness > 50) && (brightness <= 100)) +		gamma = 1; +	else if ((brightness > 100) && (brightness <= 150)) +		gamma = 2; +	else if ((brightness > 150) && (brightness <= 200)) +		gamma = 3; +	else if ((brightness > 200) && (brightness <= 255)) +		gamma = 4; + +	ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); + +	return ret; +} + +static int ams369fg06_ldi_init(struct ams369fg06 *lcd) +{ +	int ret, i; +	static const unsigned short *init_seq[] = { +		seq_setting, +		seq_stand_by_off, +	}; + +	for (i = 0; i < ARRAY_SIZE(init_seq); i++) { +		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); +		if (ret) +			break; +	} + +	return ret; +} + +static int ams369fg06_ldi_enable(struct ams369fg06 *lcd) +{ +	int ret, i; +	static const unsigned short *init_seq[] = { +		seq_stand_by_off, +		seq_display_on, +	}; + +	for (i = 0; i < ARRAY_SIZE(init_seq); i++) { +		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); +		if (ret) +			break; +	} + +	return ret; +} + +static int ams369fg06_ldi_disable(struct ams369fg06 *lcd) +{ +	int ret, i; + +	static const unsigned short *init_seq[] = { +		seq_display_off, +		seq_stand_by_on, +	}; + +	for (i = 0; i < ARRAY_SIZE(init_seq); i++) { +		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]); +		if (ret) +			break; +	} + +	return ret; +} + +static int ams369fg06_power_is_on(int power) +{ +	return ((power) <= FB_BLANK_NORMAL); +} + +static int ams369fg06_power_on(struct ams369fg06 *lcd) +{ +	int ret = 0; +	struct lcd_platform_data *pd = NULL; +	struct backlight_device *bd = NULL; + +	pd = lcd->lcd_pd; +	if (!pd) { +		dev_err(lcd->dev, "platform data is NULL.\n"); +		return -EFAULT; +	} + +	bd = lcd->bd; +	if (!bd) { +		dev_err(lcd->dev, "backlight device is NULL.\n"); +		return -EFAULT; +	} + +	if (!pd->power_on) { +		dev_err(lcd->dev, "power_on is NULL.\n"); +		return -EFAULT; +	} else { +		pd->power_on(lcd->ld, 1); +		mdelay(pd->power_on_delay); +	} + +	if (!pd->reset) { +		dev_err(lcd->dev, "reset is NULL.\n"); +		return -EFAULT; +	} else { +		pd->reset(lcd->ld); +		mdelay(pd->reset_delay); +	} + +	ret = ams369fg06_ldi_init(lcd); +	if (ret) { +		dev_err(lcd->dev, "failed to initialize ldi.\n"); +		return ret; +	} + +	ret = ams369fg06_ldi_enable(lcd); +	if (ret) { +		dev_err(lcd->dev, "failed to enable ldi.\n"); +		return ret; +	} + +	/* set brightness to current value after power on or resume. */ +	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); +	if (ret) { +		dev_err(lcd->dev, "lcd gamma setting failed.\n"); +		return ret; +	} + +	return 0; +} + +static int ams369fg06_power_off(struct ams369fg06 *lcd) +{ +	int ret = 0; +	struct lcd_platform_data *pd = NULL; + +	pd = lcd->lcd_pd; +	if (!pd) { +		dev_err(lcd->dev, "platform data is NULL\n"); +		return -EFAULT; +	} + +	ret = ams369fg06_ldi_disable(lcd); +	if (ret) { +		dev_err(lcd->dev, "lcd setting failed.\n"); +		return -EIO; +	} + +	mdelay(pd->power_off_delay); + +	if (!pd->power_on) { +		dev_err(lcd->dev, "power_on is NULL.\n"); +		return -EFAULT; +	} else +		pd->power_on(lcd->ld, 0); + +	return 0; +} + +static int ams369fg06_power(struct ams369fg06 *lcd, int power) +{ +	int ret = 0; + +	if (ams369fg06_power_is_on(power) && +		!ams369fg06_power_is_on(lcd->power)) +		ret = ams369fg06_power_on(lcd); +	else if (!ams369fg06_power_is_on(power) && +		ams369fg06_power_is_on(lcd->power)) +		ret = ams369fg06_power_off(lcd); + +	if (!ret) +		lcd->power = power; + +	return ret; +} + +static int ams369fg06_get_power(struct lcd_device *ld) +{ +	struct ams369fg06 *lcd = lcd_get_data(ld); + +	return lcd->power; +} + +static int ams369fg06_set_power(struct lcd_device *ld, int power) +{ +	struct ams369fg06 *lcd = lcd_get_data(ld); + +	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && +		power != FB_BLANK_NORMAL) { +		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); +		return -EINVAL; +	} + +	return ams369fg06_power(lcd, power); +} + +static int ams369fg06_get_brightness(struct backlight_device *bd) +{ +	return bd->props.brightness; +} + +static int ams369fg06_set_brightness(struct backlight_device *bd) +{ +	int ret = 0; +	int brightness = bd->props.brightness; +	struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev); + +	if (brightness < MIN_BRIGHTNESS || +		brightness > bd->props.max_brightness) { +		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n", +			MIN_BRIGHTNESS, MAX_BRIGHTNESS); +		return -EINVAL; +	} + +	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness); +	if (ret) { +		dev_err(&bd->dev, "lcd brightness setting failed.\n"); +		return -EIO; +	} + +	return ret; +} + +static struct lcd_ops ams369fg06_lcd_ops = { +	.get_power = ams369fg06_get_power, +	.set_power = ams369fg06_set_power, +}; + +static const struct backlight_ops ams369fg06_backlight_ops = { +	.get_brightness = ams369fg06_get_brightness, +	.update_status = ams369fg06_set_brightness, +}; + +static int __devinit ams369fg06_probe(struct spi_device *spi) +{ +	int ret = 0; +	struct ams369fg06 *lcd = NULL; +	struct lcd_device *ld = NULL; +	struct backlight_device *bd = NULL; +	struct backlight_properties props; + +	lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL); +	if (!lcd) +		return -ENOMEM; + +	/* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */ +	spi->bits_per_word = 16; + +	ret = spi_setup(spi); +	if (ret < 0) { +		dev_err(&spi->dev, "spi setup failed.\n"); +		goto out_free_lcd; +	} + +	lcd->spi = spi; +	lcd->dev = &spi->dev; + +	lcd->lcd_pd = spi->dev.platform_data; +	if (!lcd->lcd_pd) { +		dev_err(&spi->dev, "platform data is NULL\n"); +		goto out_free_lcd; +	} + +	ld = lcd_device_register("ams369fg06", &spi->dev, lcd, +		&ams369fg06_lcd_ops); +	if (IS_ERR(ld)) { +		ret = PTR_ERR(ld); +		goto out_free_lcd; +	} + +	lcd->ld = ld; + +	memset(&props, 0, sizeof(struct backlight_properties)); +	props.type = BACKLIGHT_RAW; +	props.max_brightness = MAX_BRIGHTNESS; + +	bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd, +		&ams369fg06_backlight_ops, &props); +	if (IS_ERR(bd)) { +		ret =  PTR_ERR(bd); +		goto out_lcd_unregister; +	} + +	bd->props.brightness = DEFAULT_BRIGHTNESS; +	lcd->bd = bd; + +	if (!lcd->lcd_pd->lcd_enabled) { +		/* +		 * if lcd panel was off from bootloader then +		 * current lcd status is powerdown and then +		 * it enables lcd panel. +		 */ +		lcd->power = FB_BLANK_POWERDOWN; + +		ams369fg06_power(lcd, FB_BLANK_UNBLANK); +	} else +		lcd->power = FB_BLANK_UNBLANK; + +	dev_set_drvdata(&spi->dev, lcd); + +	dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); + +	return 0; + +out_lcd_unregister: +	lcd_device_unregister(ld); +out_free_lcd: +	kfree(lcd); +	return ret; +} + +static int __devexit ams369fg06_remove(struct spi_device *spi) +{ +	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + +	ams369fg06_power(lcd, FB_BLANK_POWERDOWN); +	backlight_device_unregister(lcd->bd); +	lcd_device_unregister(lcd->ld); +	kfree(lcd); + +	return 0; +} + +#if defined(CONFIG_PM) +static unsigned int before_power; + +static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg) +{ +	int ret = 0; +	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + +	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + +	before_power = lcd->power; + +	/* +	 * when lcd panel is suspend, lcd panel becomes off +	 * regardless of status. +	 */ +	ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN); + +	return ret; +} + +static int ams369fg06_resume(struct spi_device *spi) +{ +	int ret = 0; +	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + +	/* +	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK +	 * (at that time, before_power is FB_BLANK_UNBLANK) then +	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on. +	 */ +	if (before_power == FB_BLANK_UNBLANK) +		lcd->power = FB_BLANK_POWERDOWN; + +	dev_dbg(&spi->dev, "before_power = %d\n", before_power); + +	ret = ams369fg06_power(lcd, before_power); + +	return ret; +} +#else +#define ams369fg06_suspend	NULL +#define ams369fg06_resume	NULL +#endif + +static void ams369fg06_shutdown(struct spi_device *spi) +{ +	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + +	ams369fg06_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver ams369fg06_driver = { +	.driver = { +		.name	= "ams369fg06", +		.bus	= &spi_bus_type, +		.owner	= THIS_MODULE, +	}, +	.probe		= ams369fg06_probe, +	.remove		= __devexit_p(ams369fg06_remove), +	.shutdown	= ams369fg06_shutdown, +	.suspend	= ams369fg06_suspend, +	.resume		= ams369fg06_resume, +}; + +static int __init ams369fg06_init(void) +{ +	return spi_register_driver(&ams369fg06_driver); +} + +static void __exit ams369fg06_exit(void) +{ +	spi_unregister_driver(&ams369fg06_driver); +} + +module_init(ams369fg06_init); +module_exit(ams369fg06_exit); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("ams369fg06 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c index 7281b2506a67..5934655eb1ff 100644 --- a/drivers/video/backlight/ld9040.c +++ b/drivers/video/backlight/ld9040.c @@ -668,6 +668,7 @@ static int ld9040_probe(struct spi_device *spi)  	struct ld9040 *lcd = NULL;  	struct lcd_device *ld = NULL;  	struct backlight_device *bd = NULL; +	struct backlight_properties props;  	lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);  	if (!lcd) @@ -699,14 +700,17 @@ static int ld9040_probe(struct spi_device *spi)  	lcd->ld = ld; +	memset(&props, 0, sizeof(struct backlight_properties)); +	props.type = BACKLIGHT_RAW; +	props.max_brightness = MAX_BRIGHTNESS; +  	bd = backlight_device_register("ld9040-bl", &spi->dev, -		lcd, &ld9040_backlight_ops, NULL); -	if (IS_ERR(ld)) { -		ret = PTR_ERR(ld); -		goto out_free_lcd; +		lcd, &ld9040_backlight_ops, &props); +	if (IS_ERR(bd)) { +		ret = PTR_ERR(bd); +		goto out_unregister_lcd;  	} -	bd->props.max_brightness = MAX_BRIGHTNESS;  	bd->props.brightness = MAX_BRIGHTNESS;  	lcd->bd = bd; @@ -731,6 +735,8 @@ static int ld9040_probe(struct spi_device *spi)  	dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");  	return 0; +out_unregister_lcd: +	lcd_device_unregister(lcd->ld);  out_free_lcd:  	kfree(lcd);  	return ret; @@ -741,6 +747,7 @@ static int __devexit ld9040_remove(struct spi_device *spi)  	struct ld9040 *lcd = dev_get_drvdata(&spi->dev);  	ld9040_power(lcd, FB_BLANK_POWERDOWN); +	backlight_device_unregister(lcd->bd);  	lcd_device_unregister(lcd->ld);  	kfree(lcd); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index 322040f686c2..694e5aab0d69 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -738,6 +738,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)  	struct s6e63m0 *lcd = NULL;  	struct lcd_device *ld = NULL;  	struct backlight_device *bd = NULL; +	struct backlight_properties props;  	lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);  	if (!lcd) @@ -769,16 +770,18 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)  	lcd->ld = ld; +	memset(&props, 0, sizeof(struct backlight_properties)); +	props.type = BACKLIGHT_RAW; +	props.max_brightness = MAX_BRIGHTNESS; +  	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd, -		&s6e63m0_backlight_ops, NULL); +		&s6e63m0_backlight_ops, &props);  	if (IS_ERR(bd)) {  		ret =  PTR_ERR(bd);  		goto out_lcd_unregister;  	} -	bd->props.max_brightness = MAX_BRIGHTNESS;  	bd->props.brightness = MAX_BRIGHTNESS; -	bd->props.type = BACKLIGHT_RAW;  	lcd->bd = bd;  	/* @@ -840,7 +843,7 @@ static int __devexit s6e63m0_remove(struct spi_device *spi)  }  #if defined(CONFIG_PM) -unsigned int before_power; +static unsigned int before_power;  static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)  { diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c index 47c21fb2c82f..bea53c1a4950 100644 --- a/drivers/video/bf537-lq035.c +++ b/drivers/video/bf537-lq035.c @@ -789,6 +789,7 @@ static int __devinit bfin_lq035_probe(struct platform_device *pdev)  	i2c_add_driver(&ad5280_driver);  	memset(&props, 0, sizeof(props)); +	props.type = BACKLIGHT_RAW;  	props.max_brightness = MAX_BRIGHENESS;  	bl_dev = backlight_device_register("bf537-bl", NULL, NULL,  					   &bfin_lq035fb_bl_ops, &props); diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c index ebda6876d3a9..377dde3d5bfc 100644 --- a/drivers/video/broadsheetfb.c +++ b/drivers/video/broadsheetfb.c @@ -1101,12 +1101,10 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev)  	videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE); -	videomemory = vmalloc(videomemorysize); +	videomemory = vzalloc(videomemorysize);  	if (!videomemory)  		goto err_fb_rel; -	memset(videomemory, 0, videomemorysize); -  	info->screen_base = (char *)videomemory;  	info->fbops = &broadsheetfb_ops; diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index 42fe155aba0e..e02764319ff7 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -303,7 +303,7 @@ static int __devinit cobalt_lcdfb_probe(struct platform_device *dev)  		return -EBUSY;  	} -	info->screen_size = res->end - res->start + 1; +	info->screen_size = resource_size(res);  	info->screen_base = ioremap(res->start, info->screen_size);  	info->fbops = &cobalt_lcd_fbops;  	info->fix = cobalt_lcdfb_fix; diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index c225dcce89e7..9075bea55879 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -709,11 +709,11 @@ static int __init control_of_init(struct device_node *dp)  	/* Map in frame buffer and registers */  	p->fb_orig_base = fb_res.start; -	p->fb_orig_size = fb_res.end - fb_res.start + 1; +	p->fb_orig_size = resource_size(&fb_res);  	/* use the big-endian aperture (??) */  	p->frame_buffer_phys = fb_res.start + 0x800000;  	p->control_regs_phys = reg_res.start; -	p->control_regs_size = reg_res.end - reg_res.start + 1; +	p->control_regs_size = resource_size(®_res);  	if (!p->fb_orig_base ||  	    !request_mem_region(p->fb_orig_base,p->fb_orig_size,"controlfb")) { diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index fb205843c2c7..784139aed079 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c @@ -16,6 +16,8 @@  #include <linux/pci.h>  #include <video/vga.h> +static bool request_mem_succeeded = false; +  static struct fb_var_screeninfo efifb_defined __devinitdata = {  	.activate		= FB_ACTIVATE_NOW,  	.height			= -1, @@ -281,7 +283,9 @@ static void efifb_destroy(struct fb_info *info)  {  	if (info->screen_base)  		iounmap(info->screen_base); -	release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); +	if (request_mem_succeeded) +		release_mem_region(info->apertures->ranges[0].base, +				   info->apertures->ranges[0].size);  	framebuffer_release(info);  } @@ -326,14 +330,13 @@ static int __init efifb_setup(char *options)  	return 0;  } -static int __devinit efifb_probe(struct platform_device *dev) +static int __init efifb_probe(struct platform_device *dev)  {  	struct fb_info *info;  	int err;  	unsigned int size_vmode;  	unsigned int size_remap;  	unsigned int size_total; -	int request_succeeded = 0;  	if (!screen_info.lfb_depth)  		screen_info.lfb_depth = 32; @@ -387,7 +390,7 @@ static int __devinit efifb_probe(struct platform_device *dev)  	efifb_fix.smem_len = size_remap;  	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) { -		request_succeeded = 1; +		request_mem_succeeded = true;  	} else {  		/* We cannot make this fatal. Sometimes this comes from magic  		   spaces our resource handlers simply don't know about */ @@ -413,7 +416,7 @@ static int __devinit efifb_probe(struct platform_device *dev)  	info->apertures->ranges[0].base = efifb_fix.smem_start;  	info->apertures->ranges[0].size = size_remap; -	info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len); +	info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);  	if (!info->screen_base) {  		printk(KERN_ERR "efifb: abort, cannot ioremap video memory "  				"0x%x @ 0x%lx\n", @@ -491,13 +494,12 @@ err_unmap:  err_release_fb:  	framebuffer_release(info);  err_release_mem: -	if (request_succeeded) +	if (request_mem_succeeded)  		release_mem_region(efifb_fix.smem_start, size_total);  	return err;  }  static struct platform_driver efifb_driver = { -	.probe	= efifb_probe,  	.driver	= {  		.name	= "efifb",  	}, @@ -528,13 +530,21 @@ static int __init efifb_init(void)  	if (!screen_info.lfb_linelength)  		return -ENODEV; -	ret = platform_driver_register(&efifb_driver); +	ret = platform_device_register(&efifb_device); +	if (ret) +		return ret; -	if (!ret) { -		ret = platform_device_register(&efifb_device); -		if (ret) -			platform_driver_unregister(&efifb_driver); +	/* +	 * This is not just an optimization.  We will interfere +	 * with a real driver if we get reprobed, so don't allow +	 * it. +	 */ +	ret = platform_driver_probe(&efifb_driver, efifb_probe); +	if (ret) { +		platform_device_unregister(&efifb_device); +		return ret;  	} +  	return ret;  }  module_init(efifb_init); diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index cbdb1bd77c21..40e5f17d1e4b 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -4,7 +4,7 @@   * Framebuffer support for the EP93xx series.   *   * Copyright (C) 2007 Bluewater Systems Ltd - * Author: Ryan Mallon <ryan@bluewatersys.com> + * Author: Ryan Mallon   *   * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com>   * @@ -644,6 +644,6 @@ module_exit(ep93xxfb_exit);  MODULE_DESCRIPTION("EP93XX Framebuffer Driver");  MODULE_ALIAS("platform:ep93xx-fb"); -MODULE_AUTHOR("Ryan Mallon <ryan&bluewatersys.com>, " +MODULE_AUTHOR("Ryan Mallon, "  	      "H Hartley Sweeten <hsweeten@visionengravers.com");  MODULE_LICENSE("GPL"); diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 804000183c5e..32814e8800e0 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -66,19 +66,26 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,  	return 0;  } -int fb_deferred_io_fsync(struct file *file, int datasync) +int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct fb_info *info = file->private_data; +	struct inode *inode = file->f_path.dentry->d_inode; +	int err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err;  	/* Skip if deferred io is compiled-in but disabled on this fbdev */  	if (!info->fbdefio)  		return 0; +	mutex_lock(&inode->i_mutex);  	/* Kill off the delayed work */  	cancel_delayed_work_sync(&info->deferred_work);  	/* Run it immediately */ -	return schedule_delayed_work(&info->deferred_work, 0); +	err = schedule_delayed_work(&info->deferred_work, 0); +	mutex_unlock(&inode->i_mutex); +	return err;  }  EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index bedf5be27f05..0acc7d65aeaa 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -555,8 +555,6 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,  static int fsl_diu_check_var(struct fb_var_screeninfo *var,  				struct fb_info *info)  { -	unsigned long htotal, vtotal; -  	pr_debug("check_var xres: %d\n", var->xres);  	pr_debug("check_var yres: %d\n", var->yres); @@ -635,20 +633,6 @@ static int fsl_diu_check_var(struct fb_var_screeninfo *var,  		break;  	} -	/* If the pixclock is below the minimum spec'd value then set to -	 * refresh rate for 60Hz since this is supported by most monitors. -	 * Refer to Documentation/fb/ for calculations. -	 */ -	if ((var->pixclock < MIN_PIX_CLK) || (var->pixclock > MAX_PIX_CLK)) { -		htotal = var->xres + var->right_margin + var->hsync_len + -		    var->left_margin; -		vtotal = var->yres + var->lower_margin + var->vsync_len + -		    var->upper_margin; -		var->pixclock = (vtotal * htotal * 6UL) / 100UL; -		var->pixclock = KHZ2PICOS(var->pixclock); -		pr_debug("pixclock set for 60Hz refresh = %u ps\n", -			var->pixclock); -	}  	var->height = -1;  	var->width = -1; diff --git a/drivers/video/geode/gx1fb_core.c b/drivers/video/geode/gx1fb_core.c index c6b554f72c6d..5a5d0928df33 100644 --- a/drivers/video/geode/gx1fb_core.c +++ b/drivers/video/geode/gx1fb_core.c @@ -29,7 +29,7 @@ static int  crt_option = 1;  static char panel_option[32] = "";  /* Modes relevant to the GX1 (taken from modedb.c) */ -static const struct fb_videomode __initdata gx1_modedb[] = { +static const struct fb_videomode __devinitdata gx1_modedb[] = {  	/* 640x480-60 VESA */  	{ NULL, 60, 640, 480, 39682,  48, 16, 33, 10, 96, 2,  	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, @@ -195,7 +195,7 @@ static int gx1fb_blank(int blank_mode, struct fb_info *info)  	return par->vid_ops->blank_display(info, blank_mode);  } -static int __init gx1fb_map_video_memory(struct fb_info *info, struct pci_dev *dev) +static int __devinit gx1fb_map_video_memory(struct fb_info *info, struct pci_dev *dev)  {  	struct geodefb_par *par = info->par;  	unsigned gx_base; @@ -268,7 +268,7 @@ static struct fb_ops gx1fb_ops = {  	.fb_imageblit	= cfb_imageblit,  }; -static struct fb_info * __init gx1fb_init_fbinfo(struct device *dev) +static struct fb_info * __devinit gx1fb_init_fbinfo(struct device *dev)  {  	struct geodefb_par *par;  	struct fb_info *info; @@ -318,7 +318,7 @@ static struct fb_info * __init gx1fb_init_fbinfo(struct device *dev)  	return info;  } -static int __init gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) +static int __devinit gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)  {  	struct geodefb_par *par;  	struct fb_info *info; @@ -382,7 +382,7 @@ static int __init gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *  	return ret;  } -static void gx1fb_remove(struct pci_dev *pdev) +static void __devexit gx1fb_remove(struct pci_dev *pdev)  {  	struct fb_info *info = pci_get_drvdata(pdev);  	struct geodefb_par *par = info->par; @@ -441,7 +441,7 @@ static struct pci_driver gx1fb_driver = {  	.name		= "gx1fb",  	.id_table	= gx1fb_id_table,  	.probe		= gx1fb_probe, -	.remove		= gx1fb_remove, +	.remove		= __devexit_p(gx1fb_remove),  };  static int __init gx1fb_init(void) @@ -456,7 +456,7 @@ static int __init gx1fb_init(void)  	return pci_register_driver(&gx1fb_driver);  } -static void __exit gx1fb_cleanup(void) +static void __devexit gx1fb_cleanup(void)  {  	pci_unregister_driver(&gx1fb_driver);  } diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c index 1b94643ecbcf..614251a9af91 100644 --- a/drivers/video/hecubafb.c +++ b/drivers/video/hecubafb.c @@ -231,10 +231,9 @@ static int __devinit hecubafb_probe(struct platform_device *dev)  	videomemorysize = (DPY_W*DPY_H)/8; -	if (!(videomemory = vmalloc(videomemorysize))) -		return retval; - -	memset(videomemory, 0, videomemorysize); +	videomemory = vzalloc(videomemorysize); +	if (!videomemory) +		goto err_videomem_alloc;  	info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev);  	if (!info) @@ -276,6 +275,7 @@ err_fbreg:  	framebuffer_release(info);  err_fballoc:  	vfree(videomemory); +err_videomem_alloc:  	module_put(board->owner);  	return retval;  } diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h index f37de60ecc59..1414b73ac55b 100644 --- a/drivers/video/i810/i810.h +++ b/drivers/video/i810/i810.h @@ -137,7 +137,7 @@  #define DRAM_ON                     0x08              #define DRAM_OFF                    0xE7  #define PG_ENABLE_MASK              0x01 -#define RING_SIZE_MASK              (RINGBUFFER_SIZE - 1); +#define RING_SIZE_MASK              (RINGBUFFER_SIZE - 1)  /* defines for restoring registers partially */  #define ADDR_MAP_MASK               (0x07 << 5) diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index d2ccfd6e662c..f135dbead07d 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -856,10 +856,10 @@ failed_platform_init:  		dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,  			fbi->map_dma);  failed_map: -	clk_put(fbi->clk); -failed_getclock:  	iounmap(fbi->regs);  failed_ioremap: +	clk_put(fbi->clk); +failed_getclock:  	release_mem_region(res->start, resource_size(res));  failed_req:  	kfree(info->pseudo_palette); diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index f70bd63b0187..ee1de3e26dec 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -697,7 +697,7 @@ static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev)  		goto fbrel;  	} -	res_size = 1 + res.end - res.start; +	res_size = resource_size(&res);  	par->res = request_mem_region(res.start, res_size, DRV_NAME);  	if (par->res == NULL) {  		dev_err(dev, "Cannot claim framebuffer/mmio\n"); @@ -787,7 +787,7 @@ static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev)  {  	struct fb_info *fbi = dev_get_drvdata(&ofdev->dev);  	struct mb862xxfb_par *par = fbi->par; -	resource_size_t res_size = 1 + par->res->end - par->res->start; +	resource_size_t res_size = resource_size(par->res);  	unsigned long reg;  	dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index ed64edfd2c43..97d45e5115e2 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -628,12 +628,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev)  	/* we need to add a spare page because our csum caching scheme walks  	 * to the end of the page */  	videomemorysize = PAGE_SIZE + (fw * fh); -	videomemory = vmalloc(videomemorysize); +	videomemory = vzalloc(videomemorysize);  	if (!videomemory)  		goto err_fb_rel; -	memset(videomemory, 0, videomemorysize); -  	info->screen_base = (char __force __iomem *)videomemory;  	info->fbops = &metronomefb_ops; diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 48c3ea8652b6..cb175fe7abc0 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -1128,3 +1128,4 @@ EXPORT_SYMBOL(fb_find_best_mode);  EXPORT_SYMBOL(fb_find_nearest_mode);  EXPORT_SYMBOL(fb_videomode_to_modelist);  EXPORT_SYMBOL(fb_find_mode); +EXPORT_SYMBOL(fb_find_mode_cvt); diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index c3636d55a3c5..243d16f09b8a 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -406,8 +406,7 @@ int mdp_probe(struct platform_device *pdev)  		goto error_get_irq;  	} -	mdp->base = ioremap(resource->start, -			    resource->end - resource->start); +	mdp->base = ioremap(resource->start, resource_size(resource));  	if (mdp->base == 0) {  		printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n");  		ret = -ENOMEM; diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index ec351309e607..c6e3b4fcdd68 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -525,10 +525,9 @@ static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)  		return -ENOMEM;  	}  	fb->fix.smem_start = resource->start; -	fb->fix.smem_len = resource->end - resource->start; -	fbram = ioremap(resource->start, -			resource->end - resource->start); -	if (fbram == 0) { +	fb->fix.smem_len = resource_size(resource); +	fbram = ioremap(resource->start, resource_size(resource)); +	if (fbram == NULL) {  		printk(KERN_ERR "msmfb: cannot allocate fbram!\n");  		return -ENOMEM;  	} diff --git a/drivers/video/nuc900fb.c b/drivers/video/nuc900fb.c index f838d9e277f0..0fff59782e45 100644 --- a/drivers/video/nuc900fb.c +++ b/drivers/video/nuc900fb.c @@ -551,7 +551,7 @@ static int __devinit nuc900fb_probe(struct platform_device *pdev)  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	size = (res->end - res->start) + 1; +	size = resource_size(res);  	fbi->mem = request_mem_region(res->start, size, pdev->name);  	if (fbi->mem == NULL) {  		dev_err(&pdev->dev, "failed to alloc memory region\n"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index fdd5d4ae437d..4e888ac09b3f 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -504,14 +504,18 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)  		return 0;  	r = omapdss_dsi_display_enable(dssdev); -	if (r) -		goto err; +	if (r) { +		dev_err(&dssdev->dev, "failed to enable DSI\n"); +		goto err1; +	}  	omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);  	r = _taal_enable_te(dssdev, true); -	if (r) -		goto err; +	if (r) { +		dev_err(&dssdev->dev, "failed to re-enable TE"); +		goto err2; +	}  	enable_irq(gpio_to_irq(panel_data->ext_te_gpio)); @@ -521,13 +525,15 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)  	return 0; -err: -	dev_err(&dssdev->dev, "exit ULPS failed"); -	r = taal_panel_reset(dssdev); - -	enable_irq(gpio_to_irq(panel_data->ext_te_gpio)); -	td->ulps_enabled = false; +err2: +	dev_err(&dssdev->dev, "failed to exit ULPS"); +	r = taal_panel_reset(dssdev); +	if (!r) { +		enable_irq(gpio_to_irq(panel_data->ext_te_gpio)); +		td->ulps_enabled = false; +	} +err1:  	taal_queue_ulps_work(dssdev);  	return r; @@ -1241,11 +1247,8 @@ static void taal_power_off(struct omap_dss_device *dssdev)  	int r;  	r = taal_dcs_write_0(td, DCS_DISPLAY_OFF); -	if (!r) { +	if (!r)  		r = taal_sleep_in(td); -		/* HACK: wait a bit so that the message goes through */ -		msleep(10); -	}  	if (r) {  		dev_err(&dssdev->dev, @@ -1317,8 +1320,11 @@ static void taal_disable(struct omap_dss_device *dssdev)  	dsi_bus_lock(dssdev);  	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { -		taal_wake_up(dssdev); -		taal_power_off(dssdev); +		int r; + +		r = taal_wake_up(dssdev); +		if (!r) +			taal_power_off(dssdev);  	}  	dsi_bus_unlock(dssdev); @@ -1897,20 +1903,6 @@ err:  	mutex_unlock(&td->lock);  } -static int taal_set_update_mode(struct omap_dss_device *dssdev, -		enum omap_dss_update_mode mode) -{ -	if (mode != OMAP_DSS_UPDATE_MANUAL) -		return -EINVAL; -	return 0; -} - -static enum omap_dss_update_mode taal_get_update_mode( -		struct omap_dss_device *dssdev) -{ -	return OMAP_DSS_UPDATE_MANUAL; -} -  static struct omap_dss_driver taal_driver = {  	.probe		= taal_probe,  	.remove		= __exit_p(taal_remove), @@ -1920,9 +1912,6 @@ static struct omap_dss_driver taal_driver = {  	.suspend	= taal_suspend,  	.resume		= taal_resume, -	.set_update_mode = taal_set_update_mode, -	.get_update_mode = taal_get_update_mode, -  	.update		= taal_update,  	.sync		= taal_sync, diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 6b3e2da11419..0d12524db14b 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -117,18 +117,6 @@ config OMAP2_DSS_MIN_FCK_PER_PCK  	  Max FCK is 173MHz, so this doesn't work if your PCK  	  is very high. -config OMAP2_DSS_SLEEP_BEFORE_RESET -	bool "Sleep 50ms before DSS reset" -	default y -	help -	  For some unknown reason we may get SYNC_LOST errors from the display -	  subsystem at initialization time if we don't sleep before resetting -	  the DSS. See the source (dss.c) for more comments. - -	  However, 50ms is quite long time to sleep, and with some -	  configurations the SYNC_LOST may never happen, so the sleep can -	  be disabled here. -  config OMAP2_DSS_SLEEP_AFTER_VENC_RESET  	bool "Sleep 20ms after VENC reset"  	default y diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 3da426719dd6..76821fefce9a 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -183,8 +183,11 @@ static int omap_dss_probe(struct platform_device *pdev)  		goto err_dss;  	} -	/* keep clocks enabled to prevent context saves/restores during init */ -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	r = dispc_init_platform_driver(); +	if (r) { +		DSSERR("Failed to initialize dispc platform driver\n"); +		goto err_dispc; +	}  	r = rfbi_init_platform_driver();  	if (r) { @@ -192,12 +195,6 @@ static int omap_dss_probe(struct platform_device *pdev)  		goto err_rfbi;  	} -	r = dispc_init_platform_driver(); -	if (r) { -		DSSERR("Failed to initialize dispc platform driver\n"); -		goto err_dispc; -	} -  	r = venc_init_platform_driver();  	if (r) {  		DSSERR("Failed to initialize venc platform driver\n"); @@ -238,8 +235,6 @@ static int omap_dss_probe(struct platform_device *pdev)  			pdata->default_device = dssdev;  	} -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); -  	return 0;  err_register: @@ -268,11 +263,11 @@ static int omap_dss_remove(struct platform_device *pdev)  	dss_uninitialize_debugfs(); +	hdmi_uninit_platform_driver(); +	dsi_uninit_platform_driver();  	venc_uninit_platform_driver(); -	dispc_uninit_platform_driver();  	rfbi_uninit_platform_driver(); -	dsi_uninit_platform_driver(); -	hdmi_uninit_platform_driver(); +	dispc_uninit_platform_driver();  	dss_uninit_platform_driver();  	dss_uninit_overlays(pdev); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 7a9a2e7d9685..0f3961a1ce26 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -33,6 +33,8 @@  #include <linux/workqueue.h>  #include <linux/hardirq.h>  #include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h>  #include <plat/sram.h>  #include <plat/clock.h> @@ -77,6 +79,12 @@ struct dispc_v_coef {  	s8 vc00;  }; +enum omap_burst_size { +	BURST_SIZE_X2 = 0, +	BURST_SIZE_X4 = 1, +	BURST_SIZE_X8 = 2, +}; +  #define REG_GET(idx, start, end) \  	FLD_GET(dispc_read_reg(idx), start, end) @@ -92,7 +100,11 @@ struct dispc_irq_stats {  static struct {  	struct platform_device *pdev;  	void __iomem    *base; + +	int		ctx_loss_cnt; +  	int irq; +	struct clk *dss_clk;  	u32	fifo_size[3]; @@ -102,6 +114,7 @@ static struct {  	u32 error_irqs;  	struct work_struct error_work; +	bool		ctx_valid;  	u32		ctx[DISPC_SZ_REGS / sizeof(u32)];  #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -134,18 +147,34 @@ static inline u32 dispc_read_reg(const u16 idx)  	return __raw_readl(dispc.base + idx);  } +static int dispc_get_ctx_loss_count(void) +{ +	struct device *dev = &dispc.pdev->dev; +	struct omap_display_platform_data *pdata = dev->platform_data; +	struct omap_dss_board_info *board_data = pdata->board_data; +	int cnt; + +	if (!board_data->get_context_loss_count) +		return -ENOENT; + +	cnt = board_data->get_context_loss_count(dev); + +	WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); + +	return cnt; +} +  #define SR(reg) \  	dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)  #define RR(reg) \  	dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)]) -void dispc_save_context(void) +static void dispc_save_context(void)  {  	int i; -	if (cpu_is_omap24xx()) -		return; -	SR(SYSCONFIG); +	DSSDBG("dispc_save_context\n"); +  	SR(IRQENABLE);  	SR(CONTROL);  	SR(CONFIG); @@ -158,7 +187,8 @@ void dispc_save_context(void)  	SR(TIMING_V(OMAP_DSS_CHANNEL_LCD));  	SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));  	SR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); -	SR(GLOBAL_ALPHA); +	if (dss_has_feature(FEAT_GLOBAL_ALPHA)) +		SR(GLOBAL_ALPHA);  	SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));  	SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));  	if (dss_has_feature(FEAT_MGR_LCD2)) { @@ -188,20 +218,25 @@ void dispc_save_context(void)  	SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));  	SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); -	SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); -	SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); -	SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	if (dss_has_feature(FEAT_CPR)) { +		SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); +		SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); +		SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	}  	if (dss_has_feature(FEAT_MGR_LCD2)) { -		SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); -		SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); -		SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); +		if (dss_has_feature(FEAT_CPR)) { +			SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); +			SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); +			SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); +		}  		SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));  		SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));  		SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));  	} -	SR(OVL_PRELOAD(OMAP_DSS_GFX)); +	if (dss_has_feature(FEAT_PRELOAD)) +		SR(OVL_PRELOAD(OMAP_DSS_GFX));  	/* VID1 */  	SR(OVL_BA0(OMAP_DSS_VIDEO1)); @@ -226,8 +261,10 @@ void dispc_save_context(void)  	for (i = 0; i < 5; i++)  		SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); -	for (i = 0; i < 8; i++) -		SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		for (i = 0; i < 8; i++) +			SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		SR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); @@ -248,7 +285,8 @@ void dispc_save_context(void)  	if (dss_has_feature(FEAT_ATTR2))  		SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); -	SR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); +	if (dss_has_feature(FEAT_PRELOAD)) +		SR(OVL_PRELOAD(OMAP_DSS_VIDEO1));  	/* VID2 */  	SR(OVL_BA0(OMAP_DSS_VIDEO2)); @@ -273,8 +311,10 @@ void dispc_save_context(void)  	for (i = 0; i < 5; i++)  		SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); -	for (i = 0; i < 8; i++) -		SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		for (i = 0; i < 8; i++) +			SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		SR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); @@ -295,16 +335,35 @@ void dispc_save_context(void)  	if (dss_has_feature(FEAT_ATTR2))  		SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); -	SR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); +	if (dss_has_feature(FEAT_PRELOAD)) +		SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));  	if (dss_has_feature(FEAT_CORE_CLK_DIV))  		SR(DIVISOR); + +	dispc.ctx_loss_cnt = dispc_get_ctx_loss_count(); +	dispc.ctx_valid = true; + +	DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt);  } -void dispc_restore_context(void) +static void dispc_restore_context(void)  { -	int i; -	RR(SYSCONFIG); +	int i, ctx; + +	DSSDBG("dispc_restore_context\n"); + +	if (!dispc.ctx_valid) +		return; + +	ctx = dispc_get_ctx_loss_count(); + +	if (ctx >= 0 && ctx == dispc.ctx_loss_cnt) +		return; + +	DSSDBG("ctx_loss_count: saved %d, current %d\n", +			dispc.ctx_loss_cnt, ctx); +  	/*RR(IRQENABLE);*/  	/*RR(CONTROL);*/  	RR(CONFIG); @@ -317,7 +376,8 @@ void dispc_restore_context(void)  	RR(TIMING_V(OMAP_DSS_CHANNEL_LCD));  	RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));  	RR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); -	RR(GLOBAL_ALPHA); +	if (dss_has_feature(FEAT_GLOBAL_ALPHA)) +		RR(GLOBAL_ALPHA);  	RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));  	RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));  	if (dss_has_feature(FEAT_MGR_LCD2)) { @@ -347,20 +407,25 @@ void dispc_restore_context(void)  	RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));  	RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); -	RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); -	RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); -	RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	if (dss_has_feature(FEAT_CPR)) { +		RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); +		RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); +		RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	}  	if (dss_has_feature(FEAT_MGR_LCD2)) {  		RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));  		RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));  		RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); -		RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); -		RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); -		RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); +		if (dss_has_feature(FEAT_CPR)) { +			RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); +			RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); +			RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); +		}  	} -	RR(OVL_PRELOAD(OMAP_DSS_GFX)); +	if (dss_has_feature(FEAT_PRELOAD)) +		RR(OVL_PRELOAD(OMAP_DSS_GFX));  	/* VID1 */  	RR(OVL_BA0(OMAP_DSS_VIDEO1)); @@ -385,8 +450,10 @@ void dispc_restore_context(void)  	for (i = 0; i < 5; i++)  		RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); -	for (i = 0; i < 8; i++) -		RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		for (i = 0; i < 8; i++) +			RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		RR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); @@ -407,7 +474,8 @@ void dispc_restore_context(void)  	if (dss_has_feature(FEAT_ATTR2))  		RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); -	RR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); +	if (dss_has_feature(FEAT_PRELOAD)) +		RR(OVL_PRELOAD(OMAP_DSS_VIDEO1));  	/* VID2 */  	RR(OVL_BA0(OMAP_DSS_VIDEO2)); @@ -432,8 +500,10 @@ void dispc_restore_context(void)  	for (i = 0; i < 5; i++)  		RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); -	for (i = 0; i < 8; i++) -		RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		for (i = 0; i < 8; i++) +			RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		RR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); @@ -454,7 +524,8 @@ void dispc_restore_context(void)  	if (dss_has_feature(FEAT_ATTR2))  		RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); -	RR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); +	if (dss_has_feature(FEAT_PRELOAD)) +		RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));  	if (dss_has_feature(FEAT_CORE_CLK_DIV))  		RR(DIVISOR); @@ -471,19 +542,35 @@ void dispc_restore_context(void)  	 * the context is fully restored  	 */  	RR(IRQENABLE); + +	DSSDBG("context restored\n");  }  #undef SR  #undef RR -static inline void enable_clocks(bool enable) +int dispc_runtime_get(void)  { -	if (enable) -		dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); -	else -		dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	int r; + +	DSSDBG("dispc_runtime_get\n"); + +	r = pm_runtime_get_sync(&dispc.pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0;  } +void dispc_runtime_put(void) +{ +	int r; + +	DSSDBG("dispc_runtime_put\n"); + +	r = pm_runtime_put(&dispc.pdev->dev); +	WARN_ON(r < 0); +} + +  bool dispc_go_busy(enum omap_channel channel)  {  	int bit; @@ -505,8 +592,6 @@ void dispc_go(enum omap_channel channel)  	int bit;  	bool enable_bit, go_bit; -	enable_clocks(1); -  	if (channel == OMAP_DSS_CHANNEL_LCD ||  			channel == OMAP_DSS_CHANNEL_LCD2)  		bit = 0; /* LCDENABLE */ @@ -520,7 +605,7 @@ void dispc_go(enum omap_channel channel)  		enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;  	if (!enable_bit) -		goto end; +		return;  	if (channel == OMAP_DSS_CHANNEL_LCD ||  			channel == OMAP_DSS_CHANNEL_LCD2) @@ -535,7 +620,7 @@ void dispc_go(enum omap_channel channel)  	if (go_bit) {  		DSSERR("GO bit not down for channel %d\n", channel); -		goto end; +		return;  	}  	DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : @@ -545,8 +630,6 @@ void dispc_go(enum omap_channel channel)  		REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);  	else  		REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); -end: -	enable_clocks(0);  }  static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) @@ -920,7 +1003,7 @@ static void _dispc_set_color_mode(enum omap_plane plane,  	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);  } -static void _dispc_set_channel_out(enum omap_plane plane, +void dispc_set_channel_out(enum omap_plane plane,  		enum omap_channel channel)  {  	int shift; @@ -967,13 +1050,10 @@ static void _dispc_set_channel_out(enum omap_plane plane,  	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);  } -void dispc_set_burst_size(enum omap_plane plane, +static void dispc_set_burst_size(enum omap_plane plane,  		enum omap_burst_size burst_size)  {  	int shift; -	u32 val; - -	enable_clocks(1);  	switch (plane) {  	case OMAP_DSS_GFX: @@ -988,11 +1068,24 @@ void dispc_set_burst_size(enum omap_plane plane,  		return;  	} -	val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); -	val = FLD_MOD(val, burst_size, shift+1, shift); -	dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); +	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift); +} -	enable_clocks(0); +static void dispc_configure_burst_sizes(void) +{ +	int i; +	const int burst_size = BURST_SIZE_X8; + +	/* Configure burst size always to maximum size */ +	for (i = 0; i < omap_dss_get_num_overlays(); ++i) +		dispc_set_burst_size(i, burst_size); +} + +u32 dispc_get_burst_size(enum omap_plane plane) +{ +	unsigned unit = dss_feat_get_burst_size_unit(); +	/* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ +	return unit * 8;  }  void dispc_enable_gamma_table(bool enable) @@ -1009,6 +1102,40 @@ void dispc_enable_gamma_table(bool enable)  	REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);  } +void dispc_enable_cpr(enum omap_channel channel, bool enable) +{ +	u16 reg; + +	if (channel == OMAP_DSS_CHANNEL_LCD) +		reg = DISPC_CONFIG; +	else if (channel == OMAP_DSS_CHANNEL_LCD2) +		reg = DISPC_CONFIG2; +	else +		return; + +	REG_FLD_MOD(reg, enable, 15, 15); +} + +void dispc_set_cpr_coef(enum omap_channel channel, +		struct omap_dss_cpr_coefs *coefs) +{ +	u32 coef_r, coef_g, coef_b; + +	if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2) +		return; + +	coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) | +		FLD_VAL(coefs->rb, 9, 0); +	coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) | +		FLD_VAL(coefs->gb, 9, 0); +	coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) | +		FLD_VAL(coefs->bb, 9, 0); + +	dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r); +	dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g); +	dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b); +} +  static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)  {  	u32 val; @@ -1029,9 +1156,7 @@ void dispc_enable_replication(enum omap_plane plane, bool enable)  	else  		bit = 10; -	enable_clocks(1);  	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); -	enable_clocks(0);  }  void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) @@ -1039,9 +1164,7 @@ void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height)  	u32 val;  	BUG_ON((width > (1 << 11)) || (height > (1 << 11)));  	val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); -	enable_clocks(1);  	dispc_write_reg(DISPC_SIZE_MGR(channel), val); -	enable_clocks(0);  }  void dispc_set_digit_size(u16 width, u16 height) @@ -1049,9 +1172,7 @@ void dispc_set_digit_size(u16 width, u16 height)  	u32 val;  	BUG_ON((width > (1 << 11)) || (height > (1 << 11)));  	val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); -	enable_clocks(1);  	dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); -	enable_clocks(0);  }  static void dispc_read_plane_fifo_sizes(void) @@ -1059,18 +1180,17 @@ static void dispc_read_plane_fifo_sizes(void)  	u32 size;  	int plane;  	u8 start, end; +	u32 unit; -	enable_clocks(1); +	unit = dss_feat_get_buffer_size_unit();  	dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);  	for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { -		size = FLD_GET(dispc_read_reg(DISPC_OVL_FIFO_SIZE_STATUS(plane)), -			start, end); +		size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end); +		size *= unit;  		dispc.fifo_size[plane] = size;  	} - -	enable_clocks(0);  }  u32 dispc_get_plane_fifo_size(enum omap_plane plane) @@ -1078,15 +1198,22 @@ u32 dispc_get_plane_fifo_size(enum omap_plane plane)  	return dispc.fifo_size[plane];  } -void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) +void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)  {  	u8 hi_start, hi_end, lo_start, lo_end; +	u32 unit; + +	unit = dss_feat_get_buffer_size_unit(); + +	WARN_ON(low % unit != 0); +	WARN_ON(high % unit != 0); + +	low /= unit; +	high /= unit;  	dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);  	dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); -	enable_clocks(1); -  	DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",  			plane,  			REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), @@ -1098,18 +1225,12 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)  	dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),  			FLD_VAL(high, hi_start, hi_end) |  			FLD_VAL(low, lo_start, lo_end)); - -	enable_clocks(0);  }  void dispc_enable_fifomerge(bool enable)  { -	enable_clocks(1); -  	DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");  	REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); - -	enable_clocks(0);  }  static void _dispc_set_fir(enum omap_plane plane, @@ -1729,14 +1850,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,  	return dispc_pclk_rate(channel) * vf * hf;  } -void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) -{ -	enable_clocks(1); -	_dispc_set_channel_out(plane, channel_out); -	enable_clocks(0); -} - -static int _dispc_setup_plane(enum omap_plane plane, +int dispc_setup_plane(enum omap_plane plane,  		u32 paddr, u16 screen_width,  		u16 pos_x, u16 pos_y,  		u16 width, u16 height, @@ -1744,7 +1858,7 @@ static int _dispc_setup_plane(enum omap_plane plane,  		enum omap_color_mode color_mode,  		bool ilace,  		enum omap_dss_rotation_type rotation_type, -		u8 rotation, int mirror, +		u8 rotation, bool mirror,  		u8 global_alpha, u8 pre_mult_alpha,  		enum omap_channel channel, u32 puv_addr)  { @@ -1758,6 +1872,14 @@ static int _dispc_setup_plane(enum omap_plane plane,  	u16 frame_height = height;  	unsigned int field_offset = 0; +	DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " +	       "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", +	       plane, paddr, screen_width, pos_x, pos_y, +	       width, height, +	       out_width, out_height, +	       ilace, color_mode, +	       rotation, mirror, channel); +  	if (paddr == 0)  		return -EINVAL; @@ -1903,9 +2025,13 @@ static int _dispc_setup_plane(enum omap_plane plane,  	return 0;  } -static void _dispc_enable_plane(enum omap_plane plane, bool enable) +int dispc_enable_plane(enum omap_plane plane, bool enable)  { +	DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); +  	REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); + +	return 0;  }  static void dispc_disable_isr(void *data, u32 mask) @@ -1929,8 +2055,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)  	int r;  	u32 irq; -	enable_clocks(1); -  	/* When we disable LCD output, we need to wait until frame is done.  	 * Otherwise the DSS is still working, and turning off the clocks  	 * prevents DSS from going to OFF mode */ @@ -1964,8 +2088,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)  		if (r)  			DSSERR("failed to unregister FRAMEDONE isr\n");  	} - -	enable_clocks(0);  }  static void _enable_digit_out(bool enable) @@ -1978,12 +2100,8 @@ static void dispc_enable_digit_out(bool enable)  	struct completion frame_done_completion;  	int r; -	enable_clocks(1); - -	if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { -		enable_clocks(0); +	if (REG_GET(DISPC_CONTROL, 1, 1) == enable)  		return; -	}  	if (enable) {  		unsigned long flags; @@ -2035,8 +2153,6 @@ static void dispc_enable_digit_out(bool enable)  		_omap_dispc_set_irqs();  		spin_unlock_irqrestore(&dispc.irq_lock, flags);  	} - -	enable_clocks(0);  }  bool dispc_is_channel_enabled(enum omap_channel channel) @@ -2067,9 +2183,7 @@ void dispc_lcd_enable_signal_polarity(bool act_high)  	if (!dss_has_feature(FEAT_LCDENABLEPOL))  		return; -	enable_clocks(1);  	REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); -	enable_clocks(0);  }  void dispc_lcd_enable_signal(bool enable) @@ -2077,9 +2191,7 @@ void dispc_lcd_enable_signal(bool enable)  	if (!dss_has_feature(FEAT_LCDENABLESIGNAL))  		return; -	enable_clocks(1);  	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); -	enable_clocks(0);  }  void dispc_pck_free_enable(bool enable) @@ -2087,19 +2199,15 @@ void dispc_pck_free_enable(bool enable)  	if (!dss_has_feature(FEAT_PCKFREEENABLE))  		return; -	enable_clocks(1);  	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); -	enable_clocks(0);  }  void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable)  { -	enable_clocks(1);  	if (channel == OMAP_DSS_CHANNEL_LCD2)  		REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);  	else  		REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); -	enable_clocks(0);  } @@ -2122,27 +2230,21 @@ void dispc_set_lcd_display_type(enum omap_channel channel,  		return;  	} -	enable_clocks(1);  	if (channel == OMAP_DSS_CHANNEL_LCD2)  		REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);  	else  		REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); -	enable_clocks(0);  }  void dispc_set_loadmode(enum omap_dss_load_mode mode)  { -	enable_clocks(1);  	REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); -	enable_clocks(0);  }  void dispc_set_default_color(enum omap_channel channel, u32 color)  { -	enable_clocks(1);  	dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); -	enable_clocks(0);  }  u32 dispc_get_default_color(enum omap_channel channel) @@ -2153,9 +2255,7 @@ u32 dispc_get_default_color(enum omap_channel channel)  		channel != OMAP_DSS_CHANNEL_LCD &&  		channel != OMAP_DSS_CHANNEL_LCD2); -	enable_clocks(1);  	l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel)); -	enable_clocks(0);  	return l;  } @@ -2164,7 +2264,6 @@ void dispc_set_trans_key(enum omap_channel ch,  		enum omap_dss_trans_key_type type,  		u32 trans_key)  { -	enable_clocks(1);  	if (ch == OMAP_DSS_CHANNEL_LCD)  		REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);  	else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2173,14 +2272,12 @@ void dispc_set_trans_key(enum omap_channel ch,  		REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);  	dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); -	enable_clocks(0);  }  void dispc_get_trans_key(enum omap_channel ch,  		enum omap_dss_trans_key_type *type,  		u32 *trans_key)  { -	enable_clocks(1);  	if (type) {  		if (ch == OMAP_DSS_CHANNEL_LCD)  			*type = REG_GET(DISPC_CONFIG, 11, 11); @@ -2194,33 +2291,28 @@ void dispc_get_trans_key(enum omap_channel ch,  	if (trans_key)  		*trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch)); -	enable_clocks(0);  }  void dispc_enable_trans_key(enum omap_channel ch, bool enable)  { -	enable_clocks(1);  	if (ch == OMAP_DSS_CHANNEL_LCD)  		REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);  	else if (ch == OMAP_DSS_CHANNEL_DIGIT)  		REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);  	else /* OMAP_DSS_CHANNEL_LCD2 */  		REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); -	enable_clocks(0);  }  void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)  {  	if (!dss_has_feature(FEAT_GLOBAL_ALPHA))  		return; -	enable_clocks(1);  	if (ch == OMAP_DSS_CHANNEL_LCD)  		REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);  	else if (ch == OMAP_DSS_CHANNEL_DIGIT)  		REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);  	else /* OMAP_DSS_CHANNEL_LCD2 */  		REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18); -	enable_clocks(0);  }  bool dispc_alpha_blending_enabled(enum omap_channel ch)  { @@ -2229,7 +2321,6 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch)  	if (!dss_has_feature(FEAT_GLOBAL_ALPHA))  		return false; -	enable_clocks(1);  	if (ch == OMAP_DSS_CHANNEL_LCD)  		enabled = REG_GET(DISPC_CONFIG, 18, 18);  	else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2238,7 +2329,6 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch)  		enabled = REG_GET(DISPC_CONFIG2, 18, 18);  	else  		BUG(); -	enable_clocks(0);  	return enabled;  } @@ -2248,7 +2338,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch)  {  	bool enabled; -	enable_clocks(1);  	if (ch == OMAP_DSS_CHANNEL_LCD)  		enabled = REG_GET(DISPC_CONFIG, 10, 10);  	else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2257,7 +2346,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch)  		enabled = REG_GET(DISPC_CONFIG2, 10, 10);  	else  		BUG(); -	enable_clocks(0);  	return enabled;  } @@ -2285,12 +2373,10 @@ void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines)  		return;  	} -	enable_clocks(1);  	if (channel == OMAP_DSS_CHANNEL_LCD2)  		REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);  	else  		REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); -	enable_clocks(0);  }  void dispc_set_parallel_interface_mode(enum omap_channel channel, @@ -2322,8 +2408,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel,  		return;  	} -	enable_clocks(1); -  	if (channel == OMAP_DSS_CHANNEL_LCD2) {  		l = dispc_read_reg(DISPC_CONTROL2);  		l = FLD_MOD(l, stallmode, 11, 11); @@ -2335,8 +2419,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel,  		l = FLD_MOD(l, gpout1, 16, 16);  		dispc_write_reg(DISPC_CONTROL, l);  	} - -	enable_clocks(0);  }  static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, @@ -2389,10 +2471,8 @@ static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw,  			FLD_VAL(vbp, 31, 20);  	} -	enable_clocks(1);  	dispc_write_reg(DISPC_TIMING_H(channel), timing_h);  	dispc_write_reg(DISPC_TIMING_V(channel), timing_v); -	enable_clocks(0);  }  /* change name to mode? */ @@ -2435,10 +2515,8 @@ static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div,  	BUG_ON(lck_div < 1);  	BUG_ON(pck_div < 2); -	enable_clocks(1);  	dispc_write_reg(DISPC_DIVISORo(channel),  			FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); -	enable_clocks(0);  }  static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div, @@ -2457,7 +2535,7 @@ unsigned long dispc_fclk_rate(void)  	switch (dss_get_dispc_clk_source()) {  	case OMAP_DSS_CLK_SRC_FCK: -		r = dss_clk_get_rate(DSS_CLK_FCK); +		r = clk_get_rate(dispc.dss_clk);  		break;  	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:  		dsidev = dsi_get_dsidev_from_id(0); @@ -2487,7 +2565,7 @@ unsigned long dispc_lclk_rate(enum omap_channel channel)  	switch (dss_get_lcd_clk_source(channel)) {  	case OMAP_DSS_CLK_SRC_FCK: -		r = dss_clk_get_rate(DSS_CLK_FCK); +		r = clk_get_rate(dispc.dss_clk);  		break;  	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:  		dsidev = dsi_get_dsidev_from_id(0); @@ -2526,7 +2604,8 @@ void dispc_dump_clocks(struct seq_file *s)  	enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();  	enum omap_dss_clk_source lcd_clk_src; -	enable_clocks(1); +	if (dispc_runtime_get()) +		return;  	seq_printf(s, "- DISPC -\n"); @@ -2574,7 +2653,8 @@ void dispc_dump_clocks(struct seq_file *s)  		seq_printf(s, "pck\t\t%-16lupck div\t%u\n",  				dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);  	} -	enable_clocks(0); + +	dispc_runtime_put();  }  #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -2629,7 +2709,8 @@ void dispc_dump_regs(struct seq_file *s)  {  #define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	if (dispc_runtime_get()) +		return;  	DUMPREG(DISPC_REVISION);  	DUMPREG(DISPC_SYSCONFIG); @@ -2649,7 +2730,8 @@ void dispc_dump_regs(struct seq_file *s)  	DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD));  	DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD));  	DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD)); -	DUMPREG(DISPC_GLOBAL_ALPHA); +	if (dss_has_feature(FEAT_GLOBAL_ALPHA)) +		DUMPREG(DISPC_GLOBAL_ALPHA);  	DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));  	DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD));  	if (dss_has_feature(FEAT_MGR_LCD2)) { @@ -2680,20 +2762,25 @@ void dispc_dump_regs(struct seq_file *s)  	DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));  	DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); -	DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); -	DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); -	DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	if (dss_has_feature(FEAT_CPR)) { +		DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); +		DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); +		DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); +	}  	if (dss_has_feature(FEAT_MGR_LCD2)) {  		DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));  		DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));  		DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); -		DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); -		DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); -		DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); +		if (dss_has_feature(FEAT_CPR)) { +			DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); +			DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); +			DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); +		}  	} -	DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX)); +	if (dss_has_feature(FEAT_PRELOAD)) +		DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));  	DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1));  	DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1)); @@ -2744,14 +2831,16 @@ void dispc_dump_regs(struct seq_file *s)  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2));  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3));  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7)); +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1)); @@ -2812,14 +2901,17 @@ void dispc_dump_regs(struct seq_file *s)  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2));  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3));  	DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6)); -	DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7)); + +	if (dss_has_feature(FEAT_FIR_COEF_V)) { +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6)); +		DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7)); +	}  	if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {  		DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2)); @@ -2858,10 +2950,12 @@ void dispc_dump_regs(struct seq_file *s)  	if (dss_has_feature(FEAT_ATTR2))  		DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); -	DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1)); -	DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2)); +	if (dss_has_feature(FEAT_PRELOAD)) { +		DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1)); +		DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2)); +	} -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dispc_runtime_put();  #undef DUMPREG  } @@ -2882,9 +2976,7 @@ static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf,  	l |= FLD_VAL(acbi, 11, 8);  	l |= FLD_VAL(acb, 7, 0); -	enable_clocks(1);  	dispc_write_reg(DISPC_POL_FREQ(channel), l); -	enable_clocks(0);  }  void dispc_set_pol_freq(enum omap_channel channel, @@ -3005,15 +3097,11 @@ static void _omap_dispc_set_irqs(void)  		mask |= isr_data->mask;  	} -	enable_clocks(1); -  	old_mask = dispc_read_reg(DISPC_IRQENABLE);  	/* clear the irqstatus for newly enabled irqs */  	dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask);  	dispc_write_reg(DISPC_IRQENABLE, mask); - -	enable_clocks(0);  }  int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) @@ -3522,13 +3610,6 @@ static void _omap_dispc_initial_config(void)  {  	u32 l; -	l = dispc_read_reg(DISPC_SYSCONFIG); -	l = FLD_MOD(l, 2, 13, 12);	/* MIDLEMODE: smart standby */ -	l = FLD_MOD(l, 2, 4, 3);	/* SIDLEMODE: smart idle */ -	l = FLD_MOD(l, 1, 2, 2);	/* ENWAKEUP */ -	l = FLD_MOD(l, 1, 0, 0);	/* AUTOIDLE */ -	dispc_write_reg(DISPC_SYSCONFIG, l); -  	/* Exclusively enable DISPC_CORE_CLK and set divider to 1 */  	if (dss_has_feature(FEAT_CORE_CLK_DIV)) {  		l = dispc_read_reg(DISPC_DIVISOR); @@ -3552,58 +3633,8 @@ static void _omap_dispc_initial_config(void)  	dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);  	dispc_read_plane_fifo_sizes(); -} -int dispc_enable_plane(enum omap_plane plane, bool enable) -{ -	DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); - -	enable_clocks(1); -	_dispc_enable_plane(plane, enable); -	enable_clocks(0); - -	return 0; -} - -int dispc_setup_plane(enum omap_plane plane, -		       u32 paddr, u16 screen_width, -		       u16 pos_x, u16 pos_y, -		       u16 width, u16 height, -		       u16 out_width, u16 out_height, -		       enum omap_color_mode color_mode, -		       bool ilace, -		       enum omap_dss_rotation_type rotation_type, -		       u8 rotation, bool mirror, u8 global_alpha, -		       u8 pre_mult_alpha, enum omap_channel channel, -		       u32 puv_addr) -{ -	int r = 0; - -	DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d, %d, %dx%d -> " -	       "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", -	       plane, paddr, screen_width, pos_x, pos_y, -	       width, height, -	       out_width, out_height, -	       ilace, color_mode, -	       rotation, mirror, channel); - -	enable_clocks(1); - -	r = _dispc_setup_plane(plane, -			   paddr, screen_width, -			   pos_x, pos_y, -			   width, height, -			   out_width, out_height, -			   color_mode, ilace, -			   rotation_type, -			   rotation, mirror, -			   global_alpha, -			   pre_mult_alpha, -			   channel, puv_addr); - -	enable_clocks(0); - -	return r; +	dispc_configure_burst_sizes();  }  /* DISPC HW IP initialisation */ @@ -3612,9 +3643,19 @@ static int omap_dispchw_probe(struct platform_device *pdev)  	u32 rev;  	int r = 0;  	struct resource *dispc_mem; +	struct clk *clk;  	dispc.pdev = pdev; +	clk = clk_get(&pdev->dev, "fck"); +	if (IS_ERR(clk)) { +		DSSERR("can't get fck\n"); +		r = PTR_ERR(clk); +		goto err_get_clk; +	} + +	dispc.dss_clk = clk; +  	spin_lock_init(&dispc.irq_lock);  #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -3628,62 +3669,103 @@ static int omap_dispchw_probe(struct platform_device *pdev)  	if (!dispc_mem) {  		DSSERR("can't get IORESOURCE_MEM DISPC\n");  		r = -EINVAL; -		goto fail0; +		goto err_ioremap;  	}  	dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem));  	if (!dispc.base) {  		DSSERR("can't ioremap DISPC\n");  		r = -ENOMEM; -		goto fail0; +		goto err_ioremap;  	}  	dispc.irq = platform_get_irq(dispc.pdev, 0);  	if (dispc.irq < 0) {  		DSSERR("platform_get_irq failed\n");  		r = -ENODEV; -		goto fail1; +		goto err_irq;  	}  	r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED,  		"OMAP DISPC", dispc.pdev);  	if (r < 0) {  		DSSERR("request_irq failed\n"); -		goto fail1; +		goto err_irq;  	} -	enable_clocks(1); +	pm_runtime_enable(&pdev->dev); + +	r = dispc_runtime_get(); +	if (r) +		goto err_runtime_get;  	_omap_dispc_initial_config();  	_omap_dispc_initialize_irq(); -	dispc_save_context(); -  	rev = dispc_read_reg(DISPC_REVISION);  	dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",  	       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); -	enable_clocks(0); +	dispc_runtime_put();  	return 0; -fail1: + +err_runtime_get: +	pm_runtime_disable(&pdev->dev); +	free_irq(dispc.irq, dispc.pdev); +err_irq:  	iounmap(dispc.base); -fail0: +err_ioremap: +	clk_put(dispc.dss_clk); +err_get_clk:  	return r;  }  static int omap_dispchw_remove(struct platform_device *pdev)  { +	pm_runtime_disable(&pdev->dev); + +	clk_put(dispc.dss_clk); +  	free_irq(dispc.irq, dispc.pdev);  	iounmap(dispc.base);  	return 0;  } +static int dispc_runtime_suspend(struct device *dev) +{ +	dispc_save_context(); +	clk_disable(dispc.dss_clk); +	dss_runtime_put(); + +	return 0; +} + +static int dispc_runtime_resume(struct device *dev) +{ +	int r; + +	r = dss_runtime_get(); +	if (r < 0) +		return r; + +	clk_enable(dispc.dss_clk); +	dispc_restore_context(); + +	return 0; +} + +static const struct dev_pm_ops dispc_pm_ops = { +	.runtime_suspend = dispc_runtime_suspend, +	.runtime_resume = dispc_runtime_resume, +}; +  static struct platform_driver omap_dispchw_driver = {  	.probe          = omap_dispchw_probe,  	.remove         = omap_dispchw_remove,  	.driver         = {  		.name   = "omapdss_dispc",  		.owner  = THIS_MODULE, +		.pm	= &dispc_pm_ops,  	},  }; diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index c2dfc8c50057..94495e45ec5a 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -29,6 +29,7 @@  #include <video/omapdss.h>  #include "dss.h" +#include "dss_features.h"  static ssize_t display_enabled_show(struct device *dev,  		struct device_attribute *attr, char *buf) @@ -65,48 +66,6 @@ static ssize_t display_enabled_store(struct device *dev,  	return size;  } -static ssize_t display_upd_mode_show(struct device *dev, -		struct device_attribute *attr, char *buf) -{ -	struct omap_dss_device *dssdev = to_dss_device(dev); -	enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; -	if (dssdev->driver->get_update_mode) -		mode = dssdev->driver->get_update_mode(dssdev); -	return snprintf(buf, PAGE_SIZE, "%d\n", mode); -} - -static ssize_t display_upd_mode_store(struct device *dev, -		struct device_attribute *attr, -		const char *buf, size_t size) -{ -	struct omap_dss_device *dssdev = to_dss_device(dev); -	int val, r; -	enum omap_dss_update_mode mode; - -	if (!dssdev->driver->set_update_mode) -		return -EINVAL; - -	r = kstrtoint(buf, 0, &val); -	if (r) -		return r; - -	switch (val) { -	case OMAP_DSS_UPDATE_DISABLED: -	case OMAP_DSS_UPDATE_AUTO: -	case OMAP_DSS_UPDATE_MANUAL: -		mode = (enum omap_dss_update_mode)val; -		break; -	default: -		return -EINVAL; -	} - -	r = dssdev->driver->set_update_mode(dssdev, mode); -	if (r) -		return r; - -	return size; -} -  static ssize_t display_tear_show(struct device *dev,  		struct device_attribute *attr, char *buf)  { @@ -294,8 +253,6 @@ static ssize_t display_wss_store(struct device *dev,  static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,  		display_enabled_show, display_enabled_store); -static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, -		display_upd_mode_show, display_upd_mode_store);  static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,  		display_tear_show, display_tear_store);  static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, @@ -309,7 +266,6 @@ static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,  static struct device_attribute *display_sysfs_attrs[] = {  	&dev_attr_enabled, -	&dev_attr_update_mode,  	&dev_attr_tear_elim,  	&dev_attr_timings,  	&dev_attr_rotate, @@ -327,16 +283,13 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev,  EXPORT_SYMBOL(omapdss_default_get_resolution);  void default_get_overlay_fifo_thresholds(enum omap_plane plane, -		u32 fifo_size, enum omap_burst_size *burst_size, +		u32 fifo_size, u32 burst_size,  		u32 *fifo_low, u32 *fifo_high)  { -	unsigned burst_size_bytes; - -	*burst_size = OMAP_DSS_BURST_16x32; -	burst_size_bytes = 16 * 32 / 8; +	unsigned buf_unit = dss_feat_get_buffer_size_unit(); -	*fifo_high = fifo_size - 1; -	*fifo_low = fifo_size - burst_size_bytes; +	*fifo_high = fifo_size - buf_unit; +	*fifo_low = fifo_size - burst_size;  }  int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index ff6bd30132df..f053b180ecd7 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -23,7 +23,6 @@  #define DSS_SUBSYS_NAME "DPI"  #include <linux/kernel.h> -#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/err.h>  #include <linux/errno.h> @@ -130,8 +129,6 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)  	bool is_tft;  	int r = 0; -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); -  	dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config,  			dssdev->panel.acbi, dssdev->panel.acb); @@ -144,7 +141,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)  		r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,  				&fck, &lck_div, &pck_div);  	if (r) -		goto err0; +		return r;  	pck = fck / lck_div / pck_div / 1000; @@ -158,12 +155,10 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)  	dispc_set_lcd_timings(dssdev->manager->id, t); -err0: -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); -	return r; +	return 0;  } -static int dpi_basic_init(struct omap_dss_device *dssdev) +static void dpi_basic_init(struct omap_dss_device *dssdev)  {  	bool is_tft; @@ -175,8 +170,6 @@ static int dpi_basic_init(struct omap_dss_device *dssdev)  			OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);  	dispc_set_tft_data_lines(dssdev->manager->id,  			dssdev->phy.dpi.data_lines); - -	return 0;  }  int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) @@ -186,31 +179,38 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)  	r = omap_dss_start_device(dssdev);  	if (r) {  		DSSERR("failed to start device\n"); -		goto err0; +		goto err_start_dev;  	}  	if (cpu_is_omap34xx()) {  		r = regulator_enable(dpi.vdds_dsi_reg);  		if (r) -			goto err1; +			goto err_reg_enable;  	} -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	r = dss_runtime_get(); +	if (r) +		goto err_get_dss; -	r = dpi_basic_init(dssdev); +	r = dispc_runtime_get();  	if (r) -		goto err2; +		goto err_get_dispc; + +	dpi_basic_init(dssdev);  	if (dpi_use_dsi_pll(dssdev)) { -		dss_clk_enable(DSS_CLK_SYSCK); +		r = dsi_runtime_get(dpi.dsidev); +		if (r) +			goto err_get_dsi; +  		r = dsi_pll_init(dpi.dsidev, 0, 1);  		if (r) -			goto err3; +			goto err_dsi_pll_init;  	}  	r = dpi_set_mode(dssdev);  	if (r) -		goto err4; +		goto err_set_mode;  	mdelay(2); @@ -218,19 +218,22 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)  	return 0; -err4: +err_set_mode:  	if (dpi_use_dsi_pll(dssdev))  		dsi_pll_uninit(dpi.dsidev, true); -err3: +err_dsi_pll_init:  	if (dpi_use_dsi_pll(dssdev)) -		dss_clk_disable(DSS_CLK_SYSCK); -err2: -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +		dsi_runtime_put(dpi.dsidev); +err_get_dsi: +	dispc_runtime_put(); +err_get_dispc: +	dss_runtime_put(); +err_get_dss:  	if (cpu_is_omap34xx())  		regulator_disable(dpi.vdds_dsi_reg); -err1: +err_reg_enable:  	omap_dss_stop_device(dssdev); -err0: +err_start_dev:  	return r;  }  EXPORT_SYMBOL(omapdss_dpi_display_enable); @@ -242,10 +245,11 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)  	if (dpi_use_dsi_pll(dssdev)) {  		dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);  		dsi_pll_uninit(dpi.dsidev, true); -		dss_clk_disable(DSS_CLK_SYSCK); +		dsi_runtime_put(dpi.dsidev);  	} -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dispc_runtime_put(); +	dss_runtime_put();  	if (cpu_is_omap34xx())  		regulator_disable(dpi.vdds_dsi_reg); @@ -257,11 +261,26 @@ EXPORT_SYMBOL(omapdss_dpi_display_disable);  void dpi_set_timings(struct omap_dss_device *dssdev,  			struct omap_video_timings *timings)  { +	int r; +  	DSSDBG("dpi_set_timings\n");  	dssdev->panel.timings = *timings;  	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { +		r = dss_runtime_get(); +		if (r) +			return; + +		r = dispc_runtime_get(); +		if (r) { +			dss_runtime_put(); +			return; +		} +  		dpi_set_mode(dssdev);  		dispc_go(dssdev->manager->id); + +		dispc_runtime_put(); +		dss_runtime_put();  	}  }  EXPORT_SYMBOL(dpi_set_timings); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 345757cfcbee..7adbbeb84334 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -36,6 +36,7 @@  #include <linux/sched.h>  #include <linux/slab.h>  #include <linux/debugfs.h> +#include <linux/pm_runtime.h>  #include <video/omapdss.h>  #include <plat/clock.h> @@ -267,8 +268,12 @@ struct dsi_isr_tables {  struct dsi_data {  	struct platform_device *pdev;  	void __iomem	*base; +  	int irq; +	struct clk *dss_clk; +	struct clk *sys_clk; +  	void (*dsi_mux_pads)(bool enable);  	struct dsi_clock_info current_cinfo; @@ -389,15 +394,6 @@ static inline u32 dsi_read_reg(struct platform_device *dsidev,  	return __raw_readl(dsi->base + idx.idx);  } - -void dsi_save_context(void) -{ -} - -void dsi_restore_context(void) -{ -} -  void dsi_bus_lock(struct omap_dss_device *dssdev)  {  	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -493,9 +489,18 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name)  			total_bytes * 1000 / total_us);  }  #else -#define dsi_perf_mark_setup(x) -#define dsi_perf_mark_start(x) -#define dsi_perf_show(x, y) +static inline void dsi_perf_mark_setup(struct platform_device *dsidev) +{ +} + +static inline void dsi_perf_mark_start(struct platform_device *dsidev) +{ +} + +static inline void dsi_perf_show(struct platform_device *dsidev, +		const char *name) +{ +}  #endif  static void print_irq_status(u32 status) @@ -1039,13 +1044,27 @@ static u32 dsi_get_errors(struct platform_device *dsidev)  	return e;  } -/* DSI func clock. this could also be dsi_pll_hsdiv_dsi_clk */ -static inline void enable_clocks(bool enable) +int dsi_runtime_get(struct platform_device *dsidev)  { -	if (enable) -		dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); -	else -		dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	int r; +	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + +	DSSDBG("dsi_runtime_get\n"); + +	r = pm_runtime_get_sync(&dsi->pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0; +} + +void dsi_runtime_put(struct platform_device *dsidev) +{ +	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); +	int r; + +	DSSDBG("dsi_runtime_put\n"); + +	r = pm_runtime_put(&dsi->pdev->dev); +	WARN_ON(r < 0);  }  /* source clock for DSI PLL. this could also be PCLKFREE */ @@ -1055,9 +1074,9 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,  	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);  	if (enable) -		dss_clk_enable(DSS_CLK_SYSCK); +		clk_enable(dsi->sys_clk);  	else -		dss_clk_disable(DSS_CLK_SYSCK); +		clk_disable(dsi->sys_clk);  	if (enable && dsi->pll_locked) {  		if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) @@ -1150,10 +1169,11 @@ static unsigned long dsi_fclk_rate(struct platform_device *dsidev)  {  	unsigned long r;  	int dsi_module = dsi_get_dsidev_id(dsidev); +	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);  	if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) {  		/* DSI FCLK source is DSS_CLK_FCK */ -		r = dss_clk_get_rate(DSS_CLK_FCK); +		r = clk_get_rate(dsi->dss_clk);  	} else {  		/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */  		r = dsi_get_pll_hsdiv_dsi_rate(dsidev); @@ -1262,7 +1282,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev,  		return -EINVAL;  	if (cinfo->use_sys_clk) { -		cinfo->clkin = dss_clk_get_rate(DSS_CLK_SYSCK); +		cinfo->clkin = clk_get_rate(dsi->sys_clk);  		/* XXX it is unclear if highfreq should be used  		 * with DSS_SYS_CLK source also */  		cinfo->highfreq = 0; @@ -1311,7 +1331,7 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,  	int match = 0;  	unsigned long dss_sys_clk, max_dss_fck; -	dss_sys_clk = dss_clk_get_rate(DSS_CLK_SYSCK); +	dss_sys_clk = clk_get_rate(dsi->sys_clk);  	max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); @@ -1601,7 +1621,6 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,  		dsi->vdds_dsi_reg = vdds_dsi;  	} -	enable_clocks(1);  	dsi_enable_pll_clock(dsidev, 1);  	/*  	 * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. @@ -1653,7 +1672,6 @@ err1:  	}  err0:  	dsi_disable_scp_clk(dsidev); -	enable_clocks(0);  	dsi_enable_pll_clock(dsidev, 0);  	return r;  } @@ -1671,7 +1689,6 @@ void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)  	}  	dsi_disable_scp_clk(dsidev); -	enable_clocks(0);  	dsi_enable_pll_clock(dsidev, 0);  	DSSDBG("PLL uninit done\n"); @@ -1688,7 +1705,8 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,  	dispc_clk_src = dss_get_dispc_clk_source();  	dsi_clk_src = dss_get_dsi_clk_source(dsi_module); -	enable_clocks(1); +	if (dsi_runtime_get(dsidev)) +		return;  	seq_printf(s,	"- DSI%d PLL -\n", dsi_module + 1); @@ -1731,7 +1749,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,  	seq_printf(s,	"LP_CLK\t\t%lu\n", cinfo->lp_clk); -	enable_clocks(0); +	dsi_runtime_put(dsidev);  }  void dsi_dump_clocks(struct seq_file *s) @@ -1873,7 +1891,8 @@ static void dsi_dump_dsidev_regs(struct platform_device *dsidev,  {  #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r)) -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	if (dsi_runtime_get(dsidev)) +		return;  	dsi_enable_scp_clk(dsidev);  	DUMPREG(DSI_REVISION); @@ -1947,7 +1966,7 @@ static void dsi_dump_dsidev_regs(struct platform_device *dsidev,  	DUMPREG(DSI_PLL_CONFIGURATION2);  	dsi_disable_scp_clk(dsidev); -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dsi_runtime_put(dsidev);  #undef DUMPREG  } @@ -2463,28 +2482,6 @@ static void dsi_cio_uninit(struct platform_device *dsidev)  		dsi->dsi_mux_pads(false);  } -static int _dsi_wait_reset(struct platform_device *dsidev) -{ -	int t = 0; - -	while (REG_GET(dsidev, DSI_SYSSTATUS, 0, 0) == 0) { -		if (++t > 5) { -			DSSERR("soft reset failed\n"); -			return -ENODEV; -		} -		udelay(1); -	} - -	return 0; -} - -static int _dsi_reset(struct platform_device *dsidev) -{ -	/* Soft reset */ -	REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 1, 1); -	return _dsi_wait_reset(dsidev); -} -  static void dsi_config_tx_fifo(struct platform_device *dsidev,  		enum fifo_size size1, enum fifo_size size2,  		enum fifo_size size3, enum fifo_size size4) @@ -3386,6 +3383,10 @@ static int dsi_enter_ulps(struct platform_device *dsidev)  	dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,  			DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); +	/* Reset LANEx_ULPS_SIG2 */ +	REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (0 << 0) | (0 << 1) | (0 << 2), +		7, 5); +  	dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);  	dsi_if_enable(dsidev, false); @@ -4198,22 +4199,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev,  	dsi_pll_uninit(dsidev, disconnect_lanes);  } -static int dsi_core_init(struct platform_device *dsidev) -{ -	/* Autoidle */ -	REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 0, 0); - -	/* ENWAKEUP */ -	REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 2, 2); - -	/* SIDLEMODE smart-idle */ -	REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 2, 4, 3); - -	_dsi_initialize_irq(dsidev); - -	return 0; -} -  int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)  {  	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -4229,37 +4214,37 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)  	r = omap_dss_start_device(dssdev);  	if (r) {  		DSSERR("failed to start device\n"); -		goto err0; +		goto err_start_dev;  	} -	enable_clocks(1); -	dsi_enable_pll_clock(dsidev, 1); - -	r = _dsi_reset(dsidev); +	r = dsi_runtime_get(dsidev);  	if (r) -		goto err1; +		goto err_get_dsi; -	dsi_core_init(dsidev); +	dsi_enable_pll_clock(dsidev, 1); + +	_dsi_initialize_irq(dsidev);  	r = dsi_display_init_dispc(dssdev);  	if (r) -		goto err1; +		goto err_init_dispc;  	r = dsi_display_init_dsi(dssdev);  	if (r) -		goto err2; +		goto err_init_dsi;  	mutex_unlock(&dsi->lock);  	return 0; -err2: +err_init_dsi:  	dsi_display_uninit_dispc(dssdev); -err1: -	enable_clocks(0); +err_init_dispc:  	dsi_enable_pll_clock(dsidev, 0); +	dsi_runtime_put(dsidev); +err_get_dsi:  	omap_dss_stop_device(dssdev); -err0: +err_start_dev:  	mutex_unlock(&dsi->lock);  	DSSDBG("dsi_display_enable FAILED\n");  	return r; @@ -4278,11 +4263,16 @@ void omapdss_dsi_display_disable(struct omap_dss_device *dssdev,  	mutex_lock(&dsi->lock); +	dsi_sync_vc(dsidev, 0); +	dsi_sync_vc(dsidev, 1); +	dsi_sync_vc(dsidev, 2); +	dsi_sync_vc(dsidev, 3); +  	dsi_display_uninit_dispc(dssdev);  	dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps); -	enable_clocks(0); +	dsi_runtime_put(dsidev);  	dsi_enable_pll_clock(dsidev, 0);  	omap_dss_stop_device(dssdev); @@ -4302,16 +4292,11 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable)  EXPORT_SYMBOL(omapdss_dsi_enable_te);  void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, -		u32 fifo_size, enum omap_burst_size *burst_size, +		u32 fifo_size, u32 burst_size,  		u32 *fifo_low, u32 *fifo_high)  { -	unsigned burst_size_bytes; - -	*burst_size = OMAP_DSS_BURST_16x32; -	burst_size_bytes = 16 * 32 / 8; - -	*fifo_high = fifo_size - burst_size_bytes; -	*fifo_low = fifo_size - burst_size_bytes * 2; +	*fifo_high = fifo_size - burst_size; +	*fifo_low = fifo_size - burst_size * 2;  }  int dsi_init_display(struct omap_dss_device *dssdev) @@ -4437,7 +4422,47 @@ static void dsi_calc_clock_param_ranges(struct platform_device *dsidev)  	dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);  } -static int dsi_init(struct platform_device *dsidev) +static int dsi_get_clocks(struct platform_device *dsidev) +{ +	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); +	struct clk *clk; + +	clk = clk_get(&dsidev->dev, "fck"); +	if (IS_ERR(clk)) { +		DSSERR("can't get fck\n"); +		return PTR_ERR(clk); +	} + +	dsi->dss_clk = clk; + +	if (cpu_is_omap34xx() || cpu_is_omap3630()) +		clk = clk_get(&dsidev->dev, "dss2_alwon_fck"); +	else +		clk = clk_get(&dsidev->dev, "sys_clk"); +	if (IS_ERR(clk)) { +		DSSERR("can't get sys_clk\n"); +		clk_put(dsi->dss_clk); +		dsi->dss_clk = NULL; +		return PTR_ERR(clk); +	} + +	dsi->sys_clk = clk; + +	return 0; +} + +static void dsi_put_clocks(struct platform_device *dsidev) +{ +	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + +	if (dsi->dss_clk) +		clk_put(dsi->dss_clk); +	if (dsi->sys_clk) +		clk_put(dsi->sys_clk); +} + +/* DSI1 HW IP initialisation */ +static int omap_dsi1hw_probe(struct platform_device *dsidev)  {  	struct omap_display_platform_data *dss_plat_data;  	struct omap_dss_board_info *board_info; @@ -4449,7 +4474,7 @@ static int dsi_init(struct platform_device *dsidev)  	dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);  	if (!dsi) {  		r = -ENOMEM; -		goto err0; +		goto err_alloc;  	}  	dsi->pdev = dsidev; @@ -4472,6 +4497,12 @@ static int dsi_init(struct platform_device *dsidev)  	mutex_init(&dsi->lock);  	sema_init(&dsi->bus_lock, 1); +	r = dsi_get_clocks(dsidev); +	if (r) +		goto err_get_clk; + +	pm_runtime_enable(&dsidev->dev); +  	INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work,  			dsi_framedone_timeout_work_callback); @@ -4484,26 +4515,26 @@ static int dsi_init(struct platform_device *dsidev)  	if (!dsi_mem) {  		DSSERR("can't get IORESOURCE_MEM DSI\n");  		r = -EINVAL; -		goto err1; +		goto err_ioremap;  	}  	dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem));  	if (!dsi->base) {  		DSSERR("can't ioremap DSI\n");  		r = -ENOMEM; -		goto err1; +		goto err_ioremap;  	}  	dsi->irq = platform_get_irq(dsi->pdev, 0);  	if (dsi->irq < 0) {  		DSSERR("platform_get_irq failed\n");  		r = -ENODEV; -		goto err2; +		goto err_get_irq;  	}  	r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED,  		dev_name(&dsidev->dev), dsi->pdev);  	if (r < 0) {  		DSSERR("request_irq failed\n"); -		goto err2; +		goto err_get_irq;  	}  	/* DSI VCs initialization */ @@ -4515,7 +4546,9 @@ static int dsi_init(struct platform_device *dsidev)  	dsi_calc_clock_param_ranges(dsidev); -	enable_clocks(1); +	r = dsi_runtime_get(dsidev); +	if (r) +		goto err_get_dsi;  	rev = dsi_read_reg(dsidev, DSI_REVISION);  	dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", @@ -4523,21 +4556,32 @@ static int dsi_init(struct platform_device *dsidev)  	dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev); -	enable_clocks(0); +	dsi_runtime_put(dsidev);  	return 0; -err2: + +err_get_dsi: +	free_irq(dsi->irq, dsi->pdev); +err_get_irq:  	iounmap(dsi->base); -err1: +err_ioremap: +	pm_runtime_disable(&dsidev->dev); +err_get_clk:  	kfree(dsi); -err0: +err_alloc:  	return r;  } -static void dsi_exit(struct platform_device *dsidev) +static int omap_dsi1hw_remove(struct platform_device *dsidev)  {  	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); +	WARN_ON(dsi->scp_clk_refcount > 0); + +	pm_runtime_disable(&dsidev->dev); + +	dsi_put_clocks(dsidev); +  	if (dsi->vdds_dsi_reg != NULL) {  		if (dsi->vdds_dsi_enabled) {  			regulator_disable(dsi->vdds_dsi_reg); @@ -4553,38 +4597,56 @@ static void dsi_exit(struct platform_device *dsidev)  	kfree(dsi); -	DSSDBG("omap_dsi_exit\n"); +	return 0;  } -/* DSI1 HW IP initialisation */ -static int omap_dsi1hw_probe(struct platform_device *dsidev) +static int dsi_runtime_suspend(struct device *dev)  { -	int r; +	struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); -	r = dsi_init(dsidev); -	if (r) { -		DSSERR("Failed to initialize DSI\n"); -		goto err_dsi; -	} -err_dsi: -	return r; +	clk_disable(dsi->dss_clk); + +	dispc_runtime_put(); +	dss_runtime_put(); + +	return 0;  } -static int omap_dsi1hw_remove(struct platform_device *dsidev) +static int dsi_runtime_resume(struct device *dev)  { -	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); +	struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); +	int r; + +	r = dss_runtime_get(); +	if (r) +		goto err_get_dss; + +	r = dispc_runtime_get(); +	if (r) +		goto err_get_dispc; + +	clk_enable(dsi->dss_clk); -	dsi_exit(dsidev); -	WARN_ON(dsi->scp_clk_refcount > 0);  	return 0; + +err_get_dispc: +	dss_runtime_put(); +err_get_dss: +	return r;  } +static const struct dev_pm_ops dsi_pm_ops = { +	.runtime_suspend = dsi_runtime_suspend, +	.runtime_resume = dsi_runtime_resume, +}; +  static struct platform_driver omap_dsi1hw_driver = {  	.probe          = omap_dsi1hw_probe,  	.remove         = omap_dsi1hw_remove,  	.driver         = {  		.name   = "omapdss_dsi1",  		.owner  = THIS_MODULE, +		.pm	= &dsi_pm_ops,  	},  }; diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d9489d5c4f08..0f9c3a6457a5 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -28,6 +28,8 @@  #include <linux/delay.h>  #include <linux/seq_file.h>  #include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h>  #include <video/omapdss.h>  #include <plat/clock.h> @@ -59,15 +61,9 @@ struct dss_reg {  static struct {  	struct platform_device *pdev;  	void __iomem    *base; -	int             ctx_id;  	struct clk	*dpll4_m4_ck; -	struct clk	*dss_ick; -	struct clk	*dss_fck; -	struct clk	*dss_sys_clk; -	struct clk	*dss_tv_fck; -	struct clk	*dss_video_fck; -	unsigned	num_clks_enabled; +	struct clk	*dss_clk;  	unsigned long	cache_req_pck;  	unsigned long	cache_prate; @@ -78,6 +74,7 @@ static struct {  	enum omap_dss_clk_source dispc_clk_source;  	enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; +	bool		ctx_valid;  	u32		ctx[DSS_SZ_REGS / sizeof(u32)];  } dss; @@ -87,13 +84,6 @@ static const char * const dss_generic_clk_source_names[] = {  	[OMAP_DSS_CLK_SRC_FCK]			= "DSS_FCK",  }; -static void dss_clk_enable_all_no_ctx(void); -static void dss_clk_disable_all_no_ctx(void); -static void dss_clk_enable_no_ctx(enum dss_clock clks); -static void dss_clk_disable_no_ctx(enum dss_clock clks); - -static int _omap_dss_wait_reset(void); -  static inline void dss_write_reg(const struct dss_reg idx, u32 val)  {  	__raw_writel(val, dss.base + idx.idx); @@ -109,12 +99,10 @@ static inline u32 dss_read_reg(const struct dss_reg idx)  #define RR(reg) \  	dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) -void dss_save_context(void) +static void dss_save_context(void)  { -	if (cpu_is_omap24xx()) -		return; +	DSSDBG("dss_save_context\n"); -	SR(SYSCONFIG);  	SR(CONTROL);  	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & @@ -122,14 +110,19 @@ void dss_save_context(void)  		SR(SDI_CONTROL);  		SR(PLL_CONTROL);  	} + +	dss.ctx_valid = true; + +	DSSDBG("context saved\n");  } -void dss_restore_context(void) +static void dss_restore_context(void)  { -	if (_omap_dss_wait_reset()) -		DSSERR("DSS not coming out of reset after sleep\n"); +	DSSDBG("dss_restore_context\n"); + +	if (!dss.ctx_valid) +		return; -	RR(SYSCONFIG);  	RR(CONTROL);  	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & @@ -137,6 +130,8 @@ void dss_restore_context(void)  		RR(SDI_CONTROL);  		RR(PLL_CONTROL);  	} + +	DSSDBG("context restored\n");  }  #undef SR @@ -234,6 +229,7 @@ const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src)  	return dss_generic_clk_source_names[clk_src];  } +  void dss_dump_clocks(struct seq_file *s)  {  	unsigned long dpll4_ck_rate; @@ -241,13 +237,14 @@ void dss_dump_clocks(struct seq_file *s)  	const char *fclk_name, *fclk_real_name;  	unsigned long fclk_rate; -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	if (dss_runtime_get()) +		return;  	seq_printf(s, "- DSS -\n");  	fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK);  	fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); -	fclk_rate = dss_clk_get_rate(DSS_CLK_FCK); +	fclk_rate = clk_get_rate(dss.dss_clk);  	if (dss.dpll4_m4_ck) {  		dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); @@ -273,14 +270,15 @@ void dss_dump_clocks(struct seq_file *s)  				fclk_rate);  	} -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dss_runtime_put();  }  void dss_dump_regs(struct seq_file *s)  {  #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	if (dss_runtime_get()) +		return;  	DUMPREG(DSS_REVISION);  	DUMPREG(DSS_SYSCONFIG); @@ -294,7 +292,7 @@ void dss_dump_regs(struct seq_file *s)  		DUMPREG(DSS_SDI_STATUS);  	} -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dss_runtime_put();  #undef DUMPREG  } @@ -437,7 +435,7 @@ int dss_calc_clock_rates(struct dss_clock_info *cinfo)  	} else {  		if (cinfo->fck_div != 0)  			return -EINVAL; -		cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK); +		cinfo->fck = clk_get_rate(dss.dss_clk);  	}  	return 0; @@ -467,7 +465,7 @@ int dss_set_clock_div(struct dss_clock_info *cinfo)  int dss_get_clock_div(struct dss_clock_info *cinfo)  { -	cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK); +	cinfo->fck = clk_get_rate(dss.dss_clk);  	if (dss.dpll4_m4_ck) {  		unsigned long prate; @@ -512,7 +510,7 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck,  	max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); -	fck = dss_clk_get_rate(DSS_CLK_FCK); +	fck = clk_get_rate(dss.dss_clk);  	if (req_pck == dss.cache_req_pck &&  			((cpu_is_omap34xx() && prate == dss.cache_prate) ||  			 dss.cache_dss_cinfo.fck == fck)) { @@ -539,7 +537,7 @@ retry:  	if (dss.dpll4_m4_ck == NULL) {  		struct dispc_clock_info cur_dispc;  		/* XXX can we change the clock on omap2? */ -		fck = dss_clk_get_rate(DSS_CLK_FCK); +		fck = clk_get_rate(dss.dss_clk);  		fck_div = 1;  		dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); @@ -616,28 +614,6 @@ found:  	return 0;  } -static int _omap_dss_wait_reset(void) -{ -	int t = 0; - -	while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { -		if (++t > 1000) { -			DSSERR("soft reset failed\n"); -			return -ENODEV; -		} -		udelay(1); -	} - -	return 0; -} - -static int _omap_dss_reset(void) -{ -	/* Soft reset */ -	REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); -	return _omap_dss_wait_reset(); -} -  void dss_set_venc_output(enum omap_dss_venc_type type)  {  	int l = 0; @@ -663,424 +639,88 @@ void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi)  	REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15);	/* VENC_HDMI_SWITCH */  } -static int dss_init(void) +static int dss_get_clocks(void)  { +	struct clk *clk;  	int r; -	u32 rev; -	struct resource *dss_mem; -	struct clk *dpll4_m4_ck; -	dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); -	if (!dss_mem) { -		DSSERR("can't get IORESOURCE_MEM DSS\n"); -		r = -EINVAL; -		goto fail0; -	} -	dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); -	if (!dss.base) { -		DSSERR("can't ioremap DSS\n"); -		r = -ENOMEM; -		goto fail0; +	clk = clk_get(&dss.pdev->dev, "fck"); +	if (IS_ERR(clk)) { +		DSSERR("can't get clock fck\n"); +		r = PTR_ERR(clk); +		goto err;  	} -	/* disable LCD and DIGIT output. This seems to fix the synclost -	 * problem that we get, if the bootloader starts the DSS and -	 * the kernel resets it */ -	omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); - -#ifdef CONFIG_OMAP2_DSS_SLEEP_BEFORE_RESET -	/* We need to wait here a bit, otherwise we sometimes start to -	 * get synclost errors, and after that only power cycle will -	 * restore DSS functionality. I have no idea why this happens. -	 * And we have to wait _before_ resetting the DSS, but after -	 * enabling clocks. -	 * -	 * This bug was at least present on OMAP3430. It's unknown -	 * if it happens on OMAP2 or OMAP3630. -	 */ -	msleep(50); -#endif - -	_omap_dss_reset(); +	dss.dss_clk = clk; -	/* autoidle */ -	REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); - -	/* Select DPLL */ -	REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); - -#ifdef CONFIG_OMAP2_DSS_VENC -	REG_FLD_MOD(DSS_CONTROL, 1, 4, 4);	/* venc dac demen */ -	REG_FLD_MOD(DSS_CONTROL, 1, 3, 3);	/* venc clock 4x enable */ -	REG_FLD_MOD(DSS_CONTROL, 0, 2, 2);	/* venc clock mode = normal */ -#endif  	if (cpu_is_omap34xx()) { -		dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); -		if (IS_ERR(dpll4_m4_ck)) { +		clk = clk_get(NULL, "dpll4_m4_ck"); +		if (IS_ERR(clk)) {  			DSSERR("Failed to get dpll4_m4_ck\n"); -			r = PTR_ERR(dpll4_m4_ck); -			goto fail1; +			r = PTR_ERR(clk); +			goto err;  		}  	} else if (cpu_is_omap44xx()) { -		dpll4_m4_ck = clk_get(NULL, "dpll_per_m5x2_ck"); -		if (IS_ERR(dpll4_m4_ck)) { -			DSSERR("Failed to get dpll4_m4_ck\n"); -			r = PTR_ERR(dpll4_m4_ck); -			goto fail1; +		clk = clk_get(NULL, "dpll_per_m5x2_ck"); +		if (IS_ERR(clk)) { +			DSSERR("Failed to get dpll_per_m5x2_ck\n"); +			r = PTR_ERR(clk); +			goto err;  		}  	} else { /* omap24xx */ -		dpll4_m4_ck = NULL; +		clk = NULL;  	} -	dss.dpll4_m4_ck = dpll4_m4_ck; - -	dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; -	dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; -	dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; -	dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; -	dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; - -	dss_save_context(); - -	rev = dss_read_reg(DSS_REVISION); -	printk(KERN_INFO "OMAP DSS rev %d.%d\n", -			FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); +	dss.dpll4_m4_ck = clk;  	return 0; -fail1: -	iounmap(dss.base); -fail0: -	return r; -} - -static void dss_exit(void) -{ +err: +	if (dss.dss_clk) +		clk_put(dss.dss_clk);  	if (dss.dpll4_m4_ck)  		clk_put(dss.dpll4_m4_ck); -	iounmap(dss.base); -} - -/* CONTEXT */ -static int dss_get_ctx_id(void) -{ -	struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data; -	int r; - -	if (!pdata->board_data->get_last_off_on_transaction_id) -		return 0; -	r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev); -	if (r < 0) { -		dev_err(&dss.pdev->dev, "getting transaction ID failed, " -				"will force context restore\n"); -		r = -1; -	} -	return r; -} - -int dss_need_ctx_restore(void) -{ -	int id = dss_get_ctx_id(); - -	if (id < 0 || id != dss.ctx_id) { -		DSSDBG("ctx id %d -> id %d\n", -				dss.ctx_id, id); -		dss.ctx_id = id; -		return 1; -	} else { -		return 0; -	} -} - -static void save_all_ctx(void) -{ -	DSSDBG("save context\n"); - -	dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK); - -	dss_save_context(); -	dispc_save_context(); -#ifdef CONFIG_OMAP2_DSS_DSI -	dsi_save_context(); -#endif - -	dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK); -} - -static void restore_all_ctx(void) -{ -	DSSDBG("restore context\n"); - -	dss_clk_enable_all_no_ctx(); - -	dss_restore_context(); -	dispc_restore_context(); -#ifdef CONFIG_OMAP2_DSS_DSI -	dsi_restore_context(); -#endif - -	dss_clk_disable_all_no_ctx(); -} - -static int dss_get_clock(struct clk **clock, const char *clk_name) -{ -	struct clk *clk; - -	clk = clk_get(&dss.pdev->dev, clk_name); - -	if (IS_ERR(clk)) { -		DSSERR("can't get clock %s", clk_name); -		return PTR_ERR(clk); -	} - -	*clock = clk; - -	DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); - -	return 0; -} - -static int dss_get_clocks(void) -{ -	int r; -	struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data; - -	dss.dss_ick = NULL; -	dss.dss_fck = NULL; -	dss.dss_sys_clk = NULL; -	dss.dss_tv_fck = NULL; -	dss.dss_video_fck = NULL; - -	r = dss_get_clock(&dss.dss_ick, "ick"); -	if (r) -		goto err; - -	r = dss_get_clock(&dss.dss_fck, "fck"); -	if (r) -		goto err; - -	if (!pdata->opt_clock_available) { -		r = -ENODEV; -		goto err; -	} - -	if (pdata->opt_clock_available("sys_clk")) { -		r = dss_get_clock(&dss.dss_sys_clk, "sys_clk"); -		if (r) -			goto err; -	} - -	if (pdata->opt_clock_available("tv_clk")) { -		r = dss_get_clock(&dss.dss_tv_fck, "tv_clk"); -		if (r) -			goto err; -	} - -	if (pdata->opt_clock_available("video_clk")) { -		r = dss_get_clock(&dss.dss_video_fck, "video_clk"); -		if (r) -			goto err; -	} - -	return 0; - -err: -	if (dss.dss_ick) -		clk_put(dss.dss_ick); -	if (dss.dss_fck) -		clk_put(dss.dss_fck); -	if (dss.dss_sys_clk) -		clk_put(dss.dss_sys_clk); -	if (dss.dss_tv_fck) -		clk_put(dss.dss_tv_fck); -	if (dss.dss_video_fck) -		clk_put(dss.dss_video_fck); -  	return r;  }  static void dss_put_clocks(void)  { -	if (dss.dss_video_fck) -		clk_put(dss.dss_video_fck); -	if (dss.dss_tv_fck) -		clk_put(dss.dss_tv_fck); -	if (dss.dss_sys_clk) -		clk_put(dss.dss_sys_clk); -	clk_put(dss.dss_fck); -	clk_put(dss.dss_ick); -} - -unsigned long dss_clk_get_rate(enum dss_clock clk) -{ -	switch (clk) { -	case DSS_CLK_ICK: -		return clk_get_rate(dss.dss_ick); -	case DSS_CLK_FCK: -		return clk_get_rate(dss.dss_fck); -	case DSS_CLK_SYSCK: -		return clk_get_rate(dss.dss_sys_clk); -	case DSS_CLK_TVFCK: -		return clk_get_rate(dss.dss_tv_fck); -	case DSS_CLK_VIDFCK: -		return clk_get_rate(dss.dss_video_fck); -	} - -	BUG(); -	return 0; -} - -static unsigned count_clk_bits(enum dss_clock clks) -{ -	unsigned num_clks = 0; - -	if (clks & DSS_CLK_ICK) -		++num_clks; -	if (clks & DSS_CLK_FCK) -		++num_clks; -	if (clks & DSS_CLK_SYSCK) -		++num_clks; -	if (clks & DSS_CLK_TVFCK) -		++num_clks; -	if (clks & DSS_CLK_VIDFCK) -		++num_clks; - -	return num_clks; -} - -static void dss_clk_enable_no_ctx(enum dss_clock clks) -{ -	unsigned num_clks = count_clk_bits(clks); - -	if (clks & DSS_CLK_ICK) -		clk_enable(dss.dss_ick); -	if (clks & DSS_CLK_FCK) -		clk_enable(dss.dss_fck); -	if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk) -		clk_enable(dss.dss_sys_clk); -	if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck) -		clk_enable(dss.dss_tv_fck); -	if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck) -		clk_enable(dss.dss_video_fck); - -	dss.num_clks_enabled += num_clks; -} - -void dss_clk_enable(enum dss_clock clks) -{ -	bool check_ctx = dss.num_clks_enabled == 0; - -	dss_clk_enable_no_ctx(clks); - -	/* -	 * HACK: On omap4 the registers may not be accessible right after -	 * enabling the clocks. At some point this will be handled by -	 * pm_runtime, but for the time begin this should make things work. -	 */ -	if (cpu_is_omap44xx() && check_ctx) -		udelay(10); - -	if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore()) -		restore_all_ctx(); +	if (dss.dpll4_m4_ck) +		clk_put(dss.dpll4_m4_ck); +	clk_put(dss.dss_clk);  } -static void dss_clk_disable_no_ctx(enum dss_clock clks) +struct clk *dss_get_ick(void)  { -	unsigned num_clks = count_clk_bits(clks); - -	if (clks & DSS_CLK_ICK) -		clk_disable(dss.dss_ick); -	if (clks & DSS_CLK_FCK) -		clk_disable(dss.dss_fck); -	if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk) -		clk_disable(dss.dss_sys_clk); -	if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck) -		clk_disable(dss.dss_tv_fck); -	if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck) -		clk_disable(dss.dss_video_fck); - -	dss.num_clks_enabled -= num_clks; +	return clk_get(&dss.pdev->dev, "ick");  } -void dss_clk_disable(enum dss_clock clks) +int dss_runtime_get(void)  { -	if (cpu_is_omap34xx()) { -		unsigned num_clks = count_clk_bits(clks); - -		BUG_ON(dss.num_clks_enabled < num_clks); +	int r; -		if (dss.num_clks_enabled == num_clks) -			save_all_ctx(); -	} +	DSSDBG("dss_runtime_get\n"); -	dss_clk_disable_no_ctx(clks); +	r = pm_runtime_get_sync(&dss.pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0;  } -static void dss_clk_enable_all_no_ctx(void) +void dss_runtime_put(void)  { -	enum dss_clock clks; - -	clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK; -	if (cpu_is_omap34xx()) -		clks |= DSS_CLK_VIDFCK; -	dss_clk_enable_no_ctx(clks); -} - -static void dss_clk_disable_all_no_ctx(void) -{ -	enum dss_clock clks; +	int r; -	clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK; -	if (cpu_is_omap34xx()) -		clks |= DSS_CLK_VIDFCK; -	dss_clk_disable_no_ctx(clks); -} +	DSSDBG("dss_runtime_put\n"); -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) -/* CLOCKS */ -static void core_dump_clocks(struct seq_file *s) -{ -	int i; -	struct clk *clocks[5] = { -		dss.dss_ick, -		dss.dss_fck, -		dss.dss_sys_clk, -		dss.dss_tv_fck, -		dss.dss_video_fck -	}; - -	const char *names[5] = { -		"ick", -		"fck", -		"sys_clk", -		"tv_fck", -		"video_fck" -	}; - -	seq_printf(s, "- CORE -\n"); - -	seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled); - -	for (i = 0; i < 5; i++) { -		if (!clocks[i]) -			continue; -		seq_printf(s, "%s (%s)%*s\t%lu\t%d\n", -				names[i], -				clocks[i]->name, -				24 - strlen(names[i]) - strlen(clocks[i]->name), -				"", -				clk_get_rate(clocks[i]), -				clocks[i]->usecount); -	} +	r = pm_runtime_put(&dss.pdev->dev); +	WARN_ON(r < 0);  } -#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */  /* DEBUGFS */  #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)  void dss_debug_dump_clocks(struct seq_file *s)  { -	core_dump_clocks(s);  	dss_dump_clocks(s);  	dispc_dump_clocks(s);  #ifdef CONFIG_OMAP2_DSS_DSI @@ -1089,28 +729,51 @@ void dss_debug_dump_clocks(struct seq_file *s)  }  #endif -  /* DSS HW IP initialisation */  static int omap_dsshw_probe(struct platform_device *pdev)  { +	struct resource *dss_mem; +	u32 rev;  	int r;  	dss.pdev = pdev; +	dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); +	if (!dss_mem) { +		DSSERR("can't get IORESOURCE_MEM DSS\n"); +		r = -EINVAL; +		goto err_ioremap; +	} +	dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); +	if (!dss.base) { +		DSSERR("can't ioremap DSS\n"); +		r = -ENOMEM; +		goto err_ioremap; +	} +  	r = dss_get_clocks();  	if (r)  		goto err_clocks; -	dss_clk_enable_all_no_ctx(); +	pm_runtime_enable(&pdev->dev); -	dss.ctx_id = dss_get_ctx_id(); -	DSSDBG("initial ctx id %u\n", dss.ctx_id); +	r = dss_runtime_get(); +	if (r) +		goto err_runtime_get; -	r = dss_init(); -	if (r) { -		DSSERR("Failed to initialize DSS\n"); -		goto err_dss; -	} +	/* Select DPLL */ +	REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); + +#ifdef CONFIG_OMAP2_DSS_VENC +	REG_FLD_MOD(DSS_CONTROL, 1, 4, 4);	/* venc dac demen */ +	REG_FLD_MOD(DSS_CONTROL, 1, 3, 3);	/* venc clock 4x enable */ +	REG_FLD_MOD(DSS_CONTROL, 0, 2, 2);	/* venc clock mode = normal */ +#endif +	dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; +	dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; +	dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; +	dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; +	dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;  	r = dpi_init();  	if (r) { @@ -1124,42 +787,66 @@ static int omap_dsshw_probe(struct platform_device *pdev)  		goto err_sdi;  	} -	dss_clk_disable_all_no_ctx(); +	rev = dss_read_reg(DSS_REVISION); +	printk(KERN_INFO "OMAP DSS rev %d.%d\n", +			FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + +	dss_runtime_put(); +  	return 0;  err_sdi:  	dpi_exit();  err_dpi: -	dss_exit(); -err_dss: -	dss_clk_disable_all_no_ctx(); +	dss_runtime_put(); +err_runtime_get: +	pm_runtime_disable(&pdev->dev);  	dss_put_clocks();  err_clocks: +	iounmap(dss.base); +err_ioremap:  	return r;  }  static int omap_dsshw_remove(struct platform_device *pdev)  { +	dpi_exit(); +	sdi_exit(); -	dss_exit(); +	iounmap(dss.base); -	/* -	 * As part of hwmod changes, DSS is not the only controller of dss -	 * clocks; hwmod framework itself will also enable clocks during hwmod -	 * init for dss, and autoidle is set in h/w for DSS. Hence, there's no -	 * need to disable clocks if their usecounts > 1. -	 */ -	WARN_ON(dss.num_clks_enabled > 0); +	pm_runtime_disable(&pdev->dev);  	dss_put_clocks(); + +	return 0; +} + +static int dss_runtime_suspend(struct device *dev) +{ +	dss_save_context(); +	clk_disable(dss.dss_clk);  	return 0;  } +static int dss_runtime_resume(struct device *dev) +{ +	clk_enable(dss.dss_clk); +	dss_restore_context(); +	return 0; +} + +static const struct dev_pm_ops dss_pm_ops = { +	.runtime_suspend = dss_runtime_suspend, +	.runtime_resume = dss_runtime_resume, +}; +  static struct platform_driver omap_dsshw_driver = {  	.probe          = omap_dsshw_probe,  	.remove         = omap_dsshw_remove,  	.driver         = {  		.name   = "omapdss_dss",  		.owner  = THIS_MODULE, +		.pm	= &dss_pm_ops,  	},  }; diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 8ab6d43329bb..9c94b1152c20 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -97,26 +97,12 @@ extern unsigned int dss_debug;  #define FLD_MOD(orig, val, start, end) \  	(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) -enum omap_burst_size { -	OMAP_DSS_BURST_4x32 = 0, -	OMAP_DSS_BURST_8x32 = 1, -	OMAP_DSS_BURST_16x32 = 2, -}; -  enum omap_parallel_interface_mode {  	OMAP_DSS_PARALLELMODE_BYPASS,		/* MIPI DPI */  	OMAP_DSS_PARALLELMODE_RFBI,		/* MIPI DBI */  	OMAP_DSS_PARALLELMODE_DSI,  }; -enum dss_clock { -	DSS_CLK_ICK	= 1 << 0,	/* DSS_L3_ICLK and DSS_L4_ICLK */ -	DSS_CLK_FCK	= 1 << 1,	/* DSS1_ALWON_FCLK */ -	DSS_CLK_SYSCK	= 1 << 2,	/* DSS2_ALWON_FCLK */ -	DSS_CLK_TVFCK	= 1 << 3,	/* DSS_TV_FCLK */ -	DSS_CLK_VIDFCK	= 1 << 4,	/* DSS_96M_FCLK*/ -}; -  enum dss_hdmi_venc_clk_source_select {  	DSS_VENC_TV_CLK = 0,  	DSS_HDMI_M_PCLK = 1, @@ -194,7 +180,7 @@ void dss_uninit_device(struct platform_device *pdev,  bool dss_use_replication(struct omap_dss_device *dssdev,  		enum omap_color_mode mode);  void default_get_overlay_fifo_thresholds(enum omap_plane plane, -		u32 fifo_size, enum omap_burst_size *burst_size, +		u32 fifo_size, u32 burst_size,  		u32 *fifo_low, u32 *fifo_high);  /* manager */ @@ -220,13 +206,12 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);  int dss_init_platform_driver(void);  void dss_uninit_platform_driver(void); +int dss_runtime_get(void); +void dss_runtime_put(void); + +struct clk *dss_get_ick(void); +  void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); -void dss_save_context(void); -void dss_restore_context(void); -void dss_clk_enable(enum dss_clock clks); -void dss_clk_disable(enum dss_clock clks); -unsigned long dss_clk_get_rate(enum dss_clock clk); -int dss_need_ctx_restore(void);  const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);  void dss_dump_clocks(struct seq_file *s); @@ -283,15 +268,15 @@ struct file_operations;  int dsi_init_platform_driver(void);  void dsi_uninit_platform_driver(void); +int dsi_runtime_get(struct platform_device *dsidev); +void dsi_runtime_put(struct platform_device *dsidev); +  void dsi_dump_clocks(struct seq_file *s);  void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir,  		const struct file_operations *debug_fops);  void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir,  		const struct file_operations *debug_fops); -void dsi_save_context(void); -void dsi_restore_context(void); -  int dsi_init_display(struct omap_dss_device *display);  void dsi_irq_handler(void);  unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); @@ -304,7 +289,7 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,  		bool enable_hsdiv);  void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes);  void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, -		u32 fifo_size, enum omap_burst_size *burst_size, +		u32 fifo_size, u32 burst_size,  		u32 *fifo_low, u32 *fifo_high);  void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev);  void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); @@ -317,6 +302,13 @@ static inline int dsi_init_platform_driver(void)  static inline void dsi_uninit_platform_driver(void)  {  } +static inline int dsi_runtime_get(struct platform_device *dsidev) +{ +	return 0; +} +static inline void dsi_runtime_put(struct platform_device *dsidev) +{ +}  static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)  {  	WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); @@ -384,8 +376,8 @@ void dispc_dump_regs(struct seq_file *s);  void dispc_irq_handler(void);  void dispc_fake_vsync_irq(void); -void dispc_save_context(void); -void dispc_restore_context(void); +int dispc_runtime_get(void); +void dispc_runtime_put(void);  void dispc_enable_sidle(void);  void dispc_disable_sidle(void); @@ -398,10 +390,12 @@ void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable);  void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height);  void dispc_set_digit_size(u16 width, u16 height);  u32 dispc_get_plane_fifo_size(enum omap_plane plane); -void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); +void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);  void dispc_enable_fifomerge(bool enable); -void dispc_set_burst_size(enum omap_plane plane, -		enum omap_burst_size burst_size); +u32 dispc_get_burst_size(enum omap_plane plane); +void dispc_enable_cpr(enum omap_channel channel, bool enable); +void dispc_set_cpr_coef(enum omap_channel channel, +		struct omap_dss_cpr_coefs *coefs);  void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);  void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 1c18888e5df3..b415c4ee621d 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -49,6 +49,9 @@ struct omap_dss_features {  	const enum omap_color_mode *supported_color_modes;  	const char * const *clksrc_names;  	const struct dss_param_range *dss_params; + +	const u32 buffer_size_unit; +	const u32 burst_size_unit;  };  /* This struct is assigned to one of the below during initialization */ @@ -274,6 +277,8 @@ static const struct omap_dss_features omap2_dss_features = {  	.supported_color_modes = omap2_dss_supported_color_modes,  	.clksrc_names = omap2_dss_clk_source_names,  	.dss_params = omap2_dss_param_range, +	.buffer_size_unit = 1, +	.burst_size_unit = 8,  };  /* OMAP3 DSS Features */ @@ -286,7 +291,9 @@ static const struct omap_dss_features omap3430_dss_features = {  		FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |  		FEAT_FUNCGATED | FEAT_ROWREPEATENABLE |  		FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | -		FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC, +		FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | +		FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | +		FEAT_FIR_COEF_V,  	.num_mgrs = 2,  	.num_ovls = 3, @@ -294,6 +301,8 @@ static const struct omap_dss_features omap3430_dss_features = {  	.supported_color_modes = omap3_dss_supported_color_modes,  	.clksrc_names = omap3_dss_clk_source_names,  	.dss_params = omap3_dss_param_range, +	.buffer_size_unit = 1, +	.burst_size_unit = 8,  };  static const struct omap_dss_features omap3630_dss_features = { @@ -306,7 +315,8 @@ static const struct omap_dss_features omap3630_dss_features = {  		FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED |  		FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT |  		FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | -		FEAT_DSI_PLL_FREQSEL, +		FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | +		FEAT_FIR_COEF_V,  	.num_mgrs = 2,  	.num_ovls = 3, @@ -314,6 +324,8 @@ static const struct omap_dss_features omap3630_dss_features = {  	.supported_color_modes = omap3_dss_supported_color_modes,  	.clksrc_names = omap3_dss_clk_source_names,  	.dss_params = omap3_dss_param_range, +	.buffer_size_unit = 1, +	.burst_size_unit = 8,  };  /* OMAP4 DSS Features */ @@ -327,7 +339,8 @@ static const struct omap_dss_features omap4430_es1_0_dss_features  = {  		FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 |  		FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |  		FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | -		FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2, +		FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | +		FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V,  	.num_mgrs = 3,  	.num_ovls = 3, @@ -335,6 +348,8 @@ static const struct omap_dss_features omap4430_es1_0_dss_features  = {  	.supported_color_modes = omap4_dss_supported_color_modes,  	.clksrc_names = omap4_dss_clk_source_names,  	.dss_params = omap4_dss_param_range, +	.buffer_size_unit = 16, +	.burst_size_unit = 16,  };  /* For all the other OMAP4 versions */ @@ -348,7 +363,8 @@ static const struct omap_dss_features omap4_dss_features = {  		FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |  		FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |  		FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | -		FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2, +		FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | +		FEAT_PRELOAD | FEAT_FIR_COEF_V,  	.num_mgrs = 3,  	.num_ovls = 3, @@ -356,6 +372,8 @@ static const struct omap_dss_features omap4_dss_features = {  	.supported_color_modes = omap4_dss_supported_color_modes,  	.clksrc_names = omap4_dss_clk_source_names,  	.dss_params = omap4_dss_param_range, +	.buffer_size_unit = 16, +	.burst_size_unit = 16,  };  /* Functions returning values related to a DSS feature */ @@ -401,6 +419,16 @@ const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id)  	return omap_current_dss_features->clksrc_names[id];  } +u32 dss_feat_get_buffer_size_unit(void) +{ +	return omap_current_dss_features->buffer_size_unit; +} + +u32 dss_feat_get_burst_size_unit(void) +{ +	return omap_current_dss_features->burst_size_unit; +} +  /* DSS has_feature check */  bool dss_has_feature(enum dss_feat_id id)  { diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index 07b346f7d916..b7398cbcda5f 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -51,6 +51,10 @@ enum dss_feat_id {  	FEAT_HDMI_CTS_SWMODE		= 1 << 19,  	FEAT_HANDLE_UV_SEPARATE         = 1 << 20,  	FEAT_ATTR2                      = 1 << 21, +	FEAT_VENC_REQUIRES_TV_DAC_CLK	= 1 << 22, +	FEAT_CPR			= 1 << 23, +	FEAT_PRELOAD			= 1 << 24, +	FEAT_FIR_COEF_V			= 1 << 25,  };  /* DSS register field id */ @@ -90,6 +94,9 @@ bool dss_feat_color_mode_supported(enum omap_plane plane,  		enum omap_color_mode color_mode);  const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); +u32 dss_feat_get_buffer_size_unit(void);	/* in bytes */ +u32 dss_feat_get_burst_size_unit(void);		/* in bytes */ +  bool dss_has_feature(enum dss_feat_id id);  void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);  void dss_features_init(void); diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index b0555f4f0a78..256f27a9064a 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -29,6 +29,9 @@  #include <linux/mutex.h>  #include <linux/delay.h>  #include <linux/string.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h>  #include <video/omapdss.h>  #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \  	defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) @@ -51,6 +54,9 @@ static struct {  	u8 edid_set;  	bool custom_set;  	struct hdmi_config cfg; + +	struct clk *sys_clk; +	struct clk *hdmi_clk;  } hdmi;  /* @@ -162,6 +168,27 @@ static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx,  	return val;  } +static int hdmi_runtime_get(void) +{ +	int r; + +	DSSDBG("hdmi_runtime_get\n"); + +	r = pm_runtime_get_sync(&hdmi.pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0; +} + +static void hdmi_runtime_put(void) +{ +	int r; + +	DSSDBG("hdmi_runtime_put\n"); + +	r = pm_runtime_put(&hdmi.pdev->dev); +	WARN_ON(r < 0); +} +  int hdmi_init_display(struct omap_dss_device *dssdev)  {  	DSSDBG("init_display\n"); @@ -311,30 +338,11 @@ static int hdmi_phy_init(void)  	return 0;  } -static int hdmi_wait_softreset(void) -{ -	/* reset W1 */ -	REG_FLD_MOD(HDMI_WP_SYSCONFIG, 0x1, 0, 0); - -	/* wait till SOFTRESET == 0 */ -	if (hdmi_wait_for_bit_change(HDMI_WP_SYSCONFIG, 0, 0, 0) != 0) { -		DSSERR("sysconfig reset failed\n"); -		return -ETIMEDOUT; -	} - -	return 0; -} -  static int hdmi_pll_program(struct hdmi_pll_info *fmt)  {  	u16 r = 0;  	enum hdmi_clk_refsel refsel; -	/* wait for wrapper reset */ -	r = hdmi_wait_softreset(); -	if (r) -		return r; -  	r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);  	if (r)  		return r; @@ -1064,7 +1072,7 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,  	unsigned long clkin, refclk;  	u32 mf; -	clkin = dss_clk_get_rate(DSS_CLK_SYSCK) / 10000; +	clkin = clk_get_rate(hdmi.sys_clk) / 10000;  	/*  	 * Input clock is predivided by N + 1  	 * out put of which is reference clk @@ -1098,16 +1106,6 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,  	DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);  } -static void hdmi_enable_clocks(int enable) -{ -	if (enable) -		dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | -				DSS_CLK_SYSCK | DSS_CLK_VIDFCK); -	else -		dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | -				DSS_CLK_SYSCK | DSS_CLK_VIDFCK); -} -  static int hdmi_power_on(struct omap_dss_device *dssdev)  {  	int r, code = 0; @@ -1115,7 +1113,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)  	struct omap_video_timings *p;  	unsigned long phy; -	hdmi_enable_clocks(1); +	r = hdmi_runtime_get(); +	if (r) +		return r;  	dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); @@ -1180,7 +1180,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)  	return 0;  err: -	hdmi_enable_clocks(0); +	hdmi_runtime_put();  	return -EIO;  } @@ -1191,7 +1191,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev)  	hdmi_wp_video_start(0);  	hdmi_phy_off();  	hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); -	hdmi_enable_clocks(0); +	hdmi_runtime_put();  	hdmi.edid_set = 0;  } @@ -1686,14 +1686,43 @@ static struct snd_soc_dai_driver hdmi_codec_dai_drv = {  };  #endif +static int hdmi_get_clocks(struct platform_device *pdev) +{ +	struct clk *clk; + +	clk = clk_get(&pdev->dev, "sys_clk"); +	if (IS_ERR(clk)) { +		DSSERR("can't get sys_clk\n"); +		return PTR_ERR(clk); +	} + +	hdmi.sys_clk = clk; + +	clk = clk_get(&pdev->dev, "dss_48mhz_clk"); +	if (IS_ERR(clk)) { +		DSSERR("can't get hdmi_clk\n"); +		clk_put(hdmi.sys_clk); +		return PTR_ERR(clk); +	} + +	hdmi.hdmi_clk = clk; + +	return 0; +} + +static void hdmi_put_clocks(void) +{ +	if (hdmi.sys_clk) +		clk_put(hdmi.sys_clk); +	if (hdmi.hdmi_clk) +		clk_put(hdmi.hdmi_clk); +} +  /* HDMI HW IP initialisation */  static int omapdss_hdmihw_probe(struct platform_device *pdev)  {  	struct resource *hdmi_mem; -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ -	defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -	int ret; -#endif +	int r;  	hdmi.pdata = pdev->dev.platform_data;  	hdmi.pdev = pdev; @@ -1713,17 +1742,25 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)  		return -ENOMEM;  	} +	r = hdmi_get_clocks(pdev); +	if (r) { +		iounmap(hdmi.base_wp); +		return r; +	} + +	pm_runtime_enable(&pdev->dev); +  	hdmi_panel_init();  #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \  	defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)  	/* Register ASoC codec DAI */ -	ret = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, +	r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv,  					&hdmi_codec_dai_drv, 1); -	if (ret) { +	if (r) {  		DSSERR("can't register ASoC HDMI audio codec\n"); -		return ret; +		return r;  	}  #endif  	return 0; @@ -1738,17 +1775,62 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev)  	snd_soc_unregister_codec(&pdev->dev);  #endif +	pm_runtime_disable(&pdev->dev); + +	hdmi_put_clocks(); +  	iounmap(hdmi.base_wp);  	return 0;  } +static int hdmi_runtime_suspend(struct device *dev) +{ +	clk_disable(hdmi.hdmi_clk); +	clk_disable(hdmi.sys_clk); + +	dispc_runtime_put(); +	dss_runtime_put(); + +	return 0; +} + +static int hdmi_runtime_resume(struct device *dev) +{ +	int r; + +	r = dss_runtime_get(); +	if (r < 0) +		goto err_get_dss; + +	r = dispc_runtime_get(); +	if (r < 0) +		goto err_get_dispc; + + +	clk_enable(hdmi.sys_clk); +	clk_enable(hdmi.hdmi_clk); + +	return 0; + +err_get_dispc: +	dss_runtime_put(); +err_get_dss: +	return r; +} + +static const struct dev_pm_ops hdmi_pm_ops = { +	.runtime_suspend = hdmi_runtime_suspend, +	.runtime_resume = hdmi_runtime_resume, +}; +  static struct platform_driver omapdss_hdmihw_driver = {  	.probe          = omapdss_hdmihw_probe,  	.remove         = omapdss_hdmihw_remove,  	.driver         = {  		.name   = "omapdss_hdmi",  		.owner  = THIS_MODULE, +		.pm	= &hdmi_pm_ops,  	},  }; diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 9aeea50e33ff..13d72d5c714b 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -275,6 +275,108 @@ static ssize_t manager_alpha_blending_enabled_store(  	return size;  } +static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, +		char *buf) +{ +	return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable); +} + +static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, +		const char *buf, size_t size) +{ +	struct omap_overlay_manager_info info; +	int v; +	int r; +	bool enable; + +	if (!dss_has_feature(FEAT_CPR)) +		return -ENODEV; + +	r = kstrtoint(buf, 0, &v); +	if (r) +		return r; + +	enable = !!v; + +	mgr->get_manager_info(mgr, &info); + +	if (info.cpr_enable == enable) +		return size; + +	info.cpr_enable = enable; + +	r = mgr->set_manager_info(mgr, &info); +	if (r) +		return r; + +	r = mgr->apply(mgr); +	if (r) +		return r; + +	return size; +} + +static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, +		char *buf) +{ +	struct omap_overlay_manager_info info; + +	mgr->get_manager_info(mgr, &info); + +	return snprintf(buf, PAGE_SIZE, +			"%d %d %d %d %d %d %d %d %d\n", +			info.cpr_coefs.rr, +			info.cpr_coefs.rg, +			info.cpr_coefs.rb, +			info.cpr_coefs.gr, +			info.cpr_coefs.gg, +			info.cpr_coefs.gb, +			info.cpr_coefs.br, +			info.cpr_coefs.bg, +			info.cpr_coefs.bb); +} + +static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr, +		const char *buf, size_t size) +{ +	struct omap_overlay_manager_info info; +	struct omap_dss_cpr_coefs coefs; +	int r, i; +	s16 *arr; + +	if (!dss_has_feature(FEAT_CPR)) +		return -ENODEV; + +	if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd", +				&coefs.rr, &coefs.rg, &coefs.rb, +				&coefs.gr, &coefs.gg, &coefs.gb, +				&coefs.br, &coefs.bg, &coefs.bb) != 9) +		return -EINVAL; + +	arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb, +		coefs.gr, coefs.gg, coefs.gb, +		coefs.br, coefs.bg, coefs.bb }; + +	for (i = 0; i < 9; ++i) { +		if (arr[i] < -512 || arr[i] > 511) +			return -EINVAL; +	} + +	mgr->get_manager_info(mgr, &info); + +	info.cpr_coefs = coefs; + +	r = mgr->set_manager_info(mgr, &info); +	if (r) +		return r; + +	r = mgr->apply(mgr); +	if (r) +		return r; + +	return size; +} +  struct manager_attribute {  	struct attribute attr;  	ssize_t (*show)(struct omap_overlay_manager *, char *); @@ -300,6 +402,12 @@ static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,  static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,  		manager_alpha_blending_enabled_show,  		manager_alpha_blending_enabled_store); +static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR, +		manager_cpr_enable_show, +		manager_cpr_enable_store); +static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR, +		manager_cpr_coef_show, +		manager_cpr_coef_store);  static struct attribute *manager_sysfs_attrs[] = { @@ -310,6 +418,8 @@ static struct attribute *manager_sysfs_attrs[] = {  	&manager_attr_trans_key_value.attr,  	&manager_attr_trans_key_enabled.attr,  	&manager_attr_alpha_blending_enabled.attr, +	&manager_attr_cpr_enable.attr, +	&manager_attr_cpr_coef.attr,  	NULL  }; @@ -391,33 +501,14 @@ struct overlay_cache_data {  	bool enabled; -	u32 paddr; -	void __iomem *vaddr; -	u32 p_uv_addr; /* relevant for NV12 format only */ -	u16 screen_width; -	u16 width; -	u16 height; -	enum omap_color_mode color_mode; -	u8 rotation; -	enum omap_dss_rotation_type rotation_type; -	bool mirror; - -	u16 pos_x; -	u16 pos_y; -	u16 out_width;	/* if 0, out_width == width */ -	u16 out_height;	/* if 0, out_height == height */ -	u8 global_alpha; -	u8 pre_mult_alpha; +	struct omap_overlay_info info;  	enum omap_channel channel;  	bool replication;  	bool ilace; -	enum omap_burst_size burst_size;  	u32 fifo_low;  	u32 fifo_high; - -	bool manual_update;  };  struct manager_cache_data { @@ -429,15 +520,8 @@ struct manager_cache_data {  	 * VSYNC/EVSYNC */  	bool shadow_dirty; -	u32 default_color; - -	enum omap_dss_trans_key_type trans_key_type; -	u32 trans_key; -	bool trans_enabled; - -	bool alpha_enabled; +	struct omap_overlay_manager_info info; -	bool manual_upd_display;  	bool manual_update;  	bool do_manual_update; @@ -539,24 +623,15 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)  	if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)  		return 0; +	if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) +		return 0; +  	if (dssdev->type == OMAP_DISPLAY_TYPE_VENC  			|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {  		irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;  	} else { -		if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { -			enum omap_dss_update_mode mode; -			mode = dssdev->driver->get_update_mode(dssdev); -			if (mode != OMAP_DSS_UPDATE_AUTO) -				return 0; - -			irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? -				DISPC_IRQ_FRAMEDONE -				: DISPC_IRQ_FRAMEDONE2; -		} else { -			irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? -				DISPC_IRQ_VSYNC -				: DISPC_IRQ_VSYNC2; -		} +		irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? +			DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;  	}  	mc = &dss_cache.manager_cache[mgr->id]; @@ -617,24 +692,15 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)  	if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)  		return 0; +	if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) +		return 0; +  	if (dssdev->type == OMAP_DISPLAY_TYPE_VENC  			|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {  		irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;  	} else { -		if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { -			enum omap_dss_update_mode mode; -			mode = dssdev->driver->get_update_mode(dssdev); -			if (mode != OMAP_DSS_UPDATE_AUTO) -				return 0; - -			irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? -				DISPC_IRQ_FRAMEDONE -				: DISPC_IRQ_FRAMEDONE2; -		} else { -			irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? -				DISPC_IRQ_VSYNC -				: DISPC_IRQ_VSYNC2; -		} +		irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? +			DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;  	}  	oc = &dss_cache.overlay_cache[ovl->id]; @@ -720,10 +786,12 @@ static bool rectangle_intersects(int x1, int y1, int w1, int h1,  static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)  { -	if (oc->out_width != 0 && oc->width != oc->out_width) +	struct omap_overlay_info *oi = &oc->info; + +	if (oi->out_width != 0 && oi->width != oi->out_width)  		return true; -	if (oc->out_height != 0 && oc->height != oc->out_height) +	if (oi->out_height != 0 && oi->height != oi->out_height)  		return true;  	return false; @@ -733,6 +801,8 @@ static int configure_overlay(enum omap_plane plane)  {  	struct overlay_cache_data *c;  	struct manager_cache_data *mc; +	struct omap_overlay_info *oi; +	struct omap_overlay_manager_info *mi;  	u16 outw, outh;  	u16 x, y, w, h;  	u32 paddr; @@ -742,6 +812,7 @@ static int configure_overlay(enum omap_plane plane)  	DSSDBGF("%d", plane);  	c = &dss_cache.overlay_cache[plane]; +	oi = &c->info;  	if (!c->enabled) {  		dispc_enable_plane(plane, 0); @@ -749,21 +820,22 @@ static int configure_overlay(enum omap_plane plane)  	}  	mc = &dss_cache.manager_cache[c->channel]; +	mi = &mc->info; -	x = c->pos_x; -	y = c->pos_y; -	w = c->width; -	h = c->height; -	outw = c->out_width == 0 ? c->width : c->out_width; -	outh = c->out_height == 0 ? c->height : c->out_height; -	paddr = c->paddr; +	x = oi->pos_x; +	y = oi->pos_y; +	w = oi->width; +	h = oi->height; +	outw = oi->out_width == 0 ? oi->width : oi->out_width; +	outh = oi->out_height == 0 ? oi->height : oi->out_height; +	paddr = oi->paddr;  	orig_w = w;  	orig_h = h;  	orig_outw = outw;  	orig_outh = outh; -	if (c->manual_update && mc->do_manual_update) { +	if (mc->manual_update && mc->do_manual_update) {  		unsigned bpp;  		unsigned scale_x_m = w, scale_x_d = outw;  		unsigned scale_y_m = h, scale_y_d = outh; @@ -775,7 +847,7 @@ static int configure_overlay(enum omap_plane plane)  			return 0;  		} -		switch (c->color_mode) { +		switch (oi->color_mode) {  		case OMAP_DSS_COLOR_NV12:  			bpp = 8;  			break; @@ -805,23 +877,23 @@ static int configure_overlay(enum omap_plane plane)  			BUG();  		} -		if (mc->x > c->pos_x) { +		if (mc->x > oi->pos_x) {  			x = 0; -			outw -= (mc->x - c->pos_x); -			paddr += (mc->x - c->pos_x) * +			outw -= (mc->x - oi->pos_x); +			paddr += (mc->x - oi->pos_x) *  				scale_x_m / scale_x_d * bpp / 8;  		} else { -			x = c->pos_x - mc->x; +			x = oi->pos_x - mc->x;  		} -		if (mc->y > c->pos_y) { +		if (mc->y > oi->pos_y) {  			y = 0; -			outh -= (mc->y - c->pos_y); -			paddr += (mc->y - c->pos_y) * +			outh -= (mc->y - oi->pos_y); +			paddr += (mc->y - oi->pos_y) *  				scale_y_m / scale_y_d * -				c->screen_width * bpp / 8; +				oi->screen_width * bpp / 8;  		} else { -			y = c->pos_y - mc->y; +			y = oi->pos_y - mc->y;  		}  		if (mc->w < (x + outw)) @@ -840,8 +912,8 @@ static int configure_overlay(enum omap_plane plane)  		 * the width if the original width was bigger.  		 */  		if ((w & 1) && -				(c->color_mode == OMAP_DSS_COLOR_YUV2 || -				 c->color_mode == OMAP_DSS_COLOR_UYVY)) { +				(oi->color_mode == OMAP_DSS_COLOR_YUV2 || +				 oi->color_mode == OMAP_DSS_COLOR_UYVY)) {  			if (orig_w > w)  				w += 1;  			else @@ -851,19 +923,19 @@ static int configure_overlay(enum omap_plane plane)  	r = dispc_setup_plane(plane,  			paddr, -			c->screen_width, +			oi->screen_width,  			x, y,  			w, h,  			outw, outh, -			c->color_mode, +			oi->color_mode,  			c->ilace, -			c->rotation_type, -			c->rotation, -			c->mirror, -			c->global_alpha, -			c->pre_mult_alpha, +			oi->rotation_type, +			oi->rotation, +			oi->mirror, +			oi->global_alpha, +			oi->pre_mult_alpha,  			c->channel, -			c->p_uv_addr); +			oi->p_uv_addr);  	if (r) {  		/* this shouldn't happen */ @@ -874,8 +946,7 @@ static int configure_overlay(enum omap_plane plane)  	dispc_enable_replication(plane, c->replication); -	dispc_set_burst_size(plane, c->burst_size); -	dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); +	dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);  	dispc_enable_plane(plane, 1); @@ -884,16 +955,21 @@ static int configure_overlay(enum omap_plane plane)  static void configure_manager(enum omap_channel channel)  { -	struct manager_cache_data *c; +	struct omap_overlay_manager_info *mi;  	DSSDBGF("%d", channel); -	c = &dss_cache.manager_cache[channel]; +	/* picking info from the cache */ +	mi = &dss_cache.manager_cache[channel].info; -	dispc_set_default_color(channel, c->default_color); -	dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); -	dispc_enable_trans_key(channel, c->trans_enabled); -	dispc_enable_alpha_blending(channel, c->alpha_enabled); +	dispc_set_default_color(channel, mi->default_color); +	dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key); +	dispc_enable_trans_key(channel, mi->trans_enabled); +	dispc_enable_alpha_blending(channel, mi->alpha_enabled); +	if (dss_has_feature(FEAT_CPR)) { +		dispc_enable_cpr(channel, mi->cpr_enable); +		dispc_set_cpr_coef(channel, &mi->cpr_coefs); +	}  }  /* configure_dispc() tries to write values from cache to shadow registers. @@ -928,7 +1004,7 @@ static int configure_dispc(void)  		if (!oc->dirty)  			continue; -		if (oc->manual_update && !mc->do_manual_update) +		if (mc->manual_update && !mc->do_manual_update)  			continue;  		if (mgr_busy[oc->channel]) { @@ -976,7 +1052,7 @@ static int configure_dispc(void)  		/* We don't need GO with manual update display. LCD iface will  		 * always be turned off after frame, and new settings will be  		 * taken in to use at next update */ -		if (!mc->manual_upd_display) +		if (!mc->manual_update)  			dispc_go(i);  	} @@ -1011,6 +1087,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,  {  	struct overlay_cache_data *oc;  	struct manager_cache_data *mc; +	struct omap_overlay_info *oi;  	const int num_ovls = dss_feat_get_num_ovls();  	struct omap_overlay_manager *mgr;  	int i; @@ -1053,6 +1130,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,  			unsigned outw, outh;  			oc = &dss_cache.overlay_cache[i]; +			oi = &oc->info;  			if (oc->channel != mgr->id)  				continue; @@ -1068,39 +1146,39 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,  			if (!dispc_is_overlay_scaled(oc))  				continue; -			outw = oc->out_width == 0 ? -				oc->width : oc->out_width; -			outh = oc->out_height == 0 ? -				oc->height : oc->out_height; +			outw = oi->out_width == 0 ? +				oi->width : oi->out_width; +			outh = oi->out_height == 0 ? +				oi->height : oi->out_height;  			/* is the overlay outside the update region? */  			if (!rectangle_intersects(x, y, w, h, -						oc->pos_x, oc->pos_y, +						oi->pos_x, oi->pos_y,  						outw, outh))  				continue;  			/* if the overlay totally inside the update region? */ -			if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, +			if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,  						x, y, w, h))  				continue; -			if (x > oc->pos_x) -				x1 = oc->pos_x; +			if (x > oi->pos_x) +				x1 = oi->pos_x;  			else  				x1 = x; -			if (y > oc->pos_y) -				y1 = oc->pos_y; +			if (y > oi->pos_y) +				y1 = oi->pos_y;  			else  				y1 = y; -			if ((x + w) < (oc->pos_x + outw)) -				x2 = oc->pos_x + outw; +			if ((x + w) < (oi->pos_x + outw)) +				x2 = oi->pos_x + outw;  			else  				x2 = x + w; -			if ((y + h) < (oc->pos_y + outh)) -				y2 = oc->pos_y + outh; +			if ((y + h) < (oi->pos_y + outh)) +				y2 = oi->pos_y + outh;  			else  				y2 = y + h; @@ -1236,6 +1314,10 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  	DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); +	r = dispc_runtime_get(); +	if (r) +		return r; +  	spin_lock_irqsave(&dss_cache.lock, flags);  	/* Configure overlays */ @@ -1275,23 +1357,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		ovl->info_dirty = false;  		oc->dirty = true; - -		oc->paddr = ovl->info.paddr; -		oc->vaddr = ovl->info.vaddr; -		oc->p_uv_addr = ovl->info.p_uv_addr; -		oc->screen_width = ovl->info.screen_width; -		oc->width = ovl->info.width; -		oc->height = ovl->info.height; -		oc->color_mode = ovl->info.color_mode; -		oc->rotation = ovl->info.rotation; -		oc->rotation_type = ovl->info.rotation_type; -		oc->mirror = ovl->info.mirror; -		oc->pos_x = ovl->info.pos_x; -		oc->pos_y = ovl->info.pos_y; -		oc->out_width = ovl->info.out_width; -		oc->out_height = ovl->info.out_height; -		oc->global_alpha = ovl->info.global_alpha; -		oc->pre_mult_alpha = ovl->info.pre_mult_alpha; +		oc->info = ovl->info;  		oc->replication =  			dss_use_replication(dssdev, ovl->info.color_mode); @@ -1302,11 +1368,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		oc->enabled = true; -		oc->manual_update = -			dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && -			dssdev->driver->get_update_mode(dssdev) != -				OMAP_DSS_UPDATE_AUTO; -  		++num_planes_enabled;  	} @@ -1334,20 +1395,10 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		mgr->info_dirty = false;  		mc->dirty = true; - -		mc->default_color = mgr->info.default_color; -		mc->trans_key_type = mgr->info.trans_key_type; -		mc->trans_key = mgr->info.trans_key; -		mc->trans_enabled = mgr->info.trans_enabled; -		mc->alpha_enabled = mgr->info.alpha_enabled; - -		mc->manual_upd_display = -			dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; +		mc->info = mgr->info;  		mc->manual_update = -			dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && -			dssdev->driver->get_update_mode(dssdev) != -				OMAP_DSS_UPDATE_AUTO; +			dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;  	}  	/* XXX TODO: Try to get fifomerge working. The problem is that it @@ -1368,7 +1419,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  	/* Configure overlay fifos */  	for (i = 0; i < omap_dss_get_num_overlays(); ++i) {  		struct omap_dss_device *dssdev; -		u32 size; +		u32 size, burst_size;  		ovl = omap_dss_get_overlay(i); @@ -1386,6 +1437,8 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		if (use_fifomerge)  			size *= 3; +		burst_size = dispc_get_burst_size(ovl->id); +  		switch (dssdev->type) {  		case OMAP_DISPLAY_TYPE_DPI:  		case OMAP_DISPLAY_TYPE_DBI: @@ -1393,13 +1446,13 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		case OMAP_DISPLAY_TYPE_VENC:  		case OMAP_DISPLAY_TYPE_HDMI:  			default_get_overlay_fifo_thresholds(ovl->id, size, -					&oc->burst_size, &oc->fifo_low, +					burst_size, &oc->fifo_low,  					&oc->fifo_high);  			break;  #ifdef CONFIG_OMAP2_DSS_DSI  		case OMAP_DISPLAY_TYPE_DSI:  			dsi_get_overlay_fifo_thresholds(ovl->id, size, -					&oc->burst_size, &oc->fifo_low, +					burst_size, &oc->fifo_low,  					&oc->fifo_high);  			break;  #endif @@ -1409,7 +1462,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  	}  	r = 0; -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);  	if (!dss_cache.irq_enabled) {  		u32 mask; @@ -1422,10 +1474,11 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)  		dss_cache.irq_enabled = true;  	}  	configure_dispc(); -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);  	spin_unlock_irqrestore(&dss_cache.lock, flags); +	dispc_runtime_put(); +  	return r;  } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 0f08025b1f0e..c84380c53c39 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -84,32 +84,42 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,  	old_mgr = ovl->manager; +	r = dispc_runtime_get(); +	if (r) +		return r; +  	/* detach old manager */  	if (old_mgr) {  		r = ovl->unset_manager(ovl);  		if (r) {  			DSSERR("detach failed\n"); -			return r; +			goto err;  		}  		r = old_mgr->apply(old_mgr);  		if (r) -			return r; +			goto err;  	}  	if (mgr) {  		r = ovl->set_manager(ovl, mgr);  		if (r) {  			DSSERR("Failed to attach overlay\n"); -			return r; +			goto err;  		}  		r = mgr->apply(mgr);  		if (r) -			return r; +			goto err;  	} +	dispc_runtime_put(); +  	return size; + +err: +	dispc_runtime_put(); +	return r;  }  static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) @@ -238,6 +248,9 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,  	u8 alpha;  	struct omap_overlay_info info; +	if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) +		return -ENODEV; +  	r = kstrtou8(buf, 0, &alpha);  	if (r)  		return r; @@ -504,7 +517,6 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,  	ovl->manager = mgr; -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);  	/* XXX: When there is an overlay on a DSI manual update display, and  	 * the overlay is first disabled, then moved to tv, and enabled, we  	 * seem to get SYNC_LOST_DIGIT error. @@ -518,7 +530,6 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,  	 * the overlay, but before moving the overlay to TV.  	 */  	dispc_set_channel_out(ovl->id, mgr->id); -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);  	return 0;  } @@ -719,6 +730,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)  	}  	if (mgr) { +		dispc_runtime_get(); +  		for (i = 0; i < dss_feat_get_num_ovls(); i++) {  			struct omap_overlay *ovl;  			ovl = omap_dss_get_overlay(i); @@ -728,6 +741,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)  				omap_dss_set_manager(ovl, mgr);  			}  		} + +		dispc_runtime_put();  	}  } diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index c06fbe0bc678..39f4c597026a 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -33,6 +33,8 @@  #include <linux/hrtimer.h>  #include <linux/seq_file.h>  #include <linux/semaphore.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h>  #include <video/omapdss.h>  #include "dss.h" @@ -120,12 +122,25 @@ static inline u32 rfbi_read_reg(const struct rfbi_reg idx)  	return __raw_readl(rfbi.base + idx.idx);  } -static void rfbi_enable_clocks(bool enable) +static int rfbi_runtime_get(void)  { -	if (enable) -		dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); -	else -		dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	int r; + +	DSSDBG("rfbi_runtime_get\n"); + +	r = pm_runtime_get_sync(&rfbi.pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0; +} + +static void rfbi_runtime_put(void) +{ +	int r; + +	DSSDBG("rfbi_runtime_put\n"); + +	r = pm_runtime_put(&rfbi.pdev->dev); +	WARN_ON(r < 0);  }  void rfbi_bus_lock(void) @@ -805,7 +820,8 @@ void rfbi_dump_regs(struct seq_file *s)  {  #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	if (rfbi_runtime_get()) +		return;  	DUMPREG(RFBI_REVISION);  	DUMPREG(RFBI_SYSCONFIG); @@ -836,7 +852,7 @@ void rfbi_dump_regs(struct seq_file *s)  	DUMPREG(RFBI_VSYNC_WIDTH);  	DUMPREG(RFBI_HSYNC_WIDTH); -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	rfbi_runtime_put();  #undef DUMPREG  } @@ -844,7 +860,9 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)  {  	int r; -	rfbi_enable_clocks(1); +	r = rfbi_runtime_get(); +	if (r) +		return r;  	r = omap_dss_start_device(dssdev);  	if (r) { @@ -879,6 +897,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)  err1:  	omap_dss_stop_device(dssdev);  err0: +	rfbi_runtime_put();  	return r;  }  EXPORT_SYMBOL(omapdss_rfbi_display_enable); @@ -889,7 +908,7 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev)  			DISPC_IRQ_FRAMEDONE);  	omap_dss_stop_device(dssdev); -	rfbi_enable_clocks(0); +	rfbi_runtime_put();  }  EXPORT_SYMBOL(omapdss_rfbi_display_disable); @@ -904,8 +923,9 @@ int rfbi_init_display(struct omap_dss_device *dssdev)  static int omap_rfbihw_probe(struct platform_device *pdev)  {  	u32 rev; -	u32 l;  	struct resource *rfbi_mem; +	struct clk *clk; +	int r;  	rfbi.pdev = pdev; @@ -914,46 +934,102 @@ static int omap_rfbihw_probe(struct platform_device *pdev)  	rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0);  	if (!rfbi_mem) {  		DSSERR("can't get IORESOURCE_MEM RFBI\n"); -		return -EINVAL; +		r = -EINVAL; +		goto err_ioremap;  	}  	rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem));  	if (!rfbi.base) {  		DSSERR("can't ioremap RFBI\n"); -		return -ENOMEM; +		r = -ENOMEM; +		goto err_ioremap;  	} -	rfbi_enable_clocks(1); +	pm_runtime_enable(&pdev->dev); + +	r = rfbi_runtime_get(); +	if (r) +		goto err_get_rfbi;  	msleep(10); -	rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000; +	if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap3630()) +		clk = dss_get_ick(); +	else +		clk = clk_get(&pdev->dev, "ick"); +	if (IS_ERR(clk)) { +		DSSERR("can't get ick\n"); +		r = PTR_ERR(clk); +		goto err_get_ick; +	} + +	rfbi.l4_khz = clk_get_rate(clk) / 1000; -	/* Enable autoidle and smart-idle */ -	l = rfbi_read_reg(RFBI_SYSCONFIG); -	l |= (1 << 0) | (2 << 3); -	rfbi_write_reg(RFBI_SYSCONFIG, l); +	clk_put(clk);  	rev = rfbi_read_reg(RFBI_REVISION);  	dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n",  	       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); -	rfbi_enable_clocks(0); +	rfbi_runtime_put();  	return 0; + +err_get_ick: +	rfbi_runtime_put(); +err_get_rfbi: +	pm_runtime_disable(&pdev->dev); +	iounmap(rfbi.base); +err_ioremap: +	return r;  }  static int omap_rfbihw_remove(struct platform_device *pdev)  { +	pm_runtime_disable(&pdev->dev);  	iounmap(rfbi.base);  	return 0;  } +static int rfbi_runtime_suspend(struct device *dev) +{ +	dispc_runtime_put(); +	dss_runtime_put(); + +	return 0; +} + +static int rfbi_runtime_resume(struct device *dev) +{ +	int r; + +	r = dss_runtime_get(); +	if (r < 0) +		goto err_get_dss; + +	r = dispc_runtime_get(); +	if (r < 0) +		goto err_get_dispc; + +	return 0; + +err_get_dispc: +	dss_runtime_put(); +err_get_dss: +	return r; +} + +static const struct dev_pm_ops rfbi_pm_ops = { +	.runtime_suspend = rfbi_runtime_suspend, +	.runtime_resume = rfbi_runtime_resume, +}; +  static struct platform_driver omap_rfbihw_driver = {  	.probe          = omap_rfbihw_probe,  	.remove         = omap_rfbihw_remove,  	.driver         = {  		.name   = "omapdss_rfbi",  		.owner  = THIS_MODULE, +		.pm	= &rfbi_pm_ops,  	},  }; diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 0bd4b0350f80..3a688c871a45 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -20,13 +20,11 @@  #define DSS_SUBSYS_NAME "SDI"  #include <linux/kernel.h> -#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/err.h>  #include <linux/regulator/consumer.h>  #include <video/omapdss.h> -#include <plat/cpu.h>  #include "dss.h"  static struct { @@ -60,14 +58,20 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)  	r = omap_dss_start_device(dssdev);  	if (r) {  		DSSERR("failed to start device\n"); -		goto err0; +		goto err_start_dev;  	}  	r = regulator_enable(sdi.vdds_sdi_reg);  	if (r) -		goto err1; +		goto err_reg_enable; -	dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); +	r = dss_runtime_get(); +	if (r) +		goto err_get_dss; + +	r = dispc_runtime_get(); +	if (r) +		goto err_get_dispc;  	sdi_basic_init(dssdev); @@ -80,7 +84,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)  	r = dss_calc_clock_div(1, t->pixel_clock * 1000,  			&dss_cinfo, &dispc_cinfo);  	if (r) -		goto err2; +		goto err_calc_clock_div;  	fck = dss_cinfo.fck;  	lck_div = dispc_cinfo.lck_div; @@ -101,27 +105,34 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)  	r = dss_set_clock_div(&dss_cinfo);  	if (r) -		goto err2; +		goto err_set_dss_clock_div;  	r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);  	if (r) -		goto err2; +		goto err_set_dispc_clock_div;  	dss_sdi_init(dssdev->phy.sdi.datapairs);  	r = dss_sdi_enable();  	if (r) -		goto err1; +		goto err_sdi_enable;  	mdelay(2);  	dssdev->manager->enable(dssdev->manager);  	return 0; -err2: -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); + +err_sdi_enable: +err_set_dispc_clock_div: +err_set_dss_clock_div: +err_calc_clock_div: +	dispc_runtime_put(); +err_get_dispc: +	dss_runtime_put(); +err_get_dss:  	regulator_disable(sdi.vdds_sdi_reg); -err1: +err_reg_enable:  	omap_dss_stop_device(dssdev); -err0: +err_start_dev:  	return r;  }  EXPORT_SYMBOL(omapdss_sdi_display_enable); @@ -132,7 +143,8 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)  	dss_sdi_disable(); -	dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); +	dispc_runtime_put(); +	dss_runtime_put();  	regulator_disable(sdi.vdds_sdi_reg); diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 980f919ed987..173c66430dad 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -33,11 +33,13 @@  #include <linux/seq_file.h>  #include <linux/platform_device.h>  #include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h>  #include <video/omapdss.h>  #include <plat/cpu.h>  #include "dss.h" +#include "dss_features.h"  /* Venc registers */  #define VENC_REV_ID				0x00 @@ -292,6 +294,9 @@ static struct {  	struct mutex venc_lock;  	u32 wss_data;  	struct regulator *vdda_dac_reg; + +	struct clk	*tv_clk; +	struct clk	*tv_dac_clk;  } venc;  static inline void venc_write_reg(int idx, u32 val) @@ -380,14 +385,25 @@ static void venc_reset(void)  #endif  } -static void venc_enable_clocks(int enable) +static int venc_runtime_get(void) +{ +	int r; + +	DSSDBG("venc_runtime_get\n"); + +	r = pm_runtime_get_sync(&venc.pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0; +} + +static void venc_runtime_put(void)  { -	if (enable) -		dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK | -				DSS_CLK_VIDFCK); -	else -		dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK | -				DSS_CLK_VIDFCK); +	int r; + +	DSSDBG("venc_runtime_put\n"); + +	r = pm_runtime_put(&venc.pdev->dev); +	WARN_ON(r < 0);  }  static const struct venc_config *venc_timings_to_config( @@ -406,8 +422,6 @@ static void venc_power_on(struct omap_dss_device *dssdev)  {  	u32 l; -	venc_enable_clocks(1); -  	venc_reset();  	venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); @@ -448,8 +462,6 @@ static void venc_power_off(struct omap_dss_device *dssdev)  		dssdev->platform_disable(dssdev);  	regulator_disable(venc.vdda_dac_reg); - -	venc_enable_clocks(0);  } @@ -487,6 +499,10 @@ static int venc_panel_enable(struct omap_dss_device *dssdev)  		goto err1;  	} +	r = venc_runtime_get(); +	if (r) +		goto err1; +  	venc_power_on(dssdev);  	venc.wss_data = 0; @@ -520,6 +536,8 @@ static void venc_panel_disable(struct omap_dss_device *dssdev)  	venc_power_off(dssdev); +	venc_runtime_put(); +  	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;  	omap_dss_stop_device(dssdev); @@ -538,20 +556,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev)  	return venc_panel_enable(dssdev);  } -static enum omap_dss_update_mode venc_get_update_mode( -		struct omap_dss_device *dssdev) -{ -	return OMAP_DSS_UPDATE_AUTO; -} - -static int venc_set_update_mode(struct omap_dss_device *dssdev, -		enum omap_dss_update_mode mode) -{ -	if (mode != OMAP_DSS_UPDATE_AUTO) -		return -EINVAL; -	return 0; -} -  static void venc_get_timings(struct omap_dss_device *dssdev,  			struct omap_video_timings *timings)  { @@ -598,6 +602,7 @@ static u32 venc_get_wss(struct omap_dss_device *dssdev)  static int venc_set_wss(struct omap_dss_device *dssdev,	u32 wss)  {  	const struct venc_config *config; +	int r;  	DSSDBG("venc_set_wss\n"); @@ -608,16 +613,19 @@ static int venc_set_wss(struct omap_dss_device *dssdev,	u32 wss)  	/* Invert due to VENC_L21_WC_CTL:INV=1 */  	venc.wss_data = (wss ^ 0xfffff) << 8; -	venc_enable_clocks(1); +	r = venc_runtime_get(); +	if (r) +		goto err;  	venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |  			venc.wss_data); -	venc_enable_clocks(0); +	venc_runtime_put(); +err:  	mutex_unlock(&venc.venc_lock); -	return 0; +	return r;  }  static struct omap_dss_driver venc_driver = { @@ -632,9 +640,6 @@ static struct omap_dss_driver venc_driver = {  	.get_resolution	= omapdss_default_get_resolution,  	.get_recommended_bpp = omapdss_default_get_recommended_bpp, -	.set_update_mode = venc_set_update_mode, -	.get_update_mode = venc_get_update_mode, -  	.get_timings	= venc_get_timings,  	.set_timings	= venc_set_timings,  	.check_timings	= venc_check_timings, @@ -673,7 +678,8 @@ void venc_dump_regs(struct seq_file *s)  {  #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) -	venc_enable_clocks(1); +	if (venc_runtime_get()) +		return;  	DUMPREG(VENC_F_CONTROL);  	DUMPREG(VENC_VIDOUT_CTRL); @@ -717,16 +723,56 @@ void venc_dump_regs(struct seq_file *s)  	DUMPREG(VENC_OUTPUT_CONTROL);  	DUMPREG(VENC_OUTPUT_TEST); -	venc_enable_clocks(0); +	venc_runtime_put();  #undef DUMPREG  } +static int venc_get_clocks(struct platform_device *pdev) +{ +	struct clk *clk; + +	clk = clk_get(&pdev->dev, "fck"); +	if (IS_ERR(clk)) { +		DSSERR("can't get fck\n"); +		return PTR_ERR(clk); +	} + +	venc.tv_clk = clk; + +	if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) { +		if (cpu_is_omap34xx() || cpu_is_omap3630()) +			clk = clk_get(&pdev->dev, "dss_96m_fck"); +		else +			clk = clk_get(&pdev->dev, "tv_dac_clk"); +		if (IS_ERR(clk)) { +			DSSERR("can't get tv_dac_clk\n"); +			clk_put(venc.tv_clk); +			return PTR_ERR(clk); +		} +	} else { +		clk = NULL; +	} + +	venc.tv_dac_clk = clk; + +	return 0; +} + +static void venc_put_clocks(void) +{ +	if (venc.tv_clk) +		clk_put(venc.tv_clk); +	if (venc.tv_dac_clk) +		clk_put(venc.tv_dac_clk); +} +  /* VENC HW IP initialisation */  static int omap_venchw_probe(struct platform_device *pdev)  {  	u8 rev_id;  	struct resource *venc_mem; +	int r;  	venc.pdev = pdev; @@ -737,22 +783,40 @@ static int omap_venchw_probe(struct platform_device *pdev)  	venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);  	if (!venc_mem) {  		DSSERR("can't get IORESOURCE_MEM VENC\n"); -		return -EINVAL; +		r = -EINVAL; +		goto err_ioremap;  	}  	venc.base = ioremap(venc_mem->start, resource_size(venc_mem));  	if (!venc.base) {  		DSSERR("can't ioremap VENC\n"); -		return -ENOMEM; +		r = -ENOMEM; +		goto err_ioremap;  	} -	venc_enable_clocks(1); +	r = venc_get_clocks(pdev); +	if (r) +		goto err_get_clk; + +	pm_runtime_enable(&pdev->dev); + +	r = venc_runtime_get(); +	if (r) +		goto err_get_venc;  	rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);  	dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); -	venc_enable_clocks(0); +	venc_runtime_put();  	return omap_dss_register_driver(&venc_driver); + +err_get_venc: +	pm_runtime_disable(&pdev->dev); +	venc_put_clocks(); +err_get_clk: +	iounmap(venc.base); +err_ioremap: +	return r;  }  static int omap_venchw_remove(struct platform_device *pdev) @@ -763,16 +827,61 @@ static int omap_venchw_remove(struct platform_device *pdev)  	}  	omap_dss_unregister_driver(&venc_driver); +	pm_runtime_disable(&pdev->dev); +	venc_put_clocks(); +  	iounmap(venc.base);  	return 0;  } +static int venc_runtime_suspend(struct device *dev) +{ +	if (venc.tv_dac_clk) +		clk_disable(venc.tv_dac_clk); +	clk_disable(venc.tv_clk); + +	dispc_runtime_put(); +	dss_runtime_put(); + +	return 0; +} + +static int venc_runtime_resume(struct device *dev) +{ +	int r; + +	r = dss_runtime_get(); +	if (r < 0) +		goto err_get_dss; + +	r = dispc_runtime_get(); +	if (r < 0) +		goto err_get_dispc; + +	clk_enable(venc.tv_clk); +	if (venc.tv_dac_clk) +		clk_enable(venc.tv_dac_clk); + +	return 0; + +err_get_dispc: +	dss_runtime_put(); +err_get_dss: +	return r; +} + +static const struct dev_pm_ops venc_pm_ops = { +	.runtime_suspend = venc_runtime_suspend, +	.runtime_resume = venc_runtime_resume, +}; +  static struct platform_driver omap_venchw_driver = {  	.probe          = omap_venchw_probe,  	.remove         = omap_venchw_remove,  	.driver         = {  		.name   = "omapdss_venc",  		.owner  = THIS_MODULE, +		.pm	= &venc_pm_ops,  	},  }; diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index cff450392b79..6b1ac23dbbd3 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -316,67 +316,67 @@ int omapfb_update_window(struct fb_info *fbi,  }  EXPORT_SYMBOL(omapfb_update_window); -static int omapfb_set_update_mode(struct fb_info *fbi, +int omapfb_set_update_mode(struct fb_info *fbi,  				   enum omapfb_update_mode mode)  {  	struct omap_dss_device *display = fb2display(fbi); -	enum omap_dss_update_mode um; +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_device *fbdev = ofbi->fbdev; +	struct omapfb_display_data *d;  	int r; -	if (!display || !display->driver->set_update_mode) +	if (!display)  		return -EINVAL; -	switch (mode) { -	case OMAPFB_UPDATE_DISABLED: -		um = OMAP_DSS_UPDATE_DISABLED; -		break; +	if (mode != OMAPFB_AUTO_UPDATE && mode != OMAPFB_MANUAL_UPDATE) +		return -EINVAL; -	case OMAPFB_AUTO_UPDATE: -		um = OMAP_DSS_UPDATE_AUTO; -		break; +	omapfb_lock(fbdev); -	case OMAPFB_MANUAL_UPDATE: -		um = OMAP_DSS_UPDATE_MANUAL; -		break; +	d = get_display_data(fbdev, display); -	default: -		return -EINVAL; +	if (d->update_mode == mode) { +		omapfb_unlock(fbdev); +		return 0;  	} -	r = display->driver->set_update_mode(display, um); +	r = 0; + +	if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +		if (mode == OMAPFB_AUTO_UPDATE) +			omapfb_start_auto_update(fbdev, display); +		else /* MANUAL_UPDATE */ +			omapfb_stop_auto_update(fbdev, display); + +		d->update_mode = mode; +	} else { /* AUTO_UPDATE */ +		if (mode == OMAPFB_MANUAL_UPDATE) +			r = -EINVAL; +	} + +	omapfb_unlock(fbdev);  	return r;  } -static int omapfb_get_update_mode(struct fb_info *fbi, +int omapfb_get_update_mode(struct fb_info *fbi,  		enum omapfb_update_mode *mode)  {  	struct omap_dss_device *display = fb2display(fbi); -	enum omap_dss_update_mode m; +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_device *fbdev = ofbi->fbdev; +	struct omapfb_display_data *d;  	if (!display)  		return -EINVAL; -	if (!display->driver->get_update_mode) { -		*mode = OMAPFB_AUTO_UPDATE; -		return 0; -	} +	omapfb_lock(fbdev); -	m = display->driver->get_update_mode(display); +	d = get_display_data(fbdev, display); -	switch (m) { -	case OMAP_DSS_UPDATE_DISABLED: -		*mode = OMAPFB_UPDATE_DISABLED; -		break; -	case OMAP_DSS_UPDATE_AUTO: -		*mode = OMAPFB_AUTO_UPDATE; -		break; -	case OMAP_DSS_UPDATE_MANUAL: -		*mode = OMAPFB_MANUAL_UPDATE; -		break; -	default: -		BUG(); -	} +	*mode = d->update_mode; + +	omapfb_unlock(fbdev);  	return 0;  } diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 505bc12a3031..602b71a92d3c 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -46,6 +46,10 @@ static char *def_vram;  static int def_vrfb;  static int def_rotate;  static int def_mirror; +static bool auto_update; +static unsigned int auto_update_freq; +module_param(auto_update, bool, 0); +module_param(auto_update_freq, uint, 0644);  #ifdef DEBUG  unsigned int omapfb_debug; @@ -1242,6 +1246,7 @@ static int omapfb_blank(int blank, struct fb_info *fbi)  	struct omapfb_info *ofbi = FB2OFB(fbi);  	struct omapfb2_device *fbdev = ofbi->fbdev;  	struct omap_dss_device *display = fb2display(fbi); +	struct omapfb_display_data *d;  	int r = 0;  	if (!display) @@ -1249,6 +1254,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi)  	omapfb_lock(fbdev); +	d = get_display_data(fbdev, display); +  	switch (blank) {  	case FB_BLANK_UNBLANK:  		if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) @@ -1257,6 +1264,11 @@ static int omapfb_blank(int blank, struct fb_info *fbi)  		if (display->driver->resume)  			r = display->driver->resume(display); +		if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) && +				d->update_mode == OMAPFB_AUTO_UPDATE && +				!d->auto_update_work_enabled) +			omapfb_start_auto_update(fbdev, display); +  		break;  	case FB_BLANK_NORMAL: @@ -1268,6 +1280,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi)  		if (display->state != OMAP_DSS_DISPLAY_ACTIVE)  			goto exit; +		if (d->auto_update_work_enabled) +			omapfb_stop_auto_update(fbdev, display); +  		if (display->driver->suspend)  			r = display->driver->suspend(display); @@ -1724,6 +1739,78 @@ err:  	return r;  } +static void omapfb_auto_update_work(struct work_struct *work) +{ +	struct omap_dss_device *dssdev; +	struct omap_dss_driver *dssdrv; +	struct omapfb_display_data *d; +	u16 w, h; +	unsigned int freq; +	struct omapfb2_device *fbdev; + +	d = container_of(work, struct omapfb_display_data, +			auto_update_work.work); + +	dssdev = d->dssdev; +	dssdrv = dssdev->driver; +	fbdev = d->fbdev; + +	if (!dssdrv || !dssdrv->update) +		return; + +	if (dssdrv->sync) +		dssdrv->sync(dssdev); + +	dssdrv->get_resolution(dssdev, &w, &h); +	dssdrv->update(dssdev, 0, 0, w, h); + +	freq = auto_update_freq; +	if (freq == 0) +		freq = 20; +	queue_delayed_work(fbdev->auto_update_wq, +			&d->auto_update_work, HZ / freq); +} + +void omapfb_start_auto_update(struct omapfb2_device *fbdev, +		struct omap_dss_device *display) +{ +	struct omapfb_display_data *d; + +	if (fbdev->auto_update_wq == NULL) { +		struct workqueue_struct *wq; + +		wq = create_singlethread_workqueue("omapfb_auto_update"); + +		if (wq == NULL) { +			dev_err(fbdev->dev, "Failed to create workqueue for " +					"auto-update\n"); +			return; +		} + +		fbdev->auto_update_wq = wq; +	} + +	d = get_display_data(fbdev, display); + +	INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work); + +	d->auto_update_work_enabled = true; + +	omapfb_auto_update_work(&d->auto_update_work.work); +} + +void omapfb_stop_auto_update(struct omapfb2_device *fbdev, +		struct omap_dss_device *display) +{ +	struct omapfb_display_data *d; + +	d = get_display_data(fbdev, display); + +	cancel_delayed_work_sync(&d->auto_update_work); + +	d->auto_update_work_enabled = false; +} +  /* initialize fb_info, var, fix to something sane based on the display */  static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)  { @@ -1858,10 +1945,21 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev)  	}  	for (i = 0; i < fbdev->num_displays; i++) { -		if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) -			fbdev->displays[i]->driver->disable(fbdev->displays[i]); +		struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; + +		if (fbdev->displays[i].auto_update_work_enabled) +			omapfb_stop_auto_update(fbdev, dssdev); + +		if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) +			dssdev->driver->disable(dssdev); + +		omap_dss_put_device(dssdev); +	} -		omap_dss_put_device(fbdev->displays[i]); +	if (fbdev->auto_update_wq != NULL) { +		flush_workqueue(fbdev->auto_update_wq); +		destroy_workqueue(fbdev->auto_update_wq); +		fbdev->auto_update_wq = NULL;  	}  	dev_set_drvdata(fbdev->dev, NULL); @@ -2084,14 +2182,14 @@ static int omapfb_set_def_mode(struct omapfb2_device *fbdev,  	int r;  	u8 bpp;  	struct omap_video_timings timings, temp_timings; +	struct omapfb_display_data *d;  	r = omapfb_mode_to_timings(mode_str, &timings, &bpp);  	if (r)  		return r; -	fbdev->bpp_overrides[fbdev->num_bpp_overrides].dssdev = display; -	fbdev->bpp_overrides[fbdev->num_bpp_overrides].bpp = bpp; -	++fbdev->num_bpp_overrides; +	d = get_display_data(fbdev, display); +	d->bpp_override = bpp;  	if (display->driver->check_timings) {  		r = display->driver->check_timings(display, &timings); @@ -2117,14 +2215,14 @@ static int omapfb_set_def_mode(struct omapfb2_device *fbdev,  static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,  		struct omap_dss_device *dssdev)  { -	int i; +	struct omapfb_display_data *d;  	BUG_ON(dssdev->driver->get_recommended_bpp == NULL); -	for (i = 0; i < fbdev->num_bpp_overrides; ++i) { -		if (dssdev == fbdev->bpp_overrides[i].dssdev) -			return fbdev->bpp_overrides[i].bpp; -	} +	d = get_display_data(fbdev, dssdev); + +	if (d->bpp_override != 0) +		return d->bpp_override;  	return dssdev->driver->get_recommended_bpp(dssdev);  } @@ -2156,9 +2254,9 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)  		display = NULL;  		for (i = 0; i < fbdev->num_displays; ++i) { -			if (strcmp(fbdev->displays[i]->name, +			if (strcmp(fbdev->displays[i].dssdev->name,  						display_str) == 0) { -				display = fbdev->displays[i]; +				display = fbdev->displays[i].dssdev;  				break;  			}  		} @@ -2182,6 +2280,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,  		struct omap_dss_device *dssdev)  {  	struct omap_dss_driver *dssdrv = dssdev->driver; +	struct omapfb_display_data *d;  	int r;  	r = dssdrv->enable(dssdev); @@ -2191,8 +2290,20 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,  		return r;  	} +	d = get_display_data(fbdev, dssdev); + +	d->fbdev = fbdev; +  	if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {  		u16 w, h; + +		if (auto_update) { +			omapfb_start_auto_update(fbdev, dssdev); +			d->update_mode = OMAPFB_AUTO_UPDATE; +		} else { +			d->update_mode = OMAPFB_MANUAL_UPDATE; +		} +  		if (dssdrv->enable_te) {  			r = dssdrv->enable_te(dssdev, 1);  			if (r) { @@ -2201,16 +2312,6 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,  			}  		} -		if (dssdrv->set_update_mode) { -			r = dssdrv->set_update_mode(dssdev, -					OMAP_DSS_UPDATE_MANUAL); -			if (r) { -				dev_err(fbdev->dev, -						"Failed to set update mode\n"); -				return r; -			} -		} -  		dssdrv->get_resolution(dssdev, &w, &h);  		r = dssdrv->update(dssdev, 0, 0, w, h);  		if (r) { @@ -2219,15 +2320,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,  			return r;  		}  	} else { -		if (dssdrv->set_update_mode) { -			r = dssdrv->set_update_mode(dssdev, -					OMAP_DSS_UPDATE_AUTO); -			if (r) { -				dev_err(fbdev->dev, -						"Failed to set update mode\n"); -				return r; -			} -		} +		d->update_mode = OMAPFB_AUTO_UPDATE;  	}  	return 0; @@ -2275,6 +2368,8 @@ static int omapfb_probe(struct platform_device *pdev)  	fbdev->num_displays = 0;  	dssdev = NULL;  	for_each_dss_dev(dssdev) { +		struct omapfb_display_data *d; +  		omap_dss_get_device(dssdev);  		if (!dssdev->driver) { @@ -2282,7 +2377,12 @@ static int omapfb_probe(struct platform_device *pdev)  			r = -ENODEV;  		} -		fbdev->displays[fbdev->num_displays++] = dssdev; +		d = &fbdev->displays[fbdev->num_displays++]; +		d->dssdev = dssdev; +		if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) +			d->update_mode = OMAPFB_MANUAL_UPDATE; +		else +			d->update_mode = OMAPFB_AUTO_UPDATE;  	}  	if (r) diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 2f5e817b2a9a..153bf1aceebc 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -518,6 +518,39 @@ static ssize_t show_virt(struct device *dev,  	return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);  } +static ssize_t show_upd_mode(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	enum omapfb_update_mode mode; +	int r; + +	r = omapfb_get_update_mode(fbi, &mode); + +	if (r) +		return r; + +	return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode); +} + +static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	unsigned mode; +	int r; + +	r = kstrtouint(buf, 0, &mode); +	if (r) +		return r; + +	r = omapfb_set_update_mode(fbi, mode); +	if (r) +		return r; + +	return count; +} +  static struct device_attribute omapfb_attrs[] = {  	__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,  			store_rotate_type), @@ -528,6 +561,7 @@ static struct device_attribute omapfb_attrs[] = {  			store_overlays_rotate),  	__ATTR(phys_addr, S_IRUGO, show_phys, NULL),  	__ATTR(virt_addr, S_IRUGO, show_virt, NULL), +	__ATTR(update_mode, S_IRUGO | S_IWUSR, show_upd_mode, store_upd_mode),  };  int omapfb_create_sysfs(struct omapfb2_device *fbdev) diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index aa1b1d974276..fdf0edeccf4e 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -73,6 +73,15 @@ struct omapfb_info {  	bool mirror;  }; +struct omapfb_display_data { +	struct omapfb2_device *fbdev; +	struct omap_dss_device *dssdev; +	u8 bpp_override; +	enum omapfb_update_mode update_mode; +	bool auto_update_work_enabled; +	struct delayed_work auto_update_work; +}; +  struct omapfb2_device {  	struct device *dev;  	struct mutex  mtx; @@ -86,17 +95,13 @@ struct omapfb2_device {  	struct omapfb2_mem_region regions[10];  	unsigned num_displays; -	struct omap_dss_device *displays[10]; +	struct omapfb_display_data displays[10];  	unsigned num_overlays;  	struct omap_overlay *overlays[10];  	unsigned num_managers;  	struct omap_overlay_manager *managers[10]; -	unsigned num_bpp_overrides; -	struct { -		struct omap_dss_device *dssdev; -		u8 bpp; -	} bpp_overrides[10]; +	struct workqueue_struct *auto_update_wq;  };  struct omapfb_colormode { @@ -128,6 +133,13 @@ int dss_mode_to_fb_mode(enum omap_color_mode dssmode,  int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,  		u16 posx, u16 posy, u16 outw, u16 outh); +void omapfb_start_auto_update(struct omapfb2_device *fbdev, +		struct omap_dss_device *display); +void omapfb_stop_auto_update(struct omapfb2_device *fbdev, +		struct omap_dss_device *display); +int omapfb_get_update_mode(struct fb_info *fbi, enum omapfb_update_mode *mode); +int omapfb_set_update_mode(struct fb_info *fbi, enum omapfb_update_mode mode); +  /* find the display connected to this fb, if any */  static inline struct omap_dss_device *fb2display(struct fb_info *fbi)  { @@ -143,6 +155,19 @@ static inline struct omap_dss_device *fb2display(struct fb_info *fbi)  	return NULL;  } +static inline struct omapfb_display_data *get_display_data( +		struct omapfb2_device *fbdev, struct omap_dss_device *dssdev) +{ +	int i; + +	for (i = 0; i < fbdev->num_displays; ++i) +		if (fbdev->displays[i].dssdev == dssdev) +			return &fbdev->displays[i]; + +	/* This should never happen */ +	BUG(); +} +  static inline void omapfb_lock(struct omapfb2_device *fbdev)  {  	mutex_lock(&fbdev->mtx); diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index ef532d9d3c99..f27ae16ead2e 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -567,7 +567,7 @@ static int __devinit platinumfb_probe(struct platform_device* odev)  	 * northbridge and that can fail. Only request framebuffer  	 */  	if (!request_mem_region(pinfo->rsrc_fb.start, -				pinfo->rsrc_fb.end - pinfo->rsrc_fb.start + 1, +				resource_size(&pinfo->rsrc_fb),  				"platinumfb framebuffer")) {  		printk(KERN_ERR "platinumfb: Can't request framebuffer !\n");  		framebuffer_release(info); @@ -658,8 +658,7 @@ static int __devexit platinumfb_remove(struct platform_device* odev)  	iounmap(pinfo->cmap_regs);  	release_mem_region(pinfo->rsrc_fb.start, -			   pinfo->rsrc_fb.end - -			   pinfo->rsrc_fb.start + 1); +			   resource_size(&pinfo->rsrc_fb));  	release_mem_region(pinfo->cmap_regs_phys, 0x1000); diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 35f61dd0cb3a..18ead6f0184d 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -623,19 +623,21 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (res == NULL) {  		dev_err(&pdev->dev, "no IO memory defined\n"); -		return -ENOENT; +		ret = -ENOENT; +		goto failed_put_clk;  	}  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		dev_err(&pdev->dev, "no IRQ defined\n"); -		return -ENOENT; +		ret = -ENOENT; +		goto failed_put_clk;  	}  	info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);  	if (info == NULL) { -		clk_put(clk); -		return -ENOMEM; +		ret = -ENOMEM; +		goto failed_put_clk;  	}  	/* Initialize private data */ @@ -660,7 +662,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)  	info->fix.ypanstep = 0;  	info->fix.ywrapstep = 0;  	info->fix.mmio_start = res->start; -	info->fix.mmio_len = res->end - res->start + 1; +	info->fix.mmio_len = resource_size(res);  	info->fix.accel = FB_ACCEL_NONE;  	info->fbops = &pxa168fb_ops;  	info->pseudo_palette = fbi->pseudo_palette; @@ -671,7 +673,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)  	fbi->reg_base = ioremap_nocache(res->start, resource_size(res));  	if (fbi->reg_base == NULL) {  		ret = -ENOMEM; -		goto failed; +		goto failed_free_info;  	}  	/* @@ -683,7 +685,7 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)  						&fbi->fb_start_dma, GFP_KERNEL);  	if (info->screen_base == NULL) {  		ret = -ENOMEM; -		goto failed; +		goto failed_free_info;  	}  	info->fix.smem_start = (unsigned long)fbi->fb_start_dma; @@ -772,8 +774,9 @@ failed_free_clk:  failed_free_fbmem:  	dma_free_coherent(fbi->dev, info->fix.smem_len,  			info->screen_base, fbi->fb_start_dma); -failed: +failed_free_info:  	kfree(info); +failed_put_clk:  	clk_put(clk);  	dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 0352afa49a39..4aecf213c9be 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -235,13 +235,12 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,  			    struct fb_info *info)  {  	struct s3c_fb_win *win = info->par; -	struct s3c_fb_pd_win *windata = win->windata;  	struct s3c_fb *sfb = win->parent;  	dev_dbg(sfb->dev, "checking parameters\n"); -	var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); -	var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); +	var->xres_virtual = max(var->xres_virtual, var->xres); +	var->yres_virtual = max(var->yres_virtual, var->yres);  	if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {  		dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", @@ -558,6 +557,13 @@ static int s3c_fb_set_par(struct fb_info *info)  	vidosd_set_alpha(win, alpha);  	vidosd_set_size(win, data); +	/* Enable DMA channel for this window */ +	if (sfb->variant.has_shadowcon) { +		data = readl(sfb->regs + SHADOWCON); +		data |= SHADOWCON_CHx_ENABLE(win_no); +		writel(data, sfb->regs + SHADOWCON); +	} +  	data = WINCONx_ENWIN;  	/* note, since we have to round up the bits-per-pixel, we end up @@ -637,13 +643,6 @@ static int s3c_fb_set_par(struct fb_info *info)  	writel(data, regs + sfb->variant.wincon + (win_no * 4));  	writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); -	/* Enable DMA channel for this window */ -	if (sfb->variant.has_shadowcon) { -		data = readl(sfb->regs + SHADOWCON); -		data |= SHADOWCON_CHx_ENABLE(win_no); -		writel(data, sfb->regs + SHADOWCON); -	} -  	shadow_protect_win(win, 0);  	return 0; @@ -1487,11 +1486,10 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)  	release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); -	kfree(sfb); -  	pm_runtime_put_sync(sfb->dev);  	pm_runtime_disable(sfb->dev); +	kfree(sfb);  	return 0;  } diff --git a/drivers/video/savage/savagefb.h b/drivers/video/savage/savagefb.h index 32549d177b19..dcaab9012ca2 100644 --- a/drivers/video/savage/savagefb.h +++ b/drivers/video/savage/savagefb.h @@ -55,7 +55,7 @@  #define S3_SAVAGE3D_SERIES(chip)  ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX)) -#define S3_SAVAGE4_SERIES(chip)   ((chip>=S3_SAVAGE4) || (chip<=S3_PROSAVAGEDDR)) +#define S3_SAVAGE4_SERIES(chip)   ((chip>=S3_SAVAGE4) && (chip<=S3_PROSAVAGEDDR))  #define S3_SAVAGE_MOBILE_SERIES(chip)  ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE)) diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 3b7f2f5bae71..4de541ca9c52 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -2237,6 +2237,22 @@ static int __devinit savagefb_probe(struct pci_dev* dev,  				 &info->modelist);  #endif  	info->var = savagefb_var800x600x8; +	/* if a panel was detected, default to a CVT mode instead */ +	if (par->SavagePanelWidth) { +		struct fb_videomode cvt_mode; + +		memset(&cvt_mode, 0, sizeof(cvt_mode)); +		cvt_mode.xres = par->SavagePanelWidth; +		cvt_mode.yres = par->SavagePanelHeight; +		cvt_mode.refresh = 60; +		/* FIXME: if we know there is only the panel +		 * we can enable reduced blanking as well */ +		if (fb_find_mode_cvt(&cvt_mode, 0, 0)) +			printk(KERN_WARNING "No CVT mode found for panel\n"); +		else if (fb_find_mode(&info->var, info, NULL, NULL, 0, +				      &cvt_mode, 0) != 3) +			info->var = savagefb_var800x600x8; +	}  	if (mode_option) {  		fb_find_mode(&info->var, info, mode_option, diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 6ae40b630dc9..7d54e2c612f7 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -1127,23 +1127,16 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)  		struct fb_info *info = hdmi->info;  		unsigned long parent_rate = 0, hdmi_rate; -		/* A device has been plugged in */ -		pm_runtime_get_sync(hdmi->dev); -  		ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); -		if (ret < 0) { -			pm_runtime_put(hdmi->dev); +		if (ret < 0)  			goto out; -		}  		hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;  		/* Reconfigure the clock */  		ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); -		if (ret < 0) { -			pm_runtime_put(hdmi->dev); +		if (ret < 0)  			goto out; -		}  		msleep(10);  		sh_hdmi_configure(hdmi); @@ -1191,7 +1184,6 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)  		fb_set_suspend(hdmi->info, 1);  		console_unlock(); -		pm_runtime_put(hdmi->dev);  	}  out: @@ -1312,7 +1304,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)  	INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn);  	pm_runtime_enable(&pdev->dev); -	pm_runtime_resume(&pdev->dev); +	pm_runtime_get_sync(&pdev->dev);  	/* Product and revision IDs are 0 in sh-mobile version */  	dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", @@ -1340,7 +1332,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)  ecodec:  	free_irq(irq, hdmi);  ereqirq: -	pm_runtime_suspend(&pdev->dev); +	pm_runtime_put(&pdev->dev);  	pm_runtime_disable(&pdev->dev);  	iounmap(hdmi->base);  emap: @@ -1377,7 +1369,7 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev)  	free_irq(irq, hdmi);  	/* Wait for already scheduled work */  	cancel_delayed_work_sync(&hdmi->edid_work); -	pm_runtime_suspend(&pdev->dev); +	pm_runtime_put(&pdev->dev);  	pm_runtime_disable(&pdev->dev);  	clk_disable(hdmi->hdmi_clk);  	clk_put(hdmi->hdmi_clk); diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 404c03b4b7c7..b048417247e8 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -24,7 +24,7 @@  #include <linux/backlight.h>  #include <linux/gpio.h>  #include <video/sh_mobile_lcdc.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include "sh_mobile_lcdcfb.h"  #include "sh_mobile_meram.h" @@ -470,7 +470,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)  	unsigned long tmp;  	int bpp = 0;  	unsigned long ldddsr; -	int k, m; +	int k, m, ret;  	/* enable clocks before accessing the hardware */  	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { @@ -540,7 +540,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)  		board_cfg = &ch->cfg.board_cfg;  		if (board_cfg->setup_sys) { -			int ret = board_cfg->setup_sys(board_cfg->board_data, +			ret = board_cfg->setup_sys(board_cfg->board_data,  						ch, &sh_mobile_lcdc_sys_bus_ops);  			if (ret)  				return ret; diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c index 9170c82b495c..cc7d7329dc15 100644 --- a/drivers/video/sh_mobile_meram.c +++ b/drivers/video/sh_mobile_meram.c @@ -218,7 +218,7 @@ static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,  		icb_offset = 0xc0000000 | (cfg->current_reg << 23);  	*icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); -	if ((*icb_addr_c) && is_nvcolor(cfg->pixelformat)) +	if (is_nvcolor(cfg->pixelformat))  		*icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);  } diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 87f0be1e78b5..6294dca95500 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -1664,7 +1664,7 @@ static void sm501fb_stop(struct sm501fb_info *info)  			   resource_size(info->regs_res));  } -static int sm501fb_init_fb(struct fb_info *fb, +static int __devinit sm501fb_init_fb(struct fb_info *fb,  			   enum sm501_controller head,  			   const char *fbname)  { diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 52b0f3e8ccac..087fc9960bb9 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -29,7 +29,6 @@  #include <linux/slab.h>  #include <linux/prefetch.h>  #include <linux/delay.h> -#include <linux/prefetch.h>  #include <video/udlfb.h>  #include "edid.h" @@ -1233,8 +1232,12 @@ static int dlfb_setup_modes(struct dlfb_data *dev,  			if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info))  				fb_add_videomode(&info->monspecs.modedb[i],  					&info->modelist); -			else /* if we've removed top/best mode */ -				info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; +			else { +				if (i == 0) +					/* if we've removed top/best mode */ +					info->monspecs.misc +						&= ~FB_MISC_1ST_DETAIL; +			}  		}  		default_vmode = fb_find_best_display(&info->monspecs, diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h index 7491abfcf1fc..43d11ec197fc 100644 --- a/drivers/video/vermilion/vermilion.h +++ b/drivers/video/vermilion/vermilion.h @@ -31,7 +31,7 @@  #include <linux/kernel.h>  #include <linux/pci.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <linux/mutex.h>  #define VML_DEVICE_GPU 0x5002 diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index a99bbe86db13..501b3406c6d5 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -175,6 +175,7 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,  static void vesafb_destroy(struct fb_info *info)  { +	fb_dealloc_cmap(&info->cmap);  	if (info->screen_base)  		iounmap(info->screen_base);  	release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 53b2c5aae067..305c975b1787 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -1265,9 +1265,11 @@ static void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image  static void vga16fb_destroy(struct fb_info *info)  { +	struct platform_device *dev = container_of(info->device, struct platform_device, dev);  	iounmap(info->screen_base);  	fb_dealloc_cmap(&info->cmap);  	/* XXX unshare VGA regions */ +	platform_set_drvdata(dev, NULL);  	framebuffer_release(info);  } diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index cf43c80d27f6..53aa4430d86e 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -2016,7 +2016,7 @@ static int __init viafb_setup(void)  int __init viafb_init(void)  {  	u32 dummy_x, dummy_y; -	int r; +	int r = 0;  	if (machine_is_olpc())  		/* Apply XO-1.5-specific configuration. */ @@ -2039,7 +2039,7 @@ int __init viafb_init(void)  	printk(KERN_INFO         "VIA Graphics Integration Chipset framebuffer %d.%d initializing\n",  	       VERSION_MAJOR, VERSION_MINOR); -	return 0; +	return r;  }  void __exit viafb_exit(void) diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index a20218c2fda8..beac52fc1c0e 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -395,10 +395,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,  	spin_lock_init(&info->dirty_lock);  	spin_lock_init(&info->resize_lock); -	info->fb = vmalloc(fb_size); +	info->fb = vzalloc(fb_size);  	if (info->fb == NULL)  		goto error_nomem; -	memset(info->fb, 0, fb_size);  	info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; | 
