diff options
author | Tom Cherry <tcherry@nvidia.com> | 2012-06-05 11:41:56 -0700 |
---|---|---|
committer | Tom Cherry <tcherry@nvidia.com> | 2012-06-05 11:41:56 -0700 |
commit | b46bcc0a3da47431f2711c3d63e9507cfab18ecd (patch) | |
tree | 6b9355395470108f5161840638bf48af0e10f3c1 /drivers | |
parent | 63fb092060747250a0dd305bd11018caebe23d65 (diff) | |
parent | f61bdbde09605793cfa05f7c59545c62b5e08aa6 (diff) |
Merge commit 'main-ics-2012.06.04-A5' into HEAD
Conflicts:
drivers/media/video/tegra/nvavp/nvavp_dev.c
Change-Id: I7779b0ce58004f80cccf6193148ac49551ce5da5
Diffstat (limited to 'drivers')
40 files changed, 1796 insertions, 650 deletions
diff --git a/drivers/crypto/tegra-se.c b/drivers/crypto/tegra-se.c index 3d2e9187b949..83324162a09f 100644 --- a/drivers/crypto/tegra-se.c +++ b/drivers/crypto/tegra-se.c @@ -622,9 +622,12 @@ static int tegra_se_count_sgs(struct scatterlist *sl, u32 total_bytes) return 0; do { - total_bytes -= min(sl[i].length, total_bytes); + if (!sl->length) + return 0; + total_bytes -= min(sl->length, total_bytes); i++; - } while (total_bytes); + sl = sg_next(sl); + } while (total_bytes && sl); return i; } @@ -846,7 +849,7 @@ static int tegra_se_aes_queue_req(struct ablkcipher_request *req) bool idle = true; int err = 0; - if (!req->nbytes) + if (!tegra_se_count_sgs(req->src, req->nbytes)) return -EINVAL; spin_lock_irqsave(&se_dev->lock, flags); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..9b304f254f98 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -271,6 +271,15 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_RC5T583 + bool "RICOH RC5T583 GPIO" + depends on MFD_RC5T583 + help + Select this option to enable GPIO driver for the Ricoh RC5T583 + chip family. + This driver provides the support for driving/reading the gpio pins + of RC5T583 device through standard gpio library. + config GPIO_SX150X bool "Semtech SX150x I2C GPIO expander" depends on I2C=y diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..4ef6785f446b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c new file mode 100644 index 000000000000..08428bf17718 --- /dev/null +++ b/drivers/gpio/gpio-rc5t583.c @@ -0,0 +1,180 @@ +/* + * GPIO driver for RICOH583 power management chip. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * Based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/mfd/rc5t583.h> + +struct rc5t583_gpio { + struct gpio_chip gpio_chip; + struct rc5t583 *rc5t583; +}; + +static inline struct rc5t583_gpio *to_rc5t583_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct rc5t583_gpio, gpio_chip); +} + +static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + uint8_t val = 0; + int ret; + + ret = rc5t583_read(parent, RC5T583_GPIO_MON_IOIN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + if (val) + rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + else + rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); +} + +static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + + rc5t583_gpio_set(gc, offset, value); + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); + if (ret < 0) + return ret; + + /* Set pin to gpio mode */ + return rc5t583_clear_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int rc5t583_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + + if ((offset >= 0) && (offset < 8)) + return rc5t583_gpio->rc5t583->irq_base + + RC5T583_IRQ_GPIO0 + offset; + return -EINVAL; +} + +static void rc5t583_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc); + struct device *parent = rc5t583_gpio->rc5t583->dev; + + rc5t583_set_bits(parent, RC5T583_GPIO_PGSEL, BIT(offset)); +} + +static int __devinit rc5t583_gpio_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct rc5t583_gpio *rc5t583_gpio; + + rc5t583_gpio = devm_kzalloc(&pdev->dev, sizeof(*rc5t583_gpio), + GFP_KERNEL); + if (!rc5t583_gpio) { + dev_warn(&pdev->dev, "Mem allocation for rc5t583_gpio failed"); + return -ENOMEM; + } + + rc5t583_gpio->gpio_chip.label = "gpio-rc5t583", + rc5t583_gpio->gpio_chip.owner = THIS_MODULE, + rc5t583_gpio->gpio_chip.free = rc5t583_gpio_free, + rc5t583_gpio->gpio_chip.direction_input = rc5t583_gpio_dir_input, + rc5t583_gpio->gpio_chip.direction_output = rc5t583_gpio_dir_output, + rc5t583_gpio->gpio_chip.set = rc5t583_gpio_set, + rc5t583_gpio->gpio_chip.get = rc5t583_gpio_get, + rc5t583_gpio->gpio_chip.to_irq = rc5t583_gpio_to_irq, + rc5t583_gpio->gpio_chip.ngpio = RC5T583_MAX_GPIO, + rc5t583_gpio->gpio_chip.can_sleep = 1, + rc5t583_gpio->gpio_chip.dev = &pdev->dev; + rc5t583_gpio->gpio_chip.base = -1; + rc5t583_gpio->rc5t583 = rc5t583; + + if (pdata && pdata->gpio_base) + rc5t583_gpio->gpio_chip.base = pdata->gpio_base; + + platform_set_drvdata(pdev, rc5t583_gpio); + + return gpiochip_add(&rc5t583_gpio->gpio_chip); +} + +static int __devexit rc5t583_gpio_remove(struct platform_device *pdev) +{ + struct rc5t583_gpio *rc5t583_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&rc5t583_gpio->gpio_chip); +} + +static struct platform_driver rc5t583_gpio_driver = { + .driver = { + .name = "rc5t583-gpio", + .owner = THIS_MODULE, + }, + .probe = rc5t583_gpio_probe, + .remove = __devexit_p(rc5t583_gpio_remove), +}; + +static int __init rc5t583_gpio_init(void) +{ + return platform_driver_register(&rc5t583_gpio_driver); +} +subsys_initcall(rc5t583_gpio_init); + +static void __exit rc5t583_gpio_exit(void) +{ + platform_driver_unregister(&rc5t583_gpio_driver); +} +module_exit(rc5t583_gpio_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("GPIO interface for RC5T583"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:rc5t583-gpio"); diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 7eef648a3351..af6dc837ffca 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -18,14 +18,26 @@ #include <linux/errno.h> #include <linux/gpio.h> #include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/mfd/tps65910.h> +struct tps65910_gpio { + struct gpio_chip gpio_chip; + struct tps65910 *tps65910; +}; + +static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tps65910_gpio, gpio_chip); +} + static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); - uint8_t val; + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; + unsigned int val; - tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); + tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val); if (val & GPIO_STS_MASK) return 1; @@ -36,83 +48,135 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; if (value) - tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); else - tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_SET_MASK); } static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; /* Set the initial value */ tps65910_gpio_set(gc, offset, value); - return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) { - struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc); + struct tps65910 *tps65910 = tps65910_gpio->tps65910; - return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); } -void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) +static int __devinit tps65910_gpio_probe(struct platform_device *pdev) { + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct tps65910_board *pdata = dev_get_platdata(tps65910->dev); + struct tps65910_gpio *tps65910_gpio; int ret; - struct tps65910_board *board_data; + int i; + + tps65910_gpio = devm_kzalloc(&pdev->dev, + sizeof(*tps65910_gpio), GFP_KERNEL); + if (!tps65910_gpio) { + dev_err(&pdev->dev, "Could not allocate tps65910_gpio\n"); + return -ENOMEM; + } - if (!gpio_base) - return; + tps65910_gpio->tps65910 = tps65910; - tps65910->gpio.owner = THIS_MODULE; - tps65910->gpio.label = tps65910->i2c_client->name; - tps65910->gpio.dev = tps65910->dev; - tps65910->gpio.base = gpio_base; + tps65910_gpio->gpio_chip.owner = THIS_MODULE; + tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name; switch(tps65910_chip_id(tps65910)) { case TPS65910: - tps65910->gpio.ngpio = TPS65910_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO; break; case TPS65911: - tps65910->gpio.ngpio = TPS65911_NUM_GPIO; + tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO; break; default: - return; + return -EINVAL; } - tps65910->gpio.can_sleep = 1; - - tps65910->gpio.direction_input = tps65910_gpio_input; - tps65910->gpio.direction_output = tps65910_gpio_output; - tps65910->gpio.set = tps65910_gpio_set; - tps65910->gpio.get = tps65910_gpio_get; - - /* Configure sleep control for gpios */ - board_data = dev_get_platdata(tps65910->dev); - if (board_data) { - int i; - for (i = 0; i < tps65910->gpio.ngpio; ++i) { - if (board_data->en_gpio_sleep[i]) { - ret = tps65910_set_bits(tps65910, - TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); - if (ret < 0) - dev_warn(tps65910->dev, - "GPIO Sleep setting failed\n"); - } - } + tps65910_gpio->gpio_chip.can_sleep = 1; + tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input; + tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output; + tps65910_gpio->gpio_chip.set = tps65910_gpio_set; + tps65910_gpio->gpio_chip.get = tps65910_gpio_get; + tps65910_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + tps65910_gpio->gpio_chip.base = pdata->gpio_base; + else + tps65910_gpio->gpio_chip.base = -1; + + if (!pdata) + goto skip_init; + + /* Configure sleep control for gpios if provided */ + for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) { + if (!pdata->en_gpio_sleep[i]) + continue; + + ret = tps65910_reg_set_bits(tps65910, + TPS65910_GPIO0 + i, GPIO_SLEEP_MASK); + if (ret < 0) + dev_warn(tps65910->dev, + "GPIO Sleep setting failed with err %d\n", ret); + } + +skip_init: + ret = gpiochip_add(&tps65910_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; } - ret = gpiochip_add(&tps65910->gpio); + platform_set_drvdata(pdev, tps65910_gpio); - if (ret) - dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); + return ret; } + +static int __devexit tps65910_gpio_remove(struct platform_device *pdev) +{ + struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&tps65910_gpio->gpio_chip); +} + +static struct platform_driver tps65910_gpio_driver = { + .driver.name = "tps65910-gpio", + .driver.owner = THIS_MODULE, + .probe = tps65910_gpio_probe, + .remove = __devexit_p(tps65910_gpio_remove), +}; + +static int __init tps65910_gpio_init(void) +{ + return platform_driver_register(&tps65910_gpio_driver); +} +subsys_initcall(tps65910_gpio_init); + +static void __exit tps65910_gpio_exit(void) +{ + platform_driver_unregister(&tps65910_gpio_driver); +} +module_exit(tps65910_gpio_exit); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>"); +MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65910-gpio"); diff --git a/drivers/media/video/tegra/ad5816.c b/drivers/media/video/tegra/ad5816.c index d95368f43cf6..ed113b3a187c 100644 --- a/drivers/media/video/tegra/ad5816.c +++ b/drivers/media/video/tegra/ad5816.c @@ -249,9 +249,8 @@ static int ad5816_i2c_wr16(struct ad5816_info *info, u8 reg, u16 val) return 0; } -static int ad5816_gpio_wr(struct ad5816_info *info, - enum ad5816_gpio_types i, - int val) /* val: 0=deassert, 1=assert */ +static int ad5816_gpio_wr(struct ad5816_info *info, ad5816_gpio_types i, + int val) /* val: 0=deassert, 1=assert */ { int err = -EINVAL; if (info->gpio[i].valid) { @@ -370,8 +369,7 @@ static void ad5816_gpio_init(struct ad5816_info *info) } } -static int ad5816_vreg_dis(struct ad5816_info *info, - enum ad5816_vreg i) +static int ad5816_vreg_dis(struct ad5816_info *info, ad5816_vreg i) { int err = 0; if (info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { @@ -396,8 +394,7 @@ static int ad5816_vreg_dis_all(struct ad5816_info *info) return err; } -static int ad5816_vreg_en(struct ad5816_info *info, - enum ad5816_vreg i) +static int ad5816_vreg_en(struct ad5816_info *info, ad5816_vreg i) { int err = 0; if (!info->vreg[i].vreg_flag && (info->vreg[i].vreg != NULL)) { diff --git a/drivers/media/video/tegra/nvavp/Kconfig b/drivers/media/video/tegra/nvavp/Kconfig index 2d3af3f79fb3..294253a0de49 100644 --- a/drivers/media/video/tegra/nvavp/Kconfig +++ b/drivers/media/video/tegra/nvavp/Kconfig @@ -8,3 +8,14 @@ config TEGRA_NVAVP /dev/tegra_avpchannel. If unsure, say N + +config TEGRA_NVAVP_AUDIO + bool "Enable Audio Channel support for Tegra NVAVP driver" + depends on TEGRA_NVAVP + default n + help + Enables support for the push-buffer mechanism based driver for the Tegra + audio multimedia framework. Exports the Tegra nvavp interface on device node + /dev/tegra_audio_avpchannel. + + If unsure, say N diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c index 012f50b7a5f4..b695aa16af59 100644 --- a/drivers/media/video/tegra/nvavp/nvavp_dev.c +++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c @@ -72,6 +72,34 @@ /* AVP behavior params */ #define NVAVP_OS_IDLE_TIMEOUT 100 /* milli-seconds */ +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +/* Two control channels: Audio and Video channels */ +#define NVAVP_NUM_CHANNELS 2 + +#define NVAVP_AUDIO_CHANNEL 1 + +#define IS_AUDIO_CHANNEL_ID(channel_id) (channel_id == NVAVP_AUDIO_CHANNEL ? 1: 0) +#else +#define NVAVP_NUM_CHANNELS 1 +#endif + +/* Channel ID 0 represents the Video channel control area */ +#define NVAVP_VIDEO_CHANNEL 0 +/* Channel ID 1 represents the Audio channel control area */ + +#define IS_VIDEO_CHANNEL_ID(channel_id) (channel_id == NVAVP_VIDEO_CHANNEL ? 1: 0) + + +struct nvavp_channel { + struct mutex pushbuffer_lock; + struct nvmap_handle_ref *pushbuf_handle; + unsigned long pushbuf_phys; + u8 *pushbuf_data; + u32 pushbuf_index; + u32 pushbuf_fence; + struct nv_e276_control *os_control; +}; + struct nvavp_info { u32 clk_enabled; struct clk *bsev_clk; @@ -88,8 +116,10 @@ struct nvavp_info { struct mutex open_lock; int refcount; - int initialized; - + int video_initialized; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + int audio_initialized; +#endif struct work_struct clock_disable_work; /* os information */ @@ -101,22 +131,19 @@ struct nvavp_info { /* client for driver allocations, persistent */ struct nvmap_client *nvmap; - struct mutex pushbuffer_lock; - struct nvmap_handle_ref *pushbuf_handle; - unsigned long pushbuf_phys; - u8 *pushbuf_data; - u32 pushbuf_index; - u32 pushbuf_fence; bool pending; - struct nv_e276_control *os_control; + struct nvavp_channel channel_info[NVAVP_NUM_CHANNELS]; struct nvhost_syncpt *nvhost_syncpt; u32 syncpt_id; u32 syncpt_value; struct nvhost_device *nvhost_dev; - struct miscdevice misc_dev; + struct miscdevice video_misc_dev; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + struct miscdevice audio_misc_dev; +#endif atomic_t clock_stay_on_refcount; }; @@ -128,8 +155,77 @@ struct nvavp_clientctx { int num_relocs; struct nvavp_info *nvavp; int clock_stay_on; + int channel_id; }; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static int nvavp_get_audio_init_status(struct nvavp_info *nvavp) +{ + return nvavp->audio_initialized; +} + +static void nvavp_set_audio_init_status(struct nvavp_info *nvavp, int status) +{ + nvavp->audio_initialized = status; +} +#endif + +static void nvavp_set_video_init_status(struct nvavp_info *nvavp, int status) +{ + nvavp->video_initialized = status; +} + +static int nvavp_get_video_init_status(struct nvavp_info *nvavp) +{ + return nvavp->video_initialized; +} + +static struct nvavp_channel *nvavp_get_channel_info(struct nvavp_info *nvavp, int channel_id) +{ + return &nvavp->channel_info[channel_id]; +} + +static void nvavp_set_channel_control_area(struct nvavp_info *nvavp, int channel_id) +{ + struct nv_e276_control *control; + struct nvavp_os_info *os = &nvavp->os_info; + u32 temp; + void *ptr; + struct nvavp_channel *channel_info; + + ptr = os->data + os->control_offset + (sizeof(struct nv_e276_control) * channel_id); + + channel_info = nvavp_get_channel_info(nvavp, channel_id); + channel_info->os_control = (struct nv_e276_control *)ptr; + + control = channel_info->os_control; + + /* init get and put pointers */ + writel(0x0, &control->put); + writel(0x0, &control->get); + + pr_debug("nvavp_set_channel_control_area for channel_id (%d):\ + control->put (0x%x) control->get (0x%x)\n", + channel_id, &control->put, &control->get); + + /* enable avp VDE clock control and disable iram clock gating */ + writel(0x0, &control->idle_clk_enable); + writel(0x0, &control->iram_clk_gating); + + /* enable avp idle timeout interrupt */ + writel(0x1, &control->idle_notify_enable); + writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay); + + /* init dma start and end pointers */ + writel(channel_info->pushbuf_phys, &control->dma_start); + writel((channel_info->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE), + &control->dma_end); + + writel(0x00, &channel_info->pushbuf_index); + temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE; + writel(temp, &channel_info->pushbuf_fence); +} + static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id) { if (!nvavp) @@ -175,7 +271,8 @@ static void nvavp_clks_disable(struct nvavp_info *nvavp) static u32 nvavp_check_idle(struct nvavp_info *nvavp) { - struct nv_e276_control *control = nvavp->os_control; + struct nvavp_channel *channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL); + struct nv_e276_control *control = channel_info->os_control; return ((control->put == control->get) && (!atomic_read(&nvavp->clock_stay_on_refcount))) ? 1 : 0; } @@ -183,18 +280,20 @@ static u32 nvavp_check_idle(struct nvavp_info *nvavp) static void clock_disable_handler(struct work_struct *work) { struct nvavp_info *nvavp; + struct nvavp_channel *channel_info; nvavp = container_of(work, struct nvavp_info, clock_disable_work); + channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL); - mutex_lock(&nvavp->pushbuffer_lock); + mutex_lock(&channel_info->pushbuffer_lock); mutex_lock(&nvavp->open_lock); if (nvavp_check_idle(nvavp) && nvavp->pending) { nvavp->pending = false; nvavp_clks_disable(nvavp); } mutex_unlock(&nvavp->open_lock); - mutex_unlock(&nvavp->pushbuffer_lock); + mutex_unlock(&channel_info->pushbuffer_lock); } static int nvavp_service(struct nvavp_info *nvavp) @@ -212,6 +311,10 @@ static int nvavp_service(struct nvavp_info *nvavp) if (inbox & NVE276_OS_INTERRUPT_VIDEO_IDLE) schedule_work(&nvavp->clock_disable_work); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (inbox & NVE276_OS_INTERRUPT_AUDIO_IDLE) + pr_debug("nvavp_service NVE276_OS_INTERRUPT_AUDIO_IDLE\n"); +#endif if (inbox & NVE276_OS_INTERRUPT_DEBUG_STRING) { /* Should only occur with debug AVP OS builds */ debug_print = os->data; @@ -337,99 +440,91 @@ static int nvavp_reset_vde(struct nvavp_info *nvavp) return 0; } -static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp) +static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp, int channel_id) { int ret = 0; - nvavp->pushbuf_handle = nvmap_alloc(nvavp->nvmap, NVAVP_PUSHBUFFER_SIZE, - SZ_1M, NVMAP_HANDLE_UNCACHEABLE, 0); - if (IS_ERR(nvavp->pushbuf_handle)) { + struct nvavp_channel *channel_info = nvavp_get_channel_info( + nvavp, channel_id); + + channel_info->pushbuf_handle = nvmap_alloc(nvavp->nvmap, + NVAVP_PUSHBUFFER_SIZE, + SZ_1M, NVMAP_HANDLE_UNCACHEABLE, + 0); + if (IS_ERR(channel_info->pushbuf_handle)) { dev_err(&nvavp->nvhost_dev->dev, "cannot create pushbuffer handle\n"); - ret = PTR_ERR(nvavp->pushbuf_handle); + ret = PTR_ERR(channel_info->pushbuf_handle); goto err_pushbuf_alloc; } - nvavp->pushbuf_data = (u8 *)nvmap_mmap(nvavp->pushbuf_handle); - if (!nvavp->pushbuf_data) { + channel_info->pushbuf_data = (u8 *)nvmap_mmap( + channel_info->pushbuf_handle); + + if (!channel_info->pushbuf_data) { dev_err(&nvavp->nvhost_dev->dev, "cannot map pushbuffer handle\n"); ret = -ENOMEM; goto err_pushbuf_mmap; } - nvavp->pushbuf_phys = nvmap_pin(nvavp->nvmap, nvavp->pushbuf_handle); - if (IS_ERR((void *)nvavp->pushbuf_phys)) { + channel_info->pushbuf_phys = nvmap_pin(nvavp->nvmap, + channel_info->pushbuf_handle); + if (IS_ERR((void *)channel_info->pushbuf_phys)) { dev_err(&nvavp->nvhost_dev->dev, "cannot pin pushbuffer handle\n"); - ret = PTR_ERR((void *)nvavp->pushbuf_phys); + ret = PTR_ERR((void *)channel_info->pushbuf_phys); goto err_pushbuf_pin; } - memset(nvavp->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE); + memset(channel_info->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE); return 0; err_pushbuf_pin: - nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); + nvmap_munmap(channel_info->pushbuf_handle, channel_info->pushbuf_data); err_pushbuf_mmap: - nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); + nvmap_free(nvavp->nvmap, channel_info->pushbuf_handle); err_pushbuf_alloc: return ret; } static void nvavp_pushbuffer_free(struct nvavp_info *nvavp) { - nvmap_unpin(nvavp->nvmap, nvavp->pushbuf_handle); - nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data); - nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle); + int channel_id; + + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) { + if (nvavp->channel_info[channel_id].pushbuf_data) { + nvmap_unpin(nvavp->nvmap, + nvavp->channel_info[channel_id].pushbuf_handle); + nvmap_munmap( + nvavp->channel_info[channel_id].pushbuf_handle, + nvavp->channel_info[channel_id].pushbuf_data); + nvmap_free(nvavp->nvmap, + nvavp->channel_info[channel_id].pushbuf_handle); + } + } } + static int nvavp_pushbuffer_init(struct nvavp_info *nvavp) { - void *ptr; - struct nvavp_os_info *os = &nvavp->os_info; - struct nv_e276_control *control; - u32 temp; - int ret; + int ret, channel_id; - ret = nvavp_pushbuffer_alloc(nvavp); - if (ret) { - dev_err(&nvavp->nvhost_dev->dev, - "unable to alloc pushbuffer\n"); - return ret; - } - - ptr = os->data; - ptr += os->control_offset; - nvavp->os_control = (struct nv_e276_control *)ptr; - - control = nvavp->os_control; - memset(control, 0, sizeof(struct nvavp_os_info)); - - /* init get and put pointers */ - writel(0x0, &control->put); - writel(0x0, &control->get); - - /* enable avp VDE clock control and disable iram clock gating */ - writel(0x0, &control->idle_clk_enable); - writel(0x0, &control->iram_clk_gating); - - /* enable avp idle timeout interrupt */ - writel(0x1, &control->idle_notify_enable); - writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay); - - /* init dma start and end pointers */ - writel(nvavp->pushbuf_phys, &control->dma_start); - writel((nvavp->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE), - &control->dma_end); - - writel(0x00, &nvavp->pushbuf_index); - temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE; - writel(temp, &nvavp->pushbuf_fence); - - nvavp->syncpt_id = NVSYNCPT_AVP_0; - nvavp->syncpt_value = nvhost_syncpt_read(nvavp->nvhost_syncpt, - nvavp->syncpt_id); + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) { + ret = nvavp_pushbuffer_alloc(nvavp, channel_id); + if (ret) { + dev_err(&nvavp->nvhost_dev->dev, + "unable to alloc pushbuffer\n"); + return ret; + } + nvavp_set_channel_control_area(nvavp, channel_id); + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + nvavp->syncpt_id = NVSYNCPT_AVP_0; + nvavp->syncpt_value = nvhost_syncpt_read( + nvavp->nvhost_syncpt, + nvavp->syncpt_id); + } + } return 0; } @@ -440,37 +535,47 @@ static void nvavp_pushbuffer_deinit(struct nvavp_info *nvavp) static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, u32 gather_count, struct nvavp_syncpt *syncpt, - u32 ext_ucode_flag) + u32 ext_ucode_flag, int channel_id) { - struct nv_e276_control *control = nvavp->os_control; + struct nvavp_channel *channel_info; + struct nv_e276_control *control; u32 gather_cmd, setucode_cmd, sync = 0; u32 wordcount = 0; u32 index, value = -1; - mutex_lock(&nvavp->pushbuffer_lock); + channel_info = nvavp_get_channel_info(nvavp, channel_id); + + control = channel_info->os_control; + pr_debug("nvavp_pushbuffer_update for channel_id (%d):\ + control->put (0x%x) control->get (0x%x)\n", + channel_id, &control->put, &control->get); + + mutex_lock(&channel_info->pushbuffer_lock); /* check for pushbuffer wrapping */ - if (nvavp->pushbuf_index >= nvavp->pushbuf_fence) - nvavp->pushbuf_index = 0; + if (channel_info->pushbuf_index >= channel_info->pushbuf_fence) + channel_info->pushbuf_index = 0; if (!ext_ucode_flag) { setucode_cmd = NVE26E_CH_OPCODE_INCR(NVE276_SET_MICROCODE_A, 3); - index = wordcount + nvavp->pushbuf_index; - writel(setucode_cmd, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(setucode_cmd, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(0, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(0, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(nvavp->ucode_info.phys, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(nvavp->ucode_info.phys, + (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(nvavp->ucode_info.size, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(nvavp->ucode_info.size, + (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); } @@ -485,44 +590,58 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, } /* write commands out */ - index = wordcount + nvavp->pushbuf_index; - writel(gather_cmd, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(gather_cmd, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); - index = wordcount + nvavp->pushbuf_index; - writel(phys_addr, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(phys_addr, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); if (syncpt) { - index = wordcount + nvavp->pushbuf_index; - writel(sync, (nvavp->pushbuf_data + index)); + index = wordcount + channel_info->pushbuf_index; + writel(sync, (channel_info->pushbuf_data + index)); wordcount += sizeof(u32); } /* enable clocks to VDE/BSEV */ - mutex_lock(&nvavp->open_lock); - if (!nvavp->pending) { - nvavp_clks_enable(nvavp); - nvavp->pending = true; + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + mutex_lock(&nvavp->open_lock); + if (!nvavp->pending) { + nvavp_clks_enable(nvavp); + nvavp->pending = true; + } + mutex_unlock(&nvavp->open_lock); } - mutex_unlock(&nvavp->open_lock); /* update put pointer */ - nvavp->pushbuf_index = (nvavp->pushbuf_index + wordcount) & + channel_info->pushbuf_index = (channel_info->pushbuf_index + wordcount)& (NVAVP_PUSHBUFFER_SIZE - 1); - writel(nvavp->pushbuf_index, &control->put); + + writel(channel_info->pushbuf_index, &control->put); wmb(); /* wake up avp */ - writel(0xA0000001, NVAVP_OS_OUTBOX); + if (IS_VIDEO_CHANNEL_ID(channel_id)) { + pr_debug("Wake up Video Channel\n"); + writel(0xA0000001, NVAVP_OS_OUTBOX); + } + else { +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (IS_AUDIO_CHANNEL_ID(channel_id)) { + pr_debug("Wake up Audio Channel\n"); + writel(0xA0000002, NVAVP_OS_OUTBOX); + } +#endif + } /* Fill out fence struct */ if (syncpt) { syncpt->id = nvavp->syncpt_id; syncpt->value = value; } - mutex_unlock(&nvavp->pushbuffer_lock); + mutex_unlock(&channel_info->pushbuffer_lock); return 0; } @@ -547,14 +666,14 @@ static int nvavp_load_ucode(struct nvavp_info *nvavp) sprintf(fw_ucode_file, "nvavp_vid_ucode.bin"); ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { /* Try alternative version */ sprintf(fw_ucode_file, "nvavp_vid_ucode_alt.bin"); ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { dev_err(&nvavp->nvhost_dev->dev, @@ -652,7 +771,7 @@ static int nvavp_load_os(struct nvavp_info *nvavp, char *fw_os_file) if (!os_info->os_bin) { ret = request_firmware(&nvavp_os_fw, fw_os_file, - nvavp->misc_dev.this_device); + nvavp->video_misc_dev.this_device); if (ret) { dev_err(&nvavp->nvhost_dev->dev, "cannot read os firmware '%s'\n", fw_os_file); @@ -717,14 +836,28 @@ err_req_fw: return ret; } -static int nvavp_init(struct nvavp_info *nvavp) + +static int nvavp_os_init(struct nvavp_info *nvavp) { char fw_os_file[32]; int ret = 0; + int video_initialized, audio_initialized = 0; - if (nvavp->initialized) + video_initialized = nvavp_get_video_init_status(nvavp); + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + audio_initialized = nvavp_get_audio_init_status(nvavp); +#endif + pr_debug("video_initialized(%d) audio_initialized(%d)\n", + video_initialized, audio_initialized); + + /* Video and Audio both are initialized */ + if (video_initialized || audio_initialized) return ret; + /* Video or Audio both are uninitialized */ + pr_debug("video_initialized == audio_initialized (%d)\n", + nvavp->video_initialized); #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ /* paddr is any address returned from nvmap_pin */ /* vaddr is AVP_KERNEL_VIRT_BASE */ @@ -768,7 +901,6 @@ static int nvavp_init(struct nvavp_info *nvavp) nvavp->os_info.reset_addr = nvavp->os_info.phys; nvavp->os_info.data = ioremap(nvavp->os_info.phys, SZ_1M); #endif - ret = nvavp_load_os(nvavp, fw_os_file); if (ret) { dev_err(&nvavp->nvhost_dev->dev, @@ -782,21 +914,45 @@ static int nvavp_init(struct nvavp_info *nvavp) "unable to init pushbuffer\n"); goto err_exit; } + tegra_init_legacy_irq_cop(); + enable_irq(nvavp->mbox_from_avp_pend_irq); +err_exit: + return ret; +} - ret = nvavp_load_ucode(nvavp); +static int nvavp_init(struct nvavp_info *nvavp, int channel_id) +{ + int ret = 0; + + ret = nvavp_os_init(nvavp); if (ret) { dev_err(&nvavp->nvhost_dev->dev, - "unable to load ucode\n"); - goto err_exit; + "unable to load os firmware and allocate buffers\n"); } - tegra_init_legacy_irq_cop(); + if (IS_VIDEO_CHANNEL_ID(channel_id) && + (!nvavp_get_video_init_status(nvavp)) ) { + pr_debug("nvavp_init : channel_ID (%d)\n", channel_id); + ret = nvavp_load_ucode(nvavp); + if (ret) { + dev_err(&nvavp->nvhost_dev->dev, + "unable to load ucode\n"); + goto err_exit; + } - nvavp_reset_vde(nvavp); - nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); - enable_irq(nvavp->mbox_from_avp_pend_irq); + nvavp_reset_vde(nvavp); + nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); - nvavp->initialized = 1; + nvavp_set_video_init_status(nvavp, 1); + } +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (IS_AUDIO_CHANNEL_ID(channel_id) && + (!nvavp_get_audio_init_status(nvavp))) { + pr_debug("nvavp_init : channel_ID (%d)\n", channel_id); + nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr); + nvavp_set_audio_init_status(nvavp, 1); + } +#endif err_exit: return ret; @@ -804,22 +960,48 @@ err_exit: static void nvavp_uninit(struct nvavp_info *nvavp) { - if (!nvavp->initialized) + int video_initialized, audio_initialized = 0; + + video_initialized = nvavp_get_video_init_status(nvavp); + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + audio_initialized = nvavp_get_audio_init_status(nvavp); +#endif + + pr_debug("nvavp_uninit video_initialized(%d) audio_initialized(%d)\n", + video_initialized, audio_initialized); + + /* Video and Audio both are uninitialized */ + if (!video_initialized && !audio_initialized) return; - disable_irq(nvavp->mbox_from_avp_pend_irq); + if (video_initialized) { + pr_debug("nvavp_uninit nvavp->video_initialized\n"); + cancel_work_sync(&nvavp->clock_disable_work); - cancel_work_sync(&nvavp->clock_disable_work); + nvavp_halt_vde(nvavp); - nvavp_pushbuffer_deinit(nvavp); + clk_disable(nvavp->sclk); + clk_disable(nvavp->emc_clk); - nvavp_halt_vde(nvavp); - nvavp_halt_avp(nvavp); + nvavp_set_video_init_status(nvavp, 0); + video_initialized = 0; + } - clk_disable(nvavp->sclk); - clk_disable(nvavp->emc_clk); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + if (audio_initialized) { + nvavp_set_audio_init_status(nvavp, 0); + audio_initialized = 0; + } +#endif - nvavp->initialized = 0; + /* Video and Audio both becomes uninitialized */ + if (video_initialized == audio_initialized) { + pr_debug("nvavp_uninit both channels unitialized\n"); + disable_irq(nvavp->mbox_from_avp_pend_irq); + nvavp_pushbuffer_deinit(nvavp); + nvavp_halt_avp(nvavp); + } } static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd, @@ -1010,7 +1192,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, ret = nvavp_pushbuffer_update(nvavp, (phys_addr + hdr.cmdbuf.offset), hdr.cmdbuf.words, &syncpt, - (hdr.flags & NVAVP_UCODE_EXT)); + (hdr.flags & NVAVP_UCODE_EXT), + clientctx->channel_id); if (copy_to_user((void __user *)user_hdr->syncpt, &syncpt, sizeof(struct nvavp_syncpt))) { @@ -1021,7 +1204,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, ret = nvavp_pushbuffer_update(nvavp, (phys_addr + hdr.cmdbuf.offset), hdr.cmdbuf.words, NULL, - (hdr.flags & NVAVP_UCODE_EXT)); + (hdr.flags & NVAVP_UCODE_EXT), + clientctx->channel_id); } err_reloc_info: @@ -1075,7 +1259,7 @@ static int nvavp_force_clock_stay_on_ioctl(struct file *filp, unsigned int cmd, return 0; } -static int tegra_nvavp_open(struct inode *inode, struct file *filp) +static int tegra_nvavp_open(struct inode *inode, struct file *filp, int channel_id) { struct miscdevice *miscdev = filp->private_data; struct nvavp_info *nvavp = dev_get_drvdata(miscdev->parent); @@ -1092,8 +1276,11 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp) mutex_lock(&nvavp->open_lock); - if (!nvavp->refcount) - ret = nvavp_init(nvavp); + pr_debug("tegra_nvavp_open channel_id (%d)\n", channel_id); + + clientctx->channel_id = channel_id; + + ret = nvavp_init(nvavp, channel_id); if (!ret) nvavp->refcount++; @@ -1109,6 +1296,20 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp) return ret; } +static int tegra_nvavp_video_open(struct inode *inode, struct file *filp) +{ + pr_debug("tegra_nvavp_video_open NVAVP_VIDEO_CHANNEL\n"); + return tegra_nvavp_open(inode, filp, NVAVP_VIDEO_CHANNEL); +} + +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static int tegra_nvavp_audio_open(struct inode *inode, struct file *filp) +{ + pr_debug("tegra_nvavp_audio_open NVAVP_AUDIO_CHANNEL\n"); + return tegra_nvavp_open(inode, filp, NVAVP_AUDIO_CHANNEL); +} +#endif + static int tegra_nvavp_release(struct inode *inode, struct file *filp) { struct nvavp_clientctx *clientctx = filp->private_data; @@ -1181,13 +1382,22 @@ static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd, return ret; } -static const struct file_operations tegra_nvavp_fops = { +static const struct file_operations tegra_video_nvavp_fops = { .owner = THIS_MODULE, - .open = tegra_nvavp_open, + .open = tegra_nvavp_video_open, .release = tegra_nvavp_release, .unlocked_ioctl = tegra_nvavp_ioctl, }; +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) +static const struct file_operations tegra_audio_nvavp_fops = { + .owner = THIS_MODULE, + .open = tegra_nvavp_audio_open, + .release = tegra_nvavp_release, + .unlocked_ioctl = tegra_nvavp_ioctl, +}; +#endif + static int tegra_nvavp_probe(struct nvhost_device *ndev, struct nvhost_device_id *id_table) { @@ -1195,7 +1405,7 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev, int irq; unsigned int heap_mask; u32 iovmm_addr; - int ret = 0; + int ret = 0, channel_id; irq = nvhost_get_irq_byname(ndev, "mbox_from_nvavp_pending"); if (irq < 0) { @@ -1306,7 +1516,9 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev, nvavp->mbox_from_avp_pend_irq = irq; mutex_init(&nvavp->open_lock); - mutex_init(&nvavp->pushbuffer_lock); + + for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) + mutex_init(&nvavp->channel_info[channel_id].pushbuffer_lock); /* TODO DO NOT USE NVAVP DEVICE */ nvavp->cop_clk = clk_get(&ndev->dev, "cop"); @@ -1349,18 +1561,32 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev, INIT_WORK(&nvavp->clock_disable_work, clock_disable_handler); - nvavp->misc_dev.minor = MISC_DYNAMIC_MINOR; - nvavp->misc_dev.name = "tegra_avpchannel"; - nvavp->misc_dev.fops = &tegra_nvavp_fops; - nvavp->misc_dev.mode = S_IRWXUGO; - nvavp->misc_dev.parent = &ndev->dev; + nvavp->video_misc_dev.minor = MISC_DYNAMIC_MINOR; + nvavp->video_misc_dev.name = "tegra_avpchannel"; + nvavp->video_misc_dev.fops = &tegra_video_nvavp_fops; + nvavp->video_misc_dev.mode = S_IRWXUGO; + nvavp->video_misc_dev.parent = &ndev->dev; - ret = misc_register(&nvavp->misc_dev); + ret = misc_register(&nvavp->video_misc_dev); if (ret) { dev_err(&ndev->dev, "unable to register misc device!\n"); goto err_misc_reg; } +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + nvavp->audio_misc_dev.minor = MISC_DYNAMIC_MINOR; + nvavp->audio_misc_dev.name = "tegra_audio_avpchannel"; + nvavp->audio_misc_dev.fops = &tegra_audio_nvavp_fops; + nvavp->audio_misc_dev.mode = S_IRWXUGO; + nvavp->audio_misc_dev.parent = &ndev->dev; + + ret = misc_register(&nvavp->audio_misc_dev); + if (ret) { + dev_err(&ndev->dev, "unable to register misc device!\n"); + goto err_audio_misc_reg; + } +#endif + ret = request_irq(irq, nvavp_mbox_pending_isr, 0, TEGRA_NVAVP_NAME, nvavp); if (ret) { @@ -1375,7 +1601,11 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev, return 0; err_req_irq_pend: - misc_deregister(&nvavp->misc_dev); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + misc_deregister(&nvavp->audio_misc_dev); +err_audio_misc_reg: +#endif + misc_deregister(&nvavp->video_misc_dev); err_misc_reg: clk_put(nvavp->emc_clk); err_get_emc_clk: @@ -1421,8 +1651,11 @@ static int tegra_nvavp_remove(struct nvhost_device *ndev) nvavp_unload_ucode(nvavp); nvavp_unload_os(nvavp); - misc_deregister(&nvavp->misc_dev); + misc_deregister(&nvavp->video_misc_dev); +#if defined(CONFIG_TEGRA_NVAVP_AUDIO) + misc_deregister(&nvavp->audio_misc_dev); +#endif clk_put(nvavp->bsev_clk); clk_put(nvavp->vde_clk); clk_put(nvavp->cop_clk); @@ -1463,7 +1696,7 @@ static int tegra_nvavp_resume(struct nvhost_device *ndev) mutex_lock(&nvavp->open_lock); if (nvavp->refcount) - nvavp_init(nvavp); + nvavp_init(nvavp, NVAVP_VIDEO_CHANNEL); mutex_unlock(&nvavp->open_lock); diff --git a/drivers/media/video/tegra/ov2710.c b/drivers/media/video/tegra/ov2710.c index 5e8eaa123124..293cb8932dfb 100644 --- a/drivers/media/video/tegra/ov2710.c +++ b/drivers/media/video/tegra/ov2710.c @@ -21,6 +21,8 @@ #include <linux/uaccess.h> #include <media/ov2710.h> +#define SIZEOF_I2C_TRANSBUF 32 + struct ov2710_reg { u16 addr; u16 val; @@ -30,6 +32,7 @@ struct ov2710_info { int mode; struct i2c_client *i2c_client; struct ov2710_platform_data *pdata; + u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; }; #define OV2710_TABLE_WAIT_MS 0 @@ -152,7 +155,7 @@ static struct ov2710_reg mode_1920x1080[] = { {0x3704, 0x44}, {0x3801, 0xd2}, - {0x3503, 0x17}, + {0x3503, 0x33}, {0x3500, 0x00}, {0x3501, 0x00}, {0x3502, 0x00}, @@ -283,7 +286,7 @@ static struct ov2710_reg mode_1280x720[] = { {0x3704, 0x40}, {0x3801, 0xbc}, - {0x3503, 0x17}, + {0x3503, 0x33}, {0x3500, 0x00}, {0x3501, 0x00}, {0x3502, 0x00}, @@ -400,14 +403,39 @@ static int ov2710_write_reg(struct i2c_client *client, u16 addr, u8 val) return err; } -static int ov2710_write_table(struct i2c_client *client, +static int ov2710_write_bulk_reg(struct i2c_client *client, u8 *data, int len) +{ + int err; + struct i2c_msg msg; + + if (!client->adapter) + return -ENODEV; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + pr_err("ov2710: i2c bulk transfer failed at %x\n", + (int)data[0] << 8 | data[1]); + + return err; +} + +static int ov2710_write_table(struct ov2710_info *info, const struct ov2710_reg table[], const struct ov2710_reg override_list[], int num_override_regs) { int err; - const struct ov2710_reg *next; - int i; + const struct ov2710_reg *next, *n_next; + u8 *b_ptr = info->i2c_trans_buf; + unsigned int buf_filled = 0; + unsigned int i; u16 val; for (next = table; next->addr != OV2710_TABLE_END; next++) { @@ -416,9 +444,7 @@ static int ov2710_write_table(struct i2c_client *client, continue; } - val = next->val; - /* When an override list is passed in, replace the reg */ /* value to write if the reg is in the list */ if (override_list) { @@ -430,9 +456,28 @@ static int ov2710_write_table(struct i2c_client *client, } } - err = ov2710_write_reg(client, next->addr, val); + if (!buf_filled) { + b_ptr = info->i2c_trans_buf; + *b_ptr++ = next->addr >> 8; + *b_ptr++ = next->addr & 0xff; + buf_filled = 2; + } + *b_ptr++ = val; + buf_filled++; + + n_next = next + 1; + if (n_next->addr != OV2710_TABLE_END && + n_next->addr != OV2710_TABLE_WAIT_MS && + buf_filled < SIZEOF_I2C_TRANSBUF && + n_next->addr == next->addr + 1) { + continue; + } + + err = ov2710_write_bulk_reg(info->i2c_client, + info->i2c_trans_buf, buf_filled); if (err) return err; + buf_filled = 0; } return 0; } @@ -463,7 +508,7 @@ static int ov2710_set_mode(struct ov2710_info *info, struct ov2710_mode *mode) ov2710_get_coarse_time_regs(reg_list + 2, mode->coarse_time); ov2710_get_gain_reg(reg_list + 5, mode->gain); - err = ov2710_write_table(info->i2c_client, mode_table[sensor_mode], + err = ov2710_write_table(info, mode_table[sensor_mode], reg_list, 6); if (err) return err; @@ -474,51 +519,37 @@ static int ov2710_set_mode(struct ov2710_info *info, struct ov2710_mode *mode) static int ov2710_set_frame_length(struct ov2710_info *info, u32 frame_length) { - struct ov2710_reg reg_list[2]; - int i = 0; int ret; + struct ov2710_reg reg_list[2]; + u8 *b_ptr = info->i2c_trans_buf; ov2710_get_frame_length_regs(reg_list, frame_length); - for (i = 0; i < 2; i++) { - ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } + *b_ptr++ = reg_list[0].addr >> 8; + *b_ptr++ = reg_list[0].addr & 0xff; + *b_ptr++ = reg_list[0].val & 0xff; + *b_ptr++ = reg_list[1].val & 0xff; + ret = ov2710_write_bulk_reg(info->i2c_client, info->i2c_trans_buf, 4); - return 0; + return ret; } static int ov2710_set_coarse_time(struct ov2710_info *info, u32 coarse_time) { int ret; - struct ov2710_reg reg_list[3]; - int i = 0; + u8 *b_ptr = info->i2c_trans_buf; ov2710_get_coarse_time_regs(reg_list, coarse_time); - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x01); - if (ret) - return ret; - - for (i = 0; i < 3; i++) { - ret = ov2710_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } - - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x11); - if (ret) - return ret; + *b_ptr++ = reg_list[0].addr >> 8; + *b_ptr++ = reg_list[0].addr & 0xff; + *b_ptr++ = reg_list[0].val & 0xff; + *b_ptr++ = reg_list[1].val & 0xff; + *b_ptr++ = reg_list[2].val & 0xff; + ret = ov2710_write_bulk_reg(info->i2c_client, info->i2c_trans_buf, 5); - ret = ov2710_write_reg(info->i2c_client, 0x3212, 0xa1); - if (ret) - return ret; - - return 0; + return ret; } static int ov2710_set_gain(struct ov2710_info *info, u16 gain) @@ -533,6 +564,48 @@ static int ov2710_set_gain(struct ov2710_info *info, u16 gain) return ret; } +static int ov2710_set_group_hold(struct ov2710_info *info, struct ov2710_ae *ae) +{ + int ret; + int count = 0; + bool groupHoldEnabled = false; + + if (ae->gain_enable) + count++; + if (ae->coarse_time_enable) + count++; + if (ae->frame_length_enable) + count++; + if (count >= 2) + groupHoldEnabled = true; + + if (groupHoldEnabled) { + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x01); + if (ret) + return ret; + } + + if (ae->gain_enable) + ov2710_set_gain(info, ae->gain); + if (ae->coarse_time_enable) + ov2710_set_coarse_time(info, ae->coarse_time); + if (ae->frame_length_enable) + ov2710_set_frame_length(info, ae->frame_length); + + if (groupHoldEnabled) { + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0x11); + if (ret) + return ret; + + ret = ov2710_write_reg(info->i2c_client, 0x3212, 0xa1); + if (ret) + return ret; + } + + return 0; +} + + static int ov2710_get_status(struct ov2710_info *info, u8 *status) { int err; @@ -567,6 +640,17 @@ static long ov2710_ioctl(struct file *file, return ov2710_set_coarse_time(info, (u32)arg); case OV2710_IOCTL_SET_GAIN: return ov2710_set_gain(info, (u16)arg); + case OV2710_IOCTL_SET_GROUP_HOLD: + { + struct ov2710_ae ae; + if (copy_from_user(&ae, + (const void __user *)arg, + sizeof(struct ov2710_ae))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return ov2710_set_group_hold(info, &ae); + } case OV2710_IOCTL_GET_STATUS: { u8 status; diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c index f41b44ce9530..4c7ef5aeaeab 100644 --- a/drivers/media/video/tegra/sh532u.c +++ b/drivers/media/video/tegra/sh532u.c @@ -532,7 +532,7 @@ static int sh532u_vreg_init(struct sh532u_info *info) else dev_info(&info->i2c_client->dev, "%s no regulator found for %s. " - "This board may not have an" + "This board may not have an " "independent %s regulator.\n", __func__, info->vreg[j].vreg_name, info->vreg[j].vreg_name); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f89c3eedee36..a4931c756205 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -175,7 +175,6 @@ config MFD_TPS65910 bool "TPS65910 Power Management chip" depends on I2C=y && GPIOLIB select MFD_CORE - select GPIO_TPS65910 select REGMAP_I2C help if you say yes here you get support for the TPS65910 series of diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 99ef944c621d..cdc1df7fa0e9 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -75,49 +75,12 @@ static struct deepsleep_control_data deepsleep_data[] = { (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) static struct mfd_cell rc5t583_subdevs[] = { + {.name = "rc5t583-gpio",}, {.name = "rc5t583-regulator",}, {.name = "rc5t583-rtc", }, {.name = "rc5t583-key", } }; -int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_write(rc5t583->regmap, reg, val); -} - -int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - unsigned int ival; - int ret; - ret = regmap_read(rc5t583->regmap, reg, &ival); - if (!ret) - *val = (uint8_t)ival; - return ret; -} - -int rc5t583_set_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); -} - -int rc5t583_clear_bits(struct device *dev, unsigned int reg, - unsigned int bit_mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); -} - -int rc5t583_update(struct device *dev, unsigned int reg, - unsigned int val, unsigned int mask) -{ - struct rc5t583 *rc5t583 = dev_get_drvdata(dev); - return regmap_update_bits(rc5t583->regmap, reg, mask, val); -} - static int __rc5t583_set_ext_pwrreq1_control(struct device *dev, int id, int ext_pwr, int slots) { @@ -197,6 +160,7 @@ int rc5t583_ext_power_req_config(struct device *dev, int ds_id, ds_id, ext_pwr_req); return 0; } +EXPORT_SYMBOL(rc5t583_ext_power_req_config); static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583, struct rc5t583_platform_data *pdata) @@ -304,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, rc5t583->dev = &i2c->dev; i2c_set_clientdata(i2c, rc5t583); - rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config); if (IS_ERR(rc5t583->regmap)) { ret = PTR_ERR(rc5t583->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); @@ -313,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, ret = rc5t583_clear_ext_power_req(rc5t583, pdata); if (ret < 0) - goto err_irq_init; + return ret; if (i2c->irq) { ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); @@ -336,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, err_add_devs: if (irq_init_success) rc5t583_irq_exit(rc5t583); -err_irq_init: - regmap_exit(rc5t583->regmap); return ret; } @@ -347,7 +309,6 @@ static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(rc5t583->dev); rc5t583_irq_exit(rc5t583); - regmap_exit(rc5t583->regmap); return 0; } diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index c9ed5c00a621..0f1ff7fbdc74 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -41,28 +41,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, static irqreturn_t tps65910_irq(int irq, void *irq_data) { struct tps65910 *tps65910 = irq_data; + unsigned int reg; u32 irq_sts; u32 irq_mask; - u8 reg; int i; - tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); irq_sts = reg; - tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); irq_sts |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); irq_sts |= reg << 16; } - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); irq_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); irq_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); irq_mask |= reg << 16; } @@ -82,13 +82,13 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data) /* Write the STS register back to clear IRQs we handled */ reg = irq_sts & 0xFF; irq_sts >>= 8; - tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); reg = irq_sts & 0xFF; - tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = irq_sts >> 8; - tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); } return IRQ_HANDLED; @@ -105,27 +105,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); u32 reg_mask; - u8 reg; + unsigned int reg; - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); reg_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); reg_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); reg_mask |= reg << 16; } if (tps65910->irq_mask != reg_mask) { reg = tps65910->irq_mask & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = tps65910->irq_mask >> 16; - tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); } } mutex_unlock(&tps65910->irq_lock); diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 1c4f53efee74..18b30cf45e5b 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,13 +19,16 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/mfd/core.h> #include <linux/regmap.h> #include <linux/mfd/tps65910.h> +#include <linux/of_device.h> static struct mfd_cell tps65910s[] = { { + .name = "tps65910-gpio", + }, + { .name = "tps65910-pmic", }, { @@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = { }; -static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, - int bytes, void *dest) -{ - return regmap_bulk_read(tps65910->regmap, reg, dest, bytes); -} - -static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, - int bytes, void *src) -{ - return regmap_bulk_write(tps65910->regmap, reg, src, bytes); -} - -int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, mask); -} -EXPORT_SYMBOL_GPL(tps65910_set_bits); - -int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, 0); -} -EXPORT_SYMBOL_GPL(tps65910_clear_bits); - static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); @@ -81,84 +60,212 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) return true; } -static const struct regmap_config rc5t583_regmap_config = { +static const struct regmap_config tps65910_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = is_volatile_reg, - .max_register = TPS65910_MAX_REGISTER, - .num_reg_defaults_raw = TPS65910_MAX_REGISTER, + .max_register = TPS65910_MAX_REGISTER - 1, .cache_type = REGCACHE_RBTREE, }; -static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = NULL; + int ret = 0; + + dev = tps65910->dev; + + if (!pmic_pdata->en_dev_slp) + return 0; + + /* enabling SLEEP device state */ + ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + if (ret < 0) { + dev_err(dev, "set dev_slp failed: %d\n", ret); + goto err_sleep_init; + } + + /* Return if there is no sleep keepon data. */ + if (!pmic_pdata->slp_keepon) + return 0; + + if (pmic_pdata->slp_keepon->therm_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set therm_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->clkout32k_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set clkout32k_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->i2chs_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set i2chs_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + return 0; + +disable_dev_slp: + tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + +err_sleep_init: + return ret; +} + +#ifdef CONFIG_OF +static struct of_device_id tps65910_of_match[] = { + { .compatible = "ti,tps65910", .data = (void *)TPS65910}, + { .compatible = "ti,tps65911", .data = (void *)TPS65911}, + { }, +}; +MODULE_DEVICE_TABLE(of, tps65910_of_match); + +static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + struct device_node *np = client->dev.of_node; + struct tps65910_board *board_info; + unsigned int prop; + const struct of_device_id *match; + unsigned int prop_array[TPS6591X_MAX_NUM_GPIO]; + int ret = 0; + int idx; + + match = of_match_device(tps65910_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + *chip_id = (int)match->data; + + board_info = devm_kzalloc(&client->dev, sizeof(*board_info), + GFP_KERNEL); + if (!board_info) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop); + if (!ret) + board_info->vmbch_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH-Threshold not specified"); + + ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop); + if (!ret) + board_info->vmbch2_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH2-Threshold not specified"); + + ret = of_property_read_u32_array(np, "ti,en-gpio-sleep", + prop_array, TPS6591X_MAX_NUM_GPIO); + if (!ret) + for (idx = 0; idx < ARRAY_SIZE(prop_array); idx++) + board_info->en_gpio_sleep[idx] = (prop_array[idx] != 0); + else if (ret != -EINVAL) { + dev_err(&client->dev, + "error reading property ti,en-gpio-sleep: %d\n.", ret); + return NULL; + } + + + board_info->irq = client->irq; + board_info->irq_base = -1; + board_info->gpio_base = -1; + + return board_info; +} +#else +static inline +struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + return NULL; +} +#endif + +static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; struct tps65910_platform_data *init_data; int ret = 0; + int chip_id = id->driver_data; pmic_plat_data = dev_get_platdata(&i2c->dev); + + if (!pmic_plat_data && i2c->dev.of_node) + pmic_plat_data = tps65910_parse_dt(i2c, &chip_id); + if (!pmic_plat_data) return -EINVAL; - init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL); + init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL); if (init_data == NULL) return -ENOMEM; - tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL); - if (tps65910 == NULL) { - kfree(init_data); + tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL); + if (tps65910 == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, tps65910); tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; - tps65910->id = id->driver_data; - tps65910->read = tps65910_i2c_read; - tps65910->write = tps65910_i2c_write; + tps65910->id = chip_id; mutex_init(&tps65910->io_mutex); - tps65910->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); - goto regmap_err; + return ret; } ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), NULL, 0); - if (ret < 0) - goto err; + if (ret < 0) { + dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; - tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); - tps65910_irq_init(tps65910, init_data->irq, init_data); - kfree(init_data); - return ret; + tps65910_sleepinit(tps65910, pmic_plat_data); -err: - regmap_exit(tps65910->regmap); -regmap_err: - kfree(tps65910); - kfree(init_data); return ret; } -static int tps65910_i2c_remove(struct i2c_client *i2c) +static __devexit int tps65910_i2c_remove(struct i2c_client *i2c) { struct tps65910 *tps65910 = i2c_get_clientdata(i2c); tps65910_irq_exit(tps65910); mfd_remove_devices(tps65910->dev); - regmap_exit(tps65910->regmap); - kfree(tps65910); return 0; } @@ -175,9 +282,10 @@ static struct i2c_driver tps65910_i2c_driver = { .driver = { .name = "tps65910", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65910_of_match), }, .probe = tps65910_i2c_probe, - .remove = tps65910_i2c_remove, + .remove = __devexit_p(tps65910_i2c_remove), .id_table = tps65910_i2c_id, }; diff --git a/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c index ed44cdefe43b..8a60be2298c8 100644 --- a/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c +++ b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c @@ -1082,7 +1082,7 @@ woal_cfg80211_assoc(moal_private * priv, void *sme) goto done; } - if (channel) { + if (mode == MLAN_BSS_MODE_IBSS && channel) { /* Get the secondary channel offset */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); if (req == NULL) { diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67093e3f1cd7..28c58a1c19b1 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -257,6 +257,16 @@ config REGULATOR_AB3100 AB3100 analog baseband dealing with power regulators for the system. +config REGULATOR_RC5T583 + tristate "RICOH RC5T583 Power regulators" + depends on MFD_RC5T583 + help + Select this option to enable the power regulator of RICOH + PMIC RC5T583. + This driver supports the control of different power rails of device + through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + config REGULATOR_TPS6105X tristate "TI TPS6105X Power regulators" depends on TPS6105X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 7642d39fb8d3..a25ff34afcbc 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c new file mode 100644 index 000000000000..81a03b281fa0 --- /dev/null +++ b/drivers/regulator/rc5t583-regulator.c @@ -0,0 +1,363 @@ +/* + * Regulator driver for RICOH RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/gpio.h> +#include <linux/mfd/rc5t583.h> + +struct rc5t583_regulator_info { + int deepsleep_id; + + /* Regulator register address.*/ + uint8_t reg_en_reg; + uint8_t en_bit; + uint8_t reg_disc_reg; + uint8_t disc_bit; + uint8_t vout_reg; + uint8_t vout_mask; + uint8_t deepsleep_reg; + + /* Chip constraints on regulator behavior */ + int min_uV; + int max_uV; + int step_uV; + + /* Regulator specific turn-on delay and voltage settling time*/ + int enable_uv_per_us; + int change_uv_per_us; + + /* Used by regulator core */ + struct regulator_desc desc; +}; + +struct rc5t583_regulator { + struct rc5t583_regulator_info *reg_info; + + /* Devices */ + struct device *dev; + struct rc5t583 *mfd; + struct regulator_dev *rdev; +}; + +static int rc5t583_reg_is_enabled(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t control; + int ret; + + ret = rc5t583_read(reg->mfd->dev, ri->reg_en_reg, &control); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading the control register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return !!(control & BIT(ri->en_bit)); +} + +static int rc5t583_reg_enable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_set_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in setting bit of STATE register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return ret; +} + +static int rc5t583_reg_disable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_clear_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) + dev_err(&rdev->dev, + "Error in clearing bit of STATE register 0x%02x\n", + ri->reg_en_reg); + + return ret; +} + +static int rc5t583_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + return ri->min_uV + (ri->step_uV * selector); +} + +static int rc5t583_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + if (selector >= rdev->desc->n_voltages) { + dev_err(&rdev->dev, "Invalid selector 0x%02x\n", selector); + return -EINVAL; + } + + ret = rc5t583_update(reg->mfd->dev, ri->vout_reg, + selector, ri->vout_mask); + if (ret < 0) + dev_err(&rdev->dev, + "Error in update voltage register 0x%02x\n", ri->vout_reg); + return ret; +} + +static int rc5t583_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t vsel; + int ret; + ret = rc5t583_read(reg->mfd->dev, ri->vout_reg, &vsel); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading voltage register 0x%02x\n", ri->vout_reg); + return ret; + } + return vsel & ri->vout_mask; +} + +static int rc5t583_regulator_enable_time(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int vsel = rc5t583_get_voltage_sel(rdev); + int curr_uV = rc5t583_list_voltage(rdev, vsel); + return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us); +} + +static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int old_uV, new_uV; + old_uV = rc5t583_list_voltage(rdev, old_selector); + + if (old_uV < 0) + return old_uV; + + new_uV = rc5t583_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), + reg->reg_info->change_uv_per_us); +} + + +static struct regulator_ops rc5t583_ops = { + .is_enabled = rc5t583_reg_is_enabled, + .enable = rc5t583_reg_enable, + .disable = rc5t583_reg_disable, + .enable_time = rc5t583_regulator_enable_time, + .get_voltage_sel = rc5t583_get_voltage_sel, + .set_voltage_sel = rc5t583_set_voltage_sel, + .list_voltage = rc5t583_list_voltage, + .set_voltage_time_sel = rc5t583_set_voltage_time_sel, +}; + +#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \ + _vout_mask, _ds_reg, _min_mv, _max_mv, _step_uV, _enable_mv) \ +{ \ + .reg_en_reg = RC5T583_REG_##_en_reg, \ + .en_bit = _en_bit, \ + .reg_disc_reg = RC5T583_REG_##_disc_reg, \ + .disc_bit = _disc_bit, \ + .vout_reg = RC5T583_REG_##_vout_reg, \ + .vout_mask = _vout_mask, \ + .deepsleep_reg = RC5T583_REG_##_ds_reg, \ + .min_uV = _min_mv * 1000, \ + .max_uV = _max_mv * 1000, \ + .step_uV = _step_uV, \ + .enable_uv_per_us = _enable_mv * 1000, \ + .change_uv_per_us = 40 * 1000, \ + .deepsleep_id = RC5T583_DS_##_id, \ + .desc = { \ + .name = "rc5t583-regulator-"#_id, \ + .id = RC5T583_REGULATOR_##_id, \ + .n_voltages = (_max_mv - _min_mv) * 1000 / _step_uV + 1, \ + .ops = &rc5t583_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { + RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, DC0DAC, 0x7F, DC0DAC_DS, + 700, 1500, 12500, 4), + RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, DC1DAC, 0x7F, DC1DAC_DS, + 700, 1500, 12500, 14), + RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, DC2DAC, 0x7F, DC2DAC_DS, + 900, 2400, 12500, 14), + RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, DC3DAC, 0x7F, DC3DAC_DS, + 900, 2400, 12500, 14), + RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, LDO0DAC, 0x7F, LDO0DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, LDO1DAC, 0x7F, LDO1DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, LDO2DAC, 0x7F, LDO2DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, LDO3DAC, 0x7F, LDO3DAC_DS, + 900, 3400, 25000, 160), + RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, LDO4DAC, 0x3F, LDO4DAC_DS, + 750, 1500, 12500, 133), + RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, LDO5DAC, 0x7F, LDO5DAC_DS, + 900, 3400, 25000, 267), + RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, LDO6DAC, 0x7F, LDO6DAC_DS, + 900, 3400, 25000, 133), + RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, LDO7DAC, 0x7F, LDO7DAC_DS, + 900, 3400, 25000, 233), + RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, LDO8DAC, 0x7F, LDO8DAC_DS, + 900, 3400, 25000, 233), + RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, LDO9DAC, 0x7F, LDO9DAC_DS, + 900, 3400, 25000, 133), +}; + +static int __devinit rc5t583_regulator_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct regulator_init_data *reg_data; + struct rc5t583_regulator *reg = NULL; + struct rc5t583_regulator *regs; + struct regulator_dev *rdev; + struct rc5t583_regulator_info *ri; + int ret; + int id; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * + sizeof(struct rc5t583_regulator), GFP_KERNEL); + if (!regs) { + dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + return -ENOMEM; + } + + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { + reg_data = pdata->reg_init_data[id]; + + /* No need to register if there is no regulator data */ + if (!reg_data) + continue; + + reg = ®s[id]; + ri = &rc5t583_reg_info[id]; + reg->reg_info = ri; + reg->mfd = rc5t583; + reg->dev = &pdev->dev; + + if (ri->deepsleep_id == RC5T583_DS_NONE) + goto skip_ext_pwr_config; + + ret = rc5t583_ext_power_req_config(rc5t583->dev, + ri->deepsleep_id, + pdata->regulator_ext_pwr_control[id], + pdata->regulator_deepsleep_slot[id]); + /* + * Configuring external control is not a major issue, + * just give warning. + */ + if (ret < 0) + dev_warn(&pdev->dev, + "Failed to configure ext control %d\n", id); + +skip_ext_pwr_config: + rdev = regulator_register(&ri->desc, &pdev->dev, reg_data, reg); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + ret = PTR_ERR(rdev); + goto clean_exit; + } + reg->rdev = rdev; + } + platform_set_drvdata(pdev, regs); + return 0; + +clean_exit: + while (--id >= 0) + regulator_unregister(regs[id].rdev); + + return ret; +} + +static int __devexit rc5t583_regulator_remove(struct platform_device *pdev) +{ + struct rc5t583_regulator *regs = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) + regulator_unregister(regs[id].rdev); + return 0; +} + +static struct platform_driver rc5t583_regulator_driver = { + .driver = { + .name = "rc5t583-regulator", + .owner = THIS_MODULE, + }, + .probe = rc5t583_regulator_probe, + .remove = __devexit_p(rc5t583_regulator_remove), +}; + +static int __init rc5t583_regulator_init(void) +{ + return platform_driver_register(&rc5t583_regulator_driver); +} +subsys_initcall(rc5t583_regulator_init); + +static void __exit rc5t583_regulator_exit(void) +{ + platform_driver_unregister(&rc5t583_regulator_driver); +} +module_exit(rc5t583_regulator_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("RC5T583 regulator driver"); +MODULE_ALIAS("platform:rc5t583-regulator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 7eaf08275376..7db148202436 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -1,7 +1,7 @@ /* * tps62360.c -- TI tps62360 * - * Driver for processor core supply tps62360 and tps62361B + * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363. * * Copyright (c) 2012, NVIDIA Corporation. * @@ -46,20 +46,20 @@ #define REG_RAMPCTRL 6 #define REG_CHIPID 8 -enum chips {TPS62360, TPS62361}; +#define FORCE_PWM_ENABLE BIT(7) -#define TPS62360_BASE_VOLTAGE 770 +enum chips {TPS62360, TPS62361, TPS62362, TPS62363}; + +#define TPS62360_BASE_VOLTAGE 770000 #define TPS62360_N_VOLTAGES 64 -#define TPS62361_BASE_VOLTAGE 500 +#define TPS62361_BASE_VOLTAGE 500000 #define TPS62361_N_VOLTAGES 128 /* tps 62360 chip information */ struct tps62360_chip { - const char *name; struct device *dev; struct regulator_desc desc; - struct i2c_client *client; struct regulator_dev *rdev; struct regmap *regmap; int chip_id; @@ -68,12 +68,12 @@ struct tps62360_chip { int voltage_base; u8 voltage_reg_mask; bool en_internal_pulldn; - bool en_force_pwm; bool en_discharge; bool valid_gpios; int lru_index[4]; int curr_vset_vsel[4]; int curr_vset_id; + int change_uv_per_us; }; /* @@ -99,6 +99,7 @@ static bool find_voltage_set_register(struct tps62360_chip *tps, bool found = false; int new_vset_reg = tps->lru_index[3]; int found_index = 3; + for (i = 0; i < 4; ++i) { if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) { new_vset_reg = tps->lru_index[i]; @@ -117,7 +118,7 @@ update_lru_index: return found; } -static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) +static int tps62360_dcdc_get_voltage_sel(struct regulator_dev *dev) { struct tps62360_chip *tps = rdev_get_drvdata(dev); int vsel; @@ -126,12 +127,12 @@ static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); if (ret < 0) { - dev_err(tps->dev, "%s: Error in reading register %d\n", - __func__, REG_VSET0 + tps->curr_vset_id); + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); return ret; } vsel = (int)data & tps->voltage_reg_mask; - return (tps->voltage_base + vsel * 10) * 1000; + return vsel; } static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, @@ -143,17 +144,13 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, bool found = false; int new_vset_id = tps->curr_vset_id; - if (max_uV < min_uV) - return -EINVAL; - - if (min_uV > - ((tps->voltage_base + (tps->desc.n_voltages - 1) * 10) * 1000)) + if ((max_uV < min_uV) || (max_uV < tps->voltage_base)) return -EINVAL; - if (max_uV < tps->voltage_base * 1000) + if (min_uV > (tps->voltage_base + (tps->desc.n_voltages - 1) * 10000)) return -EINVAL; - vsel = DIV_ROUND_UP(min_uV - (tps->voltage_base * 1000), 10000); + vsel = DIV_ROUND_UP(min_uV - tps->voltage_base, 10000); if (selector) *selector = (vsel & tps->voltage_reg_mask); @@ -168,8 +165,9 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, tps->voltage_reg_mask, vsel); if (ret < 0) { - dev_err(tps->dev, "%s: Error in updating register %d\n", - __func__, REG_VSET0 + new_vset_id); + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + new_vset_id, ret); return ret; } tps->curr_vset_id = new_vset_id; @@ -178,8 +176,7 @@ static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, /* Select proper VSET register vio gpios */ if (tps->valid_gpios) { - gpio_set_value_cansleep(tps->vsel0_gpio, - new_vset_id & 0x1); + gpio_set_value_cansleep(tps->vsel0_gpio, new_vset_id & 0x1); gpio_set_value_cansleep(tps->vsel1_gpio, (new_vset_id >> 1) & 0x1); } @@ -191,82 +188,146 @@ static int tps62360_dcdc_list_voltage(struct regulator_dev *dev, { struct tps62360_chip *tps = rdev_get_drvdata(dev); - if ((selector < 0) || (selector >= tps->desc.n_voltages)) + if (selector >= tps->desc.n_voltages) return -EINVAL; - return (tps->voltage_base + selector * 10) * 1000; + + return tps->voltage_base + selector * 10000; } -static struct regulator_ops tps62360_dcdc_ops = { - .get_voltage = tps62360_dcdc_get_voltage, - .set_voltage = tps62360_dcdc_set_voltage, - .list_voltage = tps62360_dcdc_list_voltage, -}; +static int tps62360_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + int old_uV, new_uV; -static int tps62360_init_force_pwm(struct tps62360_chip *tps, - struct tps62360_regulator_platform_data *pdata, - int vset_id) + old_uV = tps62360_dcdc_list_voltage(rdev, old_selector); + if (old_uV < 0) + return old_uV; + + new_uV = tps62360_dcdc_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), tps->change_uv_per_us); +} + +static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) { + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + int i; + int val; + int ret; + + /* Enable force PWM mode in FAST mode only. */ + switch (mode) { + case REGULATOR_MODE_FAST: + val = FORCE_PWM_ENABLE; + break; + + case REGULATOR_MODE_NORMAL: + val = 0; + break; + + default: + return -EINVAL; + } + + if (!tps->valid_gpios) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + tps->curr_vset_id, FORCE_PWM_ENABLE, val); + if (ret < 0) + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); + return ret; + } + + /* If gpios are valid then all register set need to be control */ + for (i = 0; i < 4; ++i) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + i, FORCE_PWM_ENABLE, val); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + i, ret); + return ret; + } + } + return ret; +} + +static unsigned int tps62360_get_mode(struct regulator_dev *rdev) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); unsigned int data; int ret; - ret = regmap_read(tps->regmap, REG_VSET0 + vset_id, &data); + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); if (ret < 0) { - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_VSET0 + vset_id); + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); return ret; } - tps->curr_vset_vsel[vset_id] = data & tps->voltage_reg_mask; - if (pdata->en_force_pwm) - data |= BIT(7); - else - data &= ~BIT(7); - ret = regmap_write(tps->regmap, REG_VSET0 + vset_id, data); - if (ret < 0) - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_VSET0 + vset_id); - return ret; + return (data & FORCE_PWM_ENABLE) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static int tps62360_init_dcdc(struct tps62360_chip *tps, +static struct regulator_ops tps62360_dcdc_ops = { + .get_voltage_sel = tps62360_dcdc_get_voltage_sel, + .set_voltage = tps62360_dcdc_set_voltage, + .list_voltage = tps62360_dcdc_list_voltage, + .set_voltage_time_sel = tps62360_set_voltage_time_sel, + .set_mode = tps62360_set_mode, + .get_mode = tps62360_get_mode, +}; + +static int __devinit tps62360_init_dcdc(struct tps62360_chip *tps, struct tps62360_regulator_platform_data *pdata) { int ret; - int i; + unsigned int ramp_ctrl; - /* Initailize internal pull up/down control */ + /* Initialize internal pull up/down control */ if (tps->en_internal_pulldn) ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0); else ret = regmap_write(tps->regmap, REG_CONTROL, 0x0); if (ret < 0) { - dev_err(tps->dev, "%s() fails in writing reg %d\n", - __func__, REG_CONTROL); + dev_err(tps->dev, + "%s(): register %d write failed with err %d\n", + __func__, REG_CONTROL, ret); return ret; } - /* Initailize force PWM mode */ - if (tps->valid_gpios) { - for (i = 0; i < 4; ++i) { - ret = tps62360_init_force_pwm(tps, pdata, i); - if (ret < 0) - return ret; - } - } else { - ret = tps62360_init_force_pwm(tps, pdata, tps->curr_vset_id); - if (ret < 0) - return ret; - } - /* Reset output discharge path to reduce power consumption */ ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0); - if (ret < 0) - dev_err(tps->dev, "%s() fails in updating reg %d\n", - __func__, REG_RAMPCTRL); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + + /* Get ramp value from ramp control register */ + ret = regmap_read(tps->regmap, REG_RAMPCTRL, &ramp_ctrl); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d read failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + ramp_ctrl = (ramp_ctrl >> 4) & 0x7; + + /* ramp mV/us = 32/(2^ramp_ctrl) */ + tps->change_uv_per_us = DIV_ROUND_UP(32000, BIT(ramp_ctrl)); return ret; } static const struct regmap_config tps62360_regmap_config = { - .reg_bits = 8, - .val_bits = 8, + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_CHIPID, + .cache_type = REGCACHE_RBTREE, }; static int __devinit tps62360_probe(struct i2c_client *client, @@ -280,42 +341,52 @@ static int __devinit tps62360_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (!pdata) { - dev_err(&client->dev, "%s() Err: Platform data not found\n", + dev_err(&client->dev, "%s(): Platform data not found\n", __func__); return -EIO; } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) { - dev_err(&client->dev, "%s() Err: Memory allocation fails\n", + dev_err(&client->dev, "%s(): Memory allocation failed\n", __func__); return -ENOMEM; } - tps->en_force_pwm = pdata->en_force_pwm; tps->en_discharge = pdata->en_discharge; tps->en_internal_pulldn = pdata->en_internal_pulldn; tps->vsel0_gpio = pdata->vsel0_gpio; tps->vsel1_gpio = pdata->vsel1_gpio; - tps->client = client; tps->dev = &client->dev; - tps->name = id->name; - tps->voltage_base = (id->driver_data == TPS62360) ? - TPS62360_BASE_VOLTAGE : TPS62361_BASE_VOLTAGE; - tps->voltage_reg_mask = (id->driver_data == TPS62360) ? 0x3F : 0x7F; + + switch (id->driver_data) { + case TPS62360: + case TPS62362: + tps->voltage_base = TPS62360_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x3F; + tps->desc.n_voltages = TPS62360_N_VOLTAGES; + break; + case TPS62361: + case TPS62363: + tps->voltage_base = TPS62361_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x7F; + tps->desc.n_voltages = TPS62361_N_VOLTAGES; + break; + default: + return -ENODEV; + } tps->desc.name = id->name; tps->desc.id = 0; - tps->desc.n_voltages = (id->driver_data == TPS62360) ? - TPS62360_N_VOLTAGES : TPS62361_N_VOLTAGES; tps->desc.ops = &tps62360_dcdc_ops; tps->desc.type = REGULATOR_VOLTAGE; tps->desc.owner = THIS_MODULE; - tps->regmap = regmap_init_i2c(client, &tps62360_regmap_config); + tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); - dev_err(&client->dev, "%s() Err: Failed to allocate register" - "map: %d\n", __func__, ret); + dev_err(&client->dev, + "%s(): regmap allocation failed with err %d\n", + __func__, ret); return ret; } i2c_set_clientdata(client, tps); @@ -326,35 +397,26 @@ static int __devinit tps62360_probe(struct i2c_client *client, tps->valid_gpios = false; if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) { - ret = gpio_request(tps->vsel0_gpio, "tps62360-vsel0"); + int gpio_flags; + gpio_flags = (pdata->vsel0_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(tps->vsel0_gpio, + gpio_flags, "tps62360-vsel0"); if (ret) { dev_err(&client->dev, - "Err: Could not obtain vsel0 GPIO %d: %d\n", - tps->vsel0_gpio, ret); - goto err_gpio0; - } - ret = gpio_direction_output(tps->vsel0_gpio, - pdata->vsel0_def_state); - if (ret) { - dev_err(&client->dev, "Err: Could not set direction of" - "vsel0 GPIO %d: %d\n", tps->vsel0_gpio, ret); - gpio_free(tps->vsel0_gpio); + "%s(): Could not obtain vsel0 GPIO %d: %d\n", + __func__, tps->vsel0_gpio, ret); goto err_gpio0; } - ret = gpio_request(tps->vsel1_gpio, "tps62360-vsel1"); + gpio_flags = (pdata->vsel1_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = gpio_request_one(tps->vsel1_gpio, + gpio_flags, "tps62360-vsel1"); if (ret) { dev_err(&client->dev, - "Err: Could not obtain vsel1 GPIO %d: %d\n", - tps->vsel1_gpio, ret); - goto err_gpio1; - } - ret = gpio_direction_output(tps->vsel1_gpio, - pdata->vsel1_def_state); - if (ret) { - dev_err(&client->dev, "Err: Could not set direction of" - "vsel1 GPIO %d: %d\n", tps->vsel1_gpio, ret); - gpio_free(tps->vsel1_gpio); + "%s(): Could not obtain vsel1 GPIO %d: %d\n", + __func__, tps->vsel1_gpio, ret); goto err_gpio1; } tps->valid_gpios = true; @@ -371,7 +433,7 @@ static int __devinit tps62360_probe(struct i2c_client *client, ret = tps62360_init_dcdc(tps, pdata); if (ret < 0) { - dev_err(tps->dev, "%s() Err: Init fails with = %d\n", + dev_err(tps->dev, "%s(): Init failed with err = %d\n", __func__, ret); goto err_init; } @@ -380,8 +442,9 @@ static int __devinit tps62360_probe(struct i2c_client *client, rdev = regulator_register(&tps->desc, &client->dev, &pdata->reg_init_data, tps); if (IS_ERR(rdev)) { - dev_err(tps->dev, "%s() Err: Failed to register %s\n", - __func__, id->name); + dev_err(tps->dev, + "%s(): regulator register failed with err %s\n", + __func__, id->name); ret = PTR_ERR(rdev); goto err_init; } @@ -396,7 +459,6 @@ err_gpio1: if (gpio_is_valid(tps->vsel0_gpio)) gpio_free(tps->vsel0_gpio); err_gpio0: - regmap_exit(tps->regmap); return ret; } @@ -417,7 +479,6 @@ static int __devexit tps62360_remove(struct i2c_client *client) gpio_free(tps->vsel0_gpio); regulator_unregister(tps->rdev); - regmap_exit(tps->regmap); return 0; } @@ -432,13 +493,16 @@ static void tps62360_shutdown(struct i2c_client *client) /* Configure the output discharge path */ st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2)); if (st < 0) - dev_err(tps->dev, "%s() fails in updating reg %d\n", - __func__, REG_RAMPCTRL); + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, st); } static const struct i2c_device_id tps62360_id[] = { {.name = "tps62360", .driver_data = TPS62360}, {.name = "tps62361", .driver_data = TPS62361}, + {.name = "tps62362", .driver_data = TPS62362}, + {.name = "tps62363", .driver_data = TPS62363}, {}, }; @@ -468,5 +532,5 @@ static void __exit tps62360_cleanup(void) module_exit(tps62360_cleanup); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("TPS62360 voltage regulator driver"); +MODULE_DESCRIPTION("TPS6236x voltage regulator driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index fe2f1a8412b7..a2fef3880065 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -331,21 +331,16 @@ struct tps65910_reg { static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) { - u8 val; + unsigned int val; int err; - err = pmic->mfd->read(pmic->mfd, reg, 1, &val); + err = tps65910_reg_read(pmic->mfd, reg, &val); if (err) return err; return val; } -static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val) -{ - return pmic->mfd->write(pmic->mfd, reg, 1, &val); -} - static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, u8 set_mask, u8 clear_mask) { @@ -362,7 +357,7 @@ static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, data &= ~clear_mask; data |= set_mask; - err = tps65910_write(pmic, reg, data); + err = tps65910_reg_write(pmic->mfd, reg, data); if (err) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -371,7 +366,7 @@ out: return err; } -static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) +static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg) { int data; @@ -385,13 +380,13 @@ static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) return data; } -static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val) +static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val) { int err; mutex_lock(&pmic->mutex); - err = tps65910_write(pmic, reg, val); + err = tps65910_reg_write(pmic->mfd, reg, val); if (err < 0) dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); @@ -476,7 +471,7 @@ static int tps65910_is_enabled(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -493,7 +488,7 @@ static int tps65910_enable(struct regulator_dev *dev) if (reg < 0) return reg; - return tps65910_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); + return tps65910_reg_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); } static int tps65910_disable(struct regulator_dev *dev) @@ -506,7 +501,7 @@ static int tps65910_disable(struct regulator_dev *dev) if (reg < 0) return reg; - return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); + return tps65910_reg_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); } static int tps65910_enable_time(struct regulator_dev *dev) @@ -532,9 +527,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) LDO_ST_MODE_BIT); case REGULATOR_MODE_IDLE: value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; - return tps65910_set_bits(mfd, reg, value); + return tps65910_reg_set_bits(mfd, reg, value); case REGULATOR_MODE_STANDBY: - return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT); + return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT); } return -EINVAL; @@ -549,7 +544,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -569,28 +564,28 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) switch (id) { case TPS65910_REG_VDD1: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD1); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1); mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR); sr = opvsel & VDD1_OP_CMD_MASK; opvsel &= VDD1_OP_SEL_MASK; srvsel &= VDD1_SR_SEL_MASK; vselmax = 75; break; case TPS65910_REG_VDD2: - opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP); - mult = tps65910_reg_read(pmic, TPS65910_VDD2); + opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP); + mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2); mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR); + srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR); sr = opvsel & VDD2_OP_CMD_MASK; opvsel &= VDD2_OP_SEL_MASK; srvsel &= VDD2_SR_SEL_MASK; vselmax = 75; break; case TPS65911_REG_VDDCTRL: - opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP); - srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR); + opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP); + srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR); sr = opvsel & VDDCTRL_OP_CMD_MASK; opvsel &= VDDCTRL_OP_SEL_MASK; srvsel &= VDDCTRL_SR_SEL_MASK; @@ -630,7 +625,7 @@ static int tps65910_get_voltage(struct regulator_dev *dev) if (reg < 0) return reg; - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); if (value < 0) return value; @@ -669,7 +664,7 @@ static int tps65911_get_voltage(struct regulator_dev *dev) reg = pmic->get_ctrl_reg(id); - value = tps65910_reg_read(pmic, reg); + value = tps65910_reg_read_locked(pmic, reg); switch (id) { case TPS65911_REG_LDO1: @@ -728,7 +723,7 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD1, (dcdc_mult << VDD1_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel); break; case TPS65910_REG_VDD2: dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; @@ -739,11 +734,11 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, tps65910_modify_bits(pmic, TPS65910_VDD2, (dcdc_mult << VDD2_VGAIN_SEL_SHIFT), VDD1_VGAIN_SEL_MASK); - tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel); break; case TPS65911_REG_VDDCTRL: vsel = selector + 3; - tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel); + tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel); } return 0; @@ -994,10 +989,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN1 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1007,10 +1002,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN2 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1022,10 +1017,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, if ((tps65910_chip_id(mfd) == TPS65910) && (id >= TPS65910_REG_VDIG1)) { if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -1037,10 +1032,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* Return if no external control is selected */ if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { /* Clear all sleep controls */ - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); if (ret < 0) dev_err(mfd->dev, @@ -1059,32 +1054,33 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, (tps65910_chip_id(mfd) == TPS65911))) { int op_reg_add = pmic->get_ctrl_reg(id) + 1; int sr_reg_add = pmic->get_ctrl_reg(id) + 2; - int opvsel = tps65910_reg_read(pmic, op_reg_add); - int srvsel = tps65910_reg_read(pmic, sr_reg_add); + int opvsel = tps65910_reg_read_locked(pmic, op_reg_add); + int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add); if (opvsel & VDD1_OP_CMD_MASK) { u8 reg_val = srvsel & VDD1_OP_SEL_MASK; - ret = tps65910_reg_write(pmic, op_reg_add, reg_val); + ret = tps65910_reg_write_locked(pmic, op_reg_add, + reg_val); if (ret < 0) { dev_err(mfd->dev, "Error in configuring op register\n"); return ret; } } - ret = tps65910_reg_write(pmic, sr_reg_add, 0); + ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0); if (ret < 0) { dev_err(mfd->dev, "Error in settting sr register\n"); return ret; } } - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) { if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) - ret = tps65910_set_bits(mfd, + ret = tps65910_reg_set_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); else - ret = tps65910_clear_bits(mfd, + ret = tps65910_reg_clear_bits(mfd, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); } if (ret < 0) @@ -1117,7 +1113,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL, + tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); switch(tps65910_chip_id(tps65910)) { diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c index 70d6734a5708..b2b9d04171c5 100644 --- a/drivers/rtc/rtc-tps80031.c +++ b/drivers/rtc/rtc-tps80031.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/rtc.h> #include <linux/slab.h> +#include <linux/gpio.h> #define RTC_CTRL 0x10 #define RTC_STATUS 0x11 @@ -65,8 +66,25 @@ struct tps80031_rtc { int irq; struct rtc_device *rtc; u8 alarm_irq_enabled; + int msecure_gpio; }; +static inline void tps80031_enable_rtc_write(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->msecure_gpio >= 0) + gpio_set_value(rtc->msecure_gpio, 1); +} + +static inline void tps80031_disable_rtc_write(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->msecure_gpio >= 0) + gpio_set_value(rtc->msecure_gpio, 0); +} + static int tps80031_read_regs(struct device *dev, int reg, int len, uint8_t *val) { @@ -93,13 +111,16 @@ static int tps80031_write_regs(struct device *dev, int reg, int len, uint8_t *val) { int ret; + + tps80031_enable_rtc_write(dev); ret = tps80031_writes(dev->parent, 1, reg, len, val); if (ret < 0) { + tps80031_disable_rtc_write(dev); dev_err(dev->parent, "failed writing reg: %d\n", reg); WARN_ON(1); return ret; } - + tps80031_disable_rtc_write(dev); return 0; } @@ -150,18 +171,24 @@ static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm) static int tps80031_rtc_stop(struct device *dev) { int err; + + tps80031_enable_rtc_write(dev); err = tps80031_clr_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); if (err < 0) dev_err(dev->parent, "failed to stop RTC. err: %d\n", err); + tps80031_disable_rtc_write(dev); return err; } static int tps80031_rtc_start(struct device *dev) { int err; + + tps80031_enable_rtc_write(dev); err = tps80031_set_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); if (err < 0) dev_err(dev->parent, "failed to start RTC. err: %d\n", err); + tps80031_disable_rtc_write(dev); return err; } @@ -262,7 +289,9 @@ static int tps80031_rtc_alarm_irq_enable(struct device *dev, if (rtc->alarm_irq_enabled) return 0; + tps80031_enable_rtc_write(dev); err = tps80031_set_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(dev); if (err < 0) { dev_err(p, "failed to set ALRM int. err: %d\n", err); return err; @@ -271,7 +300,9 @@ static int tps80031_rtc_alarm_irq_enable(struct device *dev, } else { if(!rtc->alarm_irq_enabled) return 0; + tps80031_enable_rtc_write(dev); err = tps80031_clr_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(dev); if (err < 0) { dev_err(p, "failed to clear ALRM int. err: %d\n", err); return err; @@ -303,8 +334,10 @@ static irqreturn_t tps80031_rtc_irq(int irq, void *data) return -EBUSY; } + tps80031_enable_rtc_write(dev); err = tps80031_force_update(dev->parent, 1, RTC_STATUS, ALARM_INT_STATUS, ALARM_INT_STATUS); + tps80031_disable_rtc_write(dev); if (err) { dev_err(dev->parent, "unable to set Alarm INT\n"); return -EBUSY; @@ -335,8 +368,19 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) if (pdata->irq < 0) dev_err(&pdev->dev, "no IRQ specified, wakeup is disabled\n"); + rtc->msecure_gpio = -1; + if (gpio_is_valid(pdata->msecure_gpio)) { + err = gpio_request(pdata->msecure_gpio, "tps80031 msecure"); + if (err == 0) { + rtc->msecure_gpio = pdata->msecure_gpio; + gpio_direction_output(rtc->msecure_gpio, 0); + } else + dev_warn(&pdev->dev, "could not get msecure GPIO\n"); + } + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &tps80031_rtc_ops, THIS_MODULE); + dev_set_drvdata(&pdev->dev, rtc); if (IS_ERR(rtc->rtc)) { err = PTR_ERR(rtc->rtc); @@ -379,7 +423,9 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) return -EBUSY; } + tps80031_enable_rtc_write(&pdev->dev); err = tps80031_set_bits(pdev->dev.parent, 1, RTC_INT, ENABLE_ALARM_INT); + tps80031_disable_rtc_write(&pdev->dev); if (err) { dev_err(&pdev->dev, "unable to program Interrupt Mask reg\n"); err = -EBUSY; @@ -388,7 +434,6 @@ static int __devinit tps80031_rtc_probe(struct platform_device *pdev) } else rtc->alarm_irq_enabled = 1; - dev_set_drvdata(&pdev->dev, rtc); if (pdata && (pdata->irq >= 0)) { rtc->irq = pdata->irq; err = request_threaded_irq(pdata->irq, NULL, tps80031_rtc_irq, diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c index 8b65e20c4d3e..484e228dc044 100644 --- a/drivers/tty/serial/tegra_hsuart.c +++ b/drivers/tty/serial/tegra_hsuart.c @@ -1263,6 +1263,12 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, if (t->rts_active) set_rts(t, false); + /* Clear all interrupts as configuration is going to be change */ + uart_writeb(t, t->ier_shadow | UART_IER_RDI, UART_IER); + uart_readb(t, UART_IER); + uart_writeb(t, 0, UART_IER); + uart_readb(t, UART_IER); + /* Parity */ lcr = t->lcr_shadow; lcr &= ~UART_LCR_PARITY; @@ -1335,6 +1341,13 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, /* update the port timeout based on new settings */ uart_update_timeout(u, termios->c_cflag, baud); + /* Make sure all write has completed */ + uart_readb(t, UART_IER); + + /* Reenable interrupt */ + uart_writeb(t, t->ier_shadow, UART_IER); + uart_readb(t, UART_IER); + spin_unlock_irqrestore(&u->lock, flags); dev_vdbg(t->uport.dev, "-tegra_set_termios\n"); return; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index c5fa8160bffe..98f3d5106a38 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -224,7 +224,7 @@ static int tegra_ehci_hub_control( status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { - pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + EHCI_DBG("%s: timeout waiting for SUSPEND to clear\n", __func__); } tegra_usb_phy_post_resume(tegra->phy); tegra->port_resuming = 0; @@ -241,6 +241,11 @@ static int tegra_ehci_hub_control( if (wValue == USB_PORT_FEAT_SUSPEND) { tegra_usb_phy_pre_resume(tegra->phy, false); tegra->port_resuming = 1; + } else if (wValue == USB_PORT_FEAT_ENABLE) { + u32 temp; + temp = ehci_readl(ehci, &ehci->regs->port_status[0]) & ~PORT_RWC_BITS; + ehci_writel(ehci, temp & ~PORT_PE, &ehci->regs->port_status[0]); + return retval; } break; } @@ -264,6 +269,12 @@ static int tegra_ehci_hub_control( tegra_usb_phy_port_power(tegra->phy); } break; + case ClearPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + /* tegra USB controller needs 25 ms to resume the port */ + ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); + } + break; } } diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 4c590076f73e..b5106faf68bd 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -375,10 +375,8 @@ static ssize_t store_host_en(struct device *dev, struct device_attribute *attr, unsigned long host; int err; - err = kstrtoul(buf, 10, &host); - if (err < 0) { - return err; - } + if (sscanf(buf, "%d", &host) != 1 || host < 0 || host > 1) + return -EINVAL; if (host) { enable_interrupt(tegra, false); @@ -474,6 +472,8 @@ static int tegra_otg_probe(struct platform_device *pdev) goto err_irq; } + clk_disable(tegra->clk); + return 0; err_irq: diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 59e27788680d..15c2acfa8cc7 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -102,6 +102,15 @@ config NVMAP_PAGE_POOL_SIZE hex default 0x0 +config NVMAP_CACHE_MAINT_BY_SET_WAYS + bool "Enalbe cache maintenance by set/ways" + depends on TEGRA_NVMAP + help + Say Y here to reduce cache maintenance overhead by MVA. + This helps in reducing cache maintenance overhead in the systems, + where inner cache includes only L1. For the systems, where inner cache + includes L1 and L2, keep this option disabled. + config NVMAP_VPR bool "Enable VPR Heap." depends on TEGRA_NVMAP diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index df54578a5b5a..5c960f9e45c0 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -2146,23 +2146,6 @@ static void tegra_dc_vblank(struct work_struct *work) } } -/* Must acquire dc lock and dc one-shot lock before invoking this function. - * Acquire dc one-shot lock first and then dc lock. */ -void tegra_dc_host_trigger(struct tegra_dc *dc) -{ - /* We release the lock here to prevent deadlock between - * cancel_delayed_work_sync and one-shot work. */ - mutex_unlock(&dc->lock); - - cancel_delayed_work_sync(&dc->one_shot_work); - mutex_lock(&dc->lock); - - schedule_delayed_work(&dc->one_shot_work, - msecs_to_jiffies(dc->one_shot_delay_ms)); - tegra_dc_program_bandwidth(dc); - tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL); -} - static void tegra_dc_one_shot_worker(struct work_struct *work) { struct tegra_dc *dc = container_of( diff --git a/drivers/video/tegra/dc/dc_config.h b/drivers/video/tegra/dc/dc_config.h index f513cd06dc45..314cd11e77f9 100644 --- a/drivers/video/tegra/dc/dc_config.h +++ b/drivers/video/tegra/dc/dc_config.h @@ -129,13 +129,13 @@ enum tegra_dc_feature_option { }; struct tegra_dc_feature_entry { - int window_index; - enum tegra_dc_feature_option option; + u32 window_index; + u32 option; long arg[ENTRY_SIZE]; }; struct tegra_dc_feature { - unsigned num_entries; + u32 num_entries; struct tegra_dc_feature_entry *entries; }; diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index e402c416b779..6624a8e8f52c 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -1625,7 +1625,7 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi) tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG); } -static void tegra_dsi_panelB_enable() +static void tegra_dsi_panelB_enable(void) { unsigned int val; @@ -2751,8 +2751,8 @@ static int tegra_dc_dsi_cp_info(struct tegra_dc_dsi_data *dsi, struct tegra_dsi_out *p_dsi) { struct tegra_dsi_cmd *p_init_cmd; - struct tegra_dsi_cmd *p_early_suspend_cmd; - struct tegra_dsi_cmd *p_late_resume_cmd; + struct tegra_dsi_cmd *p_early_suspend_cmd = NULL; + struct tegra_dsi_cmd *p_late_resume_cmd = NULL; struct tegra_dsi_cmd *p_suspend_cmd; int err; diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c index c349a4720d2e..a8de7a7a26e0 100644 --- a/drivers/video/tegra/dc/ext/dev.c +++ b/drivers/video/tegra/dc/ext/dev.c @@ -708,6 +708,21 @@ static int tegra_dc_ext_get_status(struct tegra_dc_ext_user *user, return 0; } +static int tegra_dc_ext_get_feature(struct tegra_dc_ext_user *user, + struct tegra_dc_ext_feature *feature) +{ + struct tegra_dc *dc = user->ext->dc; + struct tegra_dc_feature *table = dc->feature; + + if (dc->enabled && feature->entries) { + feature->length = table->num_entries; + memcpy(feature->entries, table->entries, table->num_entries * + sizeof(struct tegra_dc_feature_entry)); + } + + return 0; +} + static long tegra_dc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -805,6 +820,22 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd, return tegra_dc_ext_set_lut(user, &args); } + case TEGRA_DC_EXT_GET_FEATURES: + { + struct tegra_dc_ext_feature args; + int ret; + + if (copy_from_user(&args, user_arg, sizeof(args))) + return -EFAULT; + + ret = tegra_dc_ext_get_feature(user, &args); + + if (copy_to_user(user_arg, &args, sizeof(args))) + return -EFAULT; + + return ret; + } + default: return -EINVAL; } diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c index 65d518759243..860dd8dab1fd 100644 --- a/drivers/video/tegra/dc/nvsd.c +++ b/drivers/video/tegra/dc/nvsd.c @@ -378,7 +378,10 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); if (val & SD_ENABLE_NORMAL) - i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); + if (settings->phase_in_adjustments) + i = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); + else + i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); else i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */ diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile index d47c97571286..f228a3ad0396 100644 --- a/drivers/video/tegra/host/Makefile +++ b/drivers/video/tegra/host/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_TEGRA_GRHOST) += gr3d/ obj-$(CONFIG_TEGRA_GRHOST) += host1x/ obj-$(CONFIG_TEGRA_GRHOST) += t20/ obj-$(CONFIG_TEGRA_GRHOST) += t30/ -obj-$(CONFIG_TEGRA_GRHOST) += dsi/ obj-$(CONFIG_TEGRA_GRHOST) += gr2d/ obj-$(CONFIG_TEGRA_GRHOST) += isp/ obj-$(CONFIG_TEGRA_GRHOST) += vi/ diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 87aa9c64d363..6bfce9a3cb2a 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -50,10 +50,28 @@ #include "nvhost_job.h" #include "nvhost_hwctx.h" -void nvhost_read_module_regs(struct nvhost_device *ndev, +static int validate_reg(struct nvhost_device *ndev, u32 offset, int count) +{ + struct resource *r = nvhost_get_resource(ndev, IORESOURCE_MEM, 0); + int err = 0; + + if (offset + 4 * count > resource_size(r) + || (offset + 4 * count < offset)) + err = -EPERM; + + return err; +} + +int nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values) { void __iomem *p = ndev->aperture + offset; + int err; + + /* verify offset */ + err = validate_reg(ndev, offset, count); + if (err) + return err; nvhost_module_busy(ndev); while (count--) { @@ -62,12 +80,20 @@ void nvhost_read_module_regs(struct nvhost_device *ndev, } rmb(); nvhost_module_idle(ndev); + + return 0; } -void nvhost_write_module_regs(struct nvhost_device *ndev, +int nvhost_write_module_regs(struct nvhost_device *ndev, u32 offset, int count, const u32 *values) { void __iomem *p = ndev->aperture + offset; + int err; + + /* verify offset */ + err = validate_reg(ndev, offset, count); + if (err) + return err; nvhost_module_busy(ndev); while (count--) { @@ -76,6 +102,8 @@ void nvhost_write_module_regs(struct nvhost_device *ndev, } wmb(); nvhost_module_idle(ndev); + + return 0; } struct nvhost_channel_userctx { @@ -140,7 +168,9 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp) priv->priority = NVHOST_PRIORITY_MEDIUM; priv->clientid = atomic_add_return(1, &nvhost_get_host(ch->dev)->clientid); - priv->timeout = MAX_STUCK_CHECK_COUNT * SYNCPT_CHECK_PERIOD; + priv->timeout = + jiffies_to_msecs(MAX_STUCK_CHECK_COUNT * SYNCPT_CHECK_PERIOD); + return 0; fail: nvhost_channelrelease(inode, filp); @@ -149,14 +179,17 @@ fail: static int set_submit(struct nvhost_channel_userctx *ctx) { - struct device *device = &ctx->ch->dev->dev; + struct nvhost_device *ndev = ctx->ch->dev; + struct nvhost_master *host = nvhost_get_host(ndev); /* submit should have at least 1 cmdbuf */ - if (!ctx->hdr.num_cmdbufs) + if (!ctx->hdr.num_cmdbufs || + !nvhost_syncpt_is_valid(&host->syncpt, + ctx->hdr.syncpt_id)) return -EIO; if (!ctx->nvmap) { - dev_err(device, "no nvmap context set\n"); + dev_err(&ndev->dev, "no nvmap context set\n"); return -EFAULT; } @@ -182,6 +215,11 @@ static void reset_submit(struct nvhost_channel_userctx *ctx) ctx->hdr.num_relocs = 0; ctx->num_relocshifts = 0; ctx->hdr.num_waitchks = 0; + + if (ctx->job) { + nvhost_job_put(ctx->job); + ctx->job = NULL; + } } static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, @@ -367,11 +405,10 @@ static long nvhost_channelctl(struct file *filp, if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) || - (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST)) + (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST) || + (_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE)) return -EFAULT; - BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE); - if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) return -EFAULT; diff --git a/drivers/video/tegra/host/bus_client.h b/drivers/video/tegra/host/bus_client.h index adc3a704ea5d..e95ea0bc3401 100644 --- a/drivers/video/tegra/host/bus_client.h +++ b/drivers/video/tegra/host/bus_client.h @@ -24,10 +24,10 @@ #include <linux/types.h> struct nvhost_device; -void nvhost_read_module_regs(struct nvhost_device *ndev, +int nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values); -void nvhost_write_module_regs(struct nvhost_device *ndev, +int nvhost_write_module_regs(struct nvhost_device *ndev, u32 offset, int count, const u32 *values); int nvhost_client_user_init(struct nvhost_device *dev); diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index ca73528fbeeb..2c05fbfb7477 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -205,6 +205,7 @@ static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx, return -EINVAL; while (num_offsets--) { + int err; int remaining = args->block_size >> 2; u32 offs; if (get_user(offs, offsets)) @@ -216,17 +217,21 @@ static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx, if (copy_from_user(vals, values, batch*sizeof(u32))) return -EFAULT; - nvhost_write_module_regs(ndev, + err = nvhost_write_module_regs(ndev, offs, batch, vals); + if (err) + return err; } else { - nvhost_read_module_regs(ndev, + err = nvhost_read_module_regs(ndev, offs, batch, vals); + if (err) + return err; if (copy_to_user(values, vals, batch*sizeof(u32))) return -EFAULT; } remaining -= batch; - offs += batch; + offs += batch*sizeof(u32); values += batch; } } @@ -250,11 +255,10 @@ static long nvhost_ctrlctl(struct file *filp, if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) || - (_IOC_NR(cmd) > NVHOST_IOCTL_CTRL_LAST)) + (_IOC_NR(cmd) > NVHOST_IOCTL_CTRL_LAST) || + (_IOC_SIZE(cmd) > NVHOST_IOCTL_CTRL_MAX_ARG_SIZE)) return -EFAULT; - BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CTRL_MAX_ARG_SIZE); - if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) return -EFAULT; diff --git a/drivers/video/tegra/host/dsi/Makefile b/drivers/video/tegra/host/dsi/Makefile deleted file mode 100644 index eb94d3ec4928..000000000000 --- a/drivers/video/tegra/host/dsi/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -GCOV_PROFILE := y -EXTRA_CFLAGS += -Idrivers/video/tegra/host - -nvhost-dsi-objs = \ - dsi.o - -obj-$(CONFIG_TEGRA_GRHOST) += nvhost-dsi.o diff --git a/drivers/video/tegra/host/dsi/dsi.c b/drivers/video/tegra/host/dsi/dsi.c deleted file mode 100644 index 87da8a6f1b8a..000000000000 --- a/drivers/video/tegra/host/dsi/dsi.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * drivers/video/tegra/host/dsi/dsi.c - * - * Tegra Graphics DSI - * - * Copyright (c) 2012, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. - */ - -#include "dev.h" -#include "bus_client.h" - -static int dsi_probe(struct nvhost_device *dev, - struct nvhost_device_id *id_table) -{ - return nvhost_client_device_init(dev); -} - -static int __exit dsi_remove(struct nvhost_device *dev) -{ - /* Add clean-up */ - return 0; -} - -static int dsi_suspend(struct nvhost_device *dev, pm_message_t state) -{ - return nvhost_client_device_suspend(dev); -} - -static int dsi_resume(struct nvhost_device *dev) -{ - dev_info(&dev->dev, "resuming\n"); - return 0; -} - -struct nvhost_device *dsi_device; - -static struct nvhost_driver dsi_driver = { - .probe = dsi_probe, - .remove = __exit_p(dsi_remove), -#ifdef CONFIG_PM - .suspend = dsi_suspend, - .resume = dsi_resume, -#endif - .driver = { - .owner = THIS_MODULE, - .name = "dsi", - } -}; - -static int __init dsi_init(void) -{ - int err; - - dsi_device = nvhost_get_device("dsi"); - if (!dsi_device) - return -ENXIO; - - err = nvhost_device_register(dsi_device); - if (err) - return err; - - return nvhost_driver_register(&dsi_driver); -} - -static void __exit dsi_exit(void) -{ - nvhost_driver_unregister(&dsi_driver); -} - -module_init(dsi_init); -module_exit(dsi_exit); diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c index 57f4c779eff8..4bc7c9f92bb5 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c @@ -427,6 +427,8 @@ struct nvhost_hwctx_handler *nvhost_gr3d_t30_ctxhandler_init( setup_save(p, save_ptr); + nvmap_munmap(p->save_buf, save_ptr); + p->h.alloc = ctx3d_alloc_v1; p->h.save_push = save_push_v1; p->h.save_service = NULL; diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index 6d96bd023d81..dfd84328307b 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -134,7 +134,11 @@ static void to_state_running_locked(struct nvhost_device *dev) for (i = 0; i < dev->num_clks; i++) { int err = clk_enable(dev->clk[i]); - BUG_ON(err); + if (err) { + dev_err(&dev->dev, "Cannot turn on clock %s", + dev->clocks[i].name); + return; + } } if (prev_state == NVHOST_POWER_STATE_POWERGATED @@ -369,7 +373,11 @@ int nvhost_module_init(struct nvhost_device *dev) snprintf(devname, MAX_DEVID_LENGTH, "tegra_%s", dev->name); c = clk_get_sys(devname, dev->clocks[i].name); - BUG_ON(IS_ERR_OR_NULL(c)); + if (IS_ERR_OR_NULL(c)) { + dev_err(&dev->dev, "Cannot get clock %s\n", + dev->clocks[i].name); + continue; + } rate = clk_round_rate(c, rate); clk_enable(c); diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index b58921bffa9c..fcc9fce72071 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -140,6 +140,11 @@ int nvhost_syncpt_patch_wait(struct nvhost_syncpt *sp, void *patch_addr); void nvhost_syncpt_debug(struct nvhost_syncpt *sp); +static inline int nvhost_syncpt_is_valid(struct nvhost_syncpt *sp, u32 id) +{ + return id != NVSYNCPT_INVALID && id < sp->nb_pts; +} + int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx); void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx); diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index 14787154523f..7a10231982c1 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -540,6 +540,7 @@ static bool fast_cache_maint(struct nvmap_client *client, struct nvmap_handle *h { int ret = false; +#if defined(CONFIG_NVMAP_CACHE_MAINT_BY_SET_WAYS) if ((op == NVMAP_CACHE_OP_INV) || ((end - start) < FLUSH_CLEAN_BY_SET_WAY_THRESHOLD)) goto out; @@ -559,6 +560,7 @@ static bool fast_cache_maint(struct nvmap_client *client, struct nvmap_handle *h } ret = true; out: +#endif return ret; } |