* Re: [PATCH v4 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
From: Joel Stanley @ 2016-11-15 11:16 UTC (permalink / raw)
To: Rob Herring
Cc: Brendan Higgins, Wolfram Sang, Mark Rutland,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, OpenBMC Maillist
In-Reply-To: <20161114155731.p4kxmefqevplphtd@rob-hp-laptop>
On Tue, Nov 15, 2016 at 2:27 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Nov 04, 2016 at 06:58:19PM -0700, Brendan Higgins wrote:
>> Added device tree binding documentation for Aspeed I2C controller and
>> busses.
>>
>> Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes for v2:
>> - None
>> Changes for v3:
>> - Removed reference to "bus" device tree param
>> Changes for v4:
>> - None
>> ---
>> .../devicetree/bindings/i2c/i2c-aspeed.txt | 61 ++++++++++++++++++++++
>> 1 file changed, 61 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Thanks Rob.
Acked-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Cheers,
Joel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2] ARM: dts: da850: add the mstpri and ddrctl nodes
From: Bartosz Golaszewski @ 2016-11-15 11:00 UTC (permalink / raw)
To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
Tomi Valkeinen, David Airlie, Laurent Pinchart,
Bartosz Golaszewski
Add the nodes for the MSTPRI configuration and DDR2/mDDR memory
controller drivers to da850.dtsi.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
v1 -> v2:
- moved the priority controller node above the cfgchip node
- renamed added nodes to better reflect their purpose
arch/arm/boot/dts/da850.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
index 1bb1f6d..412eec6 100644
--- a/arch/arm/boot/dts/da850.dtsi
+++ b/arch/arm/boot/dts/da850.dtsi
@@ -210,6 +210,10 @@
};
};
+ prictrl: priority-controller@14110 {
+ compatible = "ti,da850-mstpri";
+ reg = <0x14110 0x0c>;
+ };
cfgchip: chip-controller@1417c {
compatible = "ti,da830-cfgchip", "syscon", "simple-mfd";
reg = <0x1417c 0x14>;
@@ -451,4 +455,8 @@
1 0 0x68000000 0x00008000>;
status = "disabled";
};
+ memctrl: memory-controller@b0000000 {
+ compatible = "ti,da850-ddr-controller";
+ reg = <0xb0000000 0xe8>;
+ };
};
--
2.9.3
^ permalink raw reply related
* Re: [PATCH resend] input: touchscreen: silead: Add regulator support
From: Hans de Goede @ 2016-11-15 10:56 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Rob Herring, linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
Hans de Goede
In-Reply-To: <20161114185057.GA7694@dtor-ws>
Hi,
On 14-11-16 19:50, Dmitry Torokhov wrote:
> Hi Hans,
>
> On Mon, Nov 14, 2016 at 03:45:02PM +0100, Hans de Goede wrote:
>> On some tablets the touchscreen controller is powered by seperate
>> regulators, add support for this.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> ---
>> .../bindings/input/touchscreen/silead_gsl1680.txt | 2 +
>> drivers/input/touchscreen/silead.c | 51 ++++++++++++++++++++--
>> 2 files changed, 49 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> index e844c3f..b726823 100644
>> --- a/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> +++ b/Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
>> @@ -22,6 +22,8 @@ Optional properties:
>> - touchscreen-inverted-y : See touchscreen.txt
>> - touchscreen-swapped-x-y : See touchscreen.txt
>> - silead,max-fingers : maximum number of fingers the touchscreen can detect
>> +- vddio-supply : regulator phandle for controller VDDIO
>> +- avdd-supply : regulator phandle for controller AVDD
>>
>> Example:
>>
>> diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
>> index f502c84..c6a1ae9 100644
>> --- a/drivers/input/touchscreen/silead.c
>> +++ b/drivers/input/touchscreen/silead.c
>> @@ -29,6 +29,7 @@
>> #include <linux/input/touchscreen.h>
>> #include <linux/pm.h>
>> #include <linux/irq.h>
>> +#include <linux/regulator/consumer.h>
>>
>> #include <asm/unaligned.h>
>>
>> @@ -72,6 +73,8 @@ enum silead_ts_power {
>> struct silead_ts_data {
>> struct i2c_client *client;
>> struct gpio_desc *gpio_power;
>> + struct regulator *vddio;
>> + struct regulator *avdd;
>> struct input_dev *input;
>> char fw_name[64];
>> struct touchscreen_properties prop;
>> @@ -465,21 +468,52 @@ static int silead_ts_probe(struct i2c_client *client,
>> if (client->irq <= 0)
>> return -ENODEV;
>>
>> + data->vddio = devm_regulator_get_optional(dev, "vddio");
>> + if (IS_ERR(data->vddio)) {
>> + if (PTR_ERR(data->vddio) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + data->vddio = NULL;
>
> Why do we ignore other errors?
Other errors indicate that the regulator is not there
specifically I think regulator_get_optional will return -ENOENT
in that case.
> If there is an issue reported by
> regulator framework we should net be ignoring it.
>
> Unless regulator is truly optional (i.e. chip can work with some
> functionality powered off) and not simply hidden (firmware takes care of
> powering up system),
In most systems the vddio is simply hardwired to the always-on
vcc-3.3V
> we should not be using regulator_get_optional():
> if regulator is absent from ACPI/DT/etc, regulator framework will supply
> dummy regulator that you can enable/disable and not bothering checking
> whether it is NULL or not.
Right, the downside of that is that it prints a msg about this
in dmesg which typically will lead to user questions.
Anyways if you prefer the non _optional variant I can do a v3
with that ...
> Also, please consider using devm_regulator_bulk_get().
Hmm, so I would get:
In struct silead_ts_data:
struct regulator_bulk_data regulators[2];
And then in probe() do:
data->regulators[0].supply = "vddio";
data->regulators[1].supply = "avdd";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
data->regulators);
if (ret)
return ret;
And modify the enable / disable code to match.
Yes I can see how that is better / cleaner and it also
answers the question whether or not to use get_optional.
Ok, I'll do a v2 switching to regulator_bulk_get.
Regards,
Hans
>
>> + }
>> +
>> + data->avdd = devm_regulator_get_optional(dev, "avdd");
>> + if (IS_ERR(data->avdd)) {
>> + if (PTR_ERR(data->avdd) == -EPROBE_DEFER)
>> + return -EPROBE_DEFER;
>> + data->avdd = NULL;
>> + }
>> +
>> + /*
>> + * Enable regulators at probe and disable them at remove, we need
>> + * to keep the chip powered otherwise it forgets its firmware.
>> + */
>> + if (data->vddio) {
>> + error = regulator_enable(data->vddio);
>> + if (error)
>> + return error;
>> + }
>> +
>> + if (data->avdd) {
>> + error = regulator_enable(data->avdd);
>> + if (error)
>> + goto disable_vddio;
>> + }
>
> Use devm_add_action_or_reset() to work regulator_bulk_disable call into
> devm stream. As it is you are leaving regulators on on unbind/remove.
>
>> +
>> /* Power GPIO pin */
>> data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
>> if (IS_ERR(data->gpio_power)) {
>> if (PTR_ERR(data->gpio_power) != -EPROBE_DEFER)
>> dev_err(dev, "Shutdown GPIO request failed\n");
>> - return PTR_ERR(data->gpio_power);
>> + error = PTR_ERR(data->gpio_power);
>> + goto disable_avdd;
>> }
>>
>> error = silead_ts_setup(client);
>> if (error)
>> - return error;
>> + goto disable_avdd;
>>
>> error = silead_ts_request_input_dev(data);
>> if (error)
>> - return error;
>> + goto disable_avdd;
>>
>> error = devm_request_threaded_irq(dev, client->irq,
>> NULL, silead_ts_threaded_irq_handler,
>> @@ -487,10 +521,19 @@ static int silead_ts_probe(struct i2c_client *client,
>> if (error) {
>> if (error != -EPROBE_DEFER)
>> dev_err(dev, "IRQ request failed %d\n", error);
>> - return error;
>> + goto disable_avdd;
>> }
>>
>> return 0;
>> +
>> +disable_avdd:
>> + if (data->avdd)
>> + regulator_disable(data->avdd);
>> +disable_vddio:
>> + if (data->vddio)
>> + regulator_disable(data->vddio);
>> +
>> + return error;
>> }
>>
>> static int __maybe_unused silead_ts_suspend(struct device *dev)
>> --
>> 2.9.3
>>
>
> Thanks.
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Fabrice Gasnier @ 2016-11-15 10:47 UTC (permalink / raw)
To: Lee Jones, Jonathan Cameron
Cc: linux-iio, linux-arm-kernel, devicetree, linux-kernel, linux,
robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
knaack.h, pmeerw
In-Reply-To: <20161114164701.GA2668@dell>
On 11/14/2016 05:47 PM, Lee Jones wrote:
> On Sat, 12 Nov 2016, Jonathan Cameron wrote:
>
>> On 10/11/16 16:18, Fabrice Gasnier wrote:
>>> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
>>> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
>>> resources like clock prescaler, common interrupt line and analog
>>> reference voltage.
>>> This core driver basically manages shared resources.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> Looks good to me (other than the build issue obviously ;)
Hi Jonathan,
Thanks for your review.
I'll fix build issue, sure ;-)
>>
>> The fun bit will be trying to keep the whole thing this clean as you
>> add the more 'interesting' functionality. *fingers crossed*
Yes... But in the end, splitting shared resources in core driver makes
it more simple.
Not sure there will be more complexity here.
>>
>> Acked-by: Jonathan Cameron <jic23@kernel.org>
> There isn't anything MFD about this driver.
>
> Please move it into IIO.
Hmm, there is no other sub sysbtem that may be used here, ADC driver
belongs to IIO.
Also, of_platform_populate() is being used here. This can perfectly be
called from within IIO.
Jonathan, can this "stm32-adc-core" driver be moved to, and live in
drivers/iio/adc ?
(e.g. in addition to stm32-adc iio driver)
Is it ok for you ?
Please advise,
Best Regards,
Fabrice
>
>>> ---
>>> drivers/mfd/Kconfig | 14 ++
>>> drivers/mfd/Makefile | 1 +
>>> drivers/mfd/stm32-adc-core.c | 301 +++++++++++++++++++++++++++++++++++++
>>> include/linux/mfd/stm32-adc-core.h | 52 +++++++
>>> 4 files changed, 368 insertions(+)
>>> create mode 100644 drivers/mfd/stm32-adc-core.c
>>> create mode 100644 include/linux/mfd/stm32-adc-core.h
>>>
>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index c6df644..2580cee 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -1152,6 +1152,20 @@ config MFD_PALMAS
>>> If you say yes here you get support for the Palmas
>>> series of PMIC chips from Texas Instruments.
>>>
>>> +config MFD_STM32_ADC
>>> + tristate "STMicroelectronics STM32 adc"
>>> + depends on ARCH_STM32 || COMPILE_TEST
>>> + depends on OF
>>> + select MFD_CORE
>>> + select REGULATOR
>>> + select REGULATOR_FIXED_VOLTAGE
>>> + help
>>> + Select this option to enable the core driver for STMicroelectronics
>>> + STM32 analog-to-digital converter (ADC).
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32-adc-core.
>>> +
>>> config TPS6105X
>>> tristate "TI TPS61050/61052 Boost Converters"
>>> depends on I2C
>>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>>> index 9834e66..4571506 100644
>>> --- a/drivers/mfd/Makefile
>>> +++ b/drivers/mfd/Makefile
>>> @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
>>> obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
>>> obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
>>> obj-$(CONFIG_MFD_PALMAS) += palmas.o
>>> +obj-$(CONFIG_MFD_STM32_ADC) += stm32-adc-core.o
>>> obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
>>> obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
>>> obj-$(CONFIG_MFD_RK808) += rk808.o
>>> diff --git a/drivers/mfd/stm32-adc-core.c b/drivers/mfd/stm32-adc-core.c
>>> new file mode 100644
>>> index 0000000..bcf52fb
>>> --- /dev/null
>>> +++ b/drivers/mfd/stm32-adc-core.c
>>> @@ -0,0 +1,301 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * Inspired from: fsl-imx25-tsadc
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/irqchip/chained_irq.h>
>>> +#include <linux/irqdesc.h>
>>> +#include <linux/irqdomain.h>
>>> +#include <linux/mfd/stm32-adc-core.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/slab.h>
>>> +
>>> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>> +
>>> +/* STM32F4_ADC_CSR - bit fields */
>>> +#define STM32F4_EOC3 BIT(17)
>>> +#define STM32F4_EOC2 BIT(9)
>>> +#define STM32F4_EOC1 BIT(1)
>>> +
>>> +/* STM32F4_ADC_CCR - bit fields */
>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>> +
>>> +/* STM32 F4 maximum analog clock rate (from datasheet) */
>>> +#define STM32F4_ADC_MAX_CLK_RATE 36000000
>>> +
>>> +/**
>>> + * struct stm32_adc_priv - stm32 ADC core private data
>>> + * @irq: irq for ADC block
>>> + * @domain: irq domain reference
>>> + * @aclk: clock reference for the analog circuitry
>>> + * @vref: regulator reference
>>> + * @common: common data for all ADC instances
>>> + */
>>> +struct stm32_adc_priv {
>>> + int irq;
>>> + struct irq_domain *domain;
>>> + struct clk *aclk;
>>> + struct regulator *vref;
>>> + struct stm32_adc_common common;
>>> +};
>>> +
>>> +static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
>>> +{
>>> + return container_of(com, struct stm32_adc_priv, common);
>>> +}
>>> +
>>> +/* STM32F4 ADC internal common clock prescaler division ratios */
>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>> +
>>> +/**
>>> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
>>> + * @priv: stm32 ADC core private data
>>> + * Select clock prescaler used for analog conversions, before using ADC.
>>> + */
>>> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + unsigned long rate;
>>> + u32 val;
>>> + int i;
>>> +
>>> + rate = clk_get_rate(priv->aclk);
>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>> + if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
>>> + break;
>>> + }
>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>> + return -EINVAL;
>>> +
>>> + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>> + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
>>> +
>>> + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* ADC common interrupt for all instances */
>>> +static void stm32_adc_irq_handler(struct irq_desc *desc)
>>> +{
>>> + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
>>> + struct irq_chip *chip = irq_desc_get_chip(desc);
>>> + u32 status;
>>> +
>>> + chained_irq_enter(chip, desc);
>>> + status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
>>> +
>>> + if (status & STM32F4_EOC1)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 0));
>>> +
>>> + if (status & STM32F4_EOC2)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 1));
>>> +
>>> + if (status & STM32F4_EOC3)
>>> + generic_handle_irq(irq_find_mapping(priv->domain, 2));
>>> +
>>> + chained_irq_exit(chip, desc);
>>> +};
>>> +
>>> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
>>> + irq_hw_number_t hwirq)
>>> +{
>>> + irq_set_chip_data(irq, d->host_data);
>>> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
>>> +{
>>> + irq_set_chip_and_handler(irq, NULL, NULL);
>>> + irq_set_chip_data(irq, NULL);
>>> +}
>>> +
>>> +static const struct irq_domain_ops stm32_adc_domain_ops = {
>>> + .map = stm32_adc_domain_map,
>>> + .unmap = stm32_adc_domain_unmap,
>>> + .xlate = irq_domain_xlate_onecell,
>>> +};
>>> +
>>> +static int stm32_adc_irq_probe(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + struct device_node *np = pdev->dev.of_node;
>>> +
>>> + priv->irq = platform_get_irq(pdev, 0);
>>> + if (priv->irq < 0) {
>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>> + return priv->irq;
>>> + }
>>> +
>>> + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
>>> + &stm32_adc_domain_ops,
>>> + priv);
>>> + if (!priv->domain) {
>>> + dev_err(&pdev->dev, "Failed to add irq domain\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
>>> + irq_set_handler_data(priv->irq, priv);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void stm32_adc_irq_remove(struct platform_device *pdev,
>>> + struct stm32_adc_priv *priv)
>>> +{
>>> + int hwirq;
>>> +
>>> + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
>>> + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
>>> + irq_domain_remove(priv->domain);
>>> + irq_set_chained_handler(priv->irq, NULL);
>>> +}
>>> +
>>> +static int stm32_adc_probe(struct platform_device *pdev)
>>> +{
>>> + struct stm32_adc_priv *priv;
>>> + struct device_node *np = pdev->dev.of_node;
>>> + struct resource *res;
>>> + int ret;
>>> +
>>> + if (!pdev->dev.of_node)
>>> + return -ENODEV;
>>> +
>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(priv->common.base))
>>> + return PTR_ERR(priv->common.base);
>>> +
>>> + priv->vref = devm_regulator_get(&pdev->dev, "vref");
>>> + if (IS_ERR(priv->vref)) {
>>> + ret = PTR_ERR(priv->vref);
>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_enable(priv->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_get_voltage(priv->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>> + goto err_regulator_disable;
>>> + }
>>> + priv->common.vref_mv = ret / 1000;
>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
>>> +
>>> + priv->aclk = devm_clk_get(&pdev->dev, "adc");
>>> + if (IS_ERR(priv->aclk)) {
>>> + ret = PTR_ERR(priv->aclk);
>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + ret = clk_prepare_enable(priv->aclk);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "adc clk enable failed\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + ret = stm32f4_adc_clk_sel(pdev, priv);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "adc clk selection failed\n");
>>> + goto err_clk_disable;
>>> + }
>>> +
>>> + ret = stm32_adc_irq_probe(pdev, priv);
>>> + if (ret < 0)
>>> + goto err_clk_disable;
>>> +
>>> + platform_set_drvdata(pdev, &priv->common);
>>> +
>>> + ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "failed to populate DT children\n");
>>> + goto err_irq_remove;
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +err_irq_remove:
>>> + stm32_adc_irq_remove(pdev, priv);
>>> +
>>> +err_clk_disable:
>>> + clk_disable_unprepare(priv->aclk);
>>> +
>>> +err_regulator_disable:
>>> + regulator_disable(priv->vref);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int stm32_adc_remove(struct platform_device *pdev)
>>> +{
>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>> + struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
>>> +
>>> + of_platform_depopulate(&pdev->dev);
>>> + stm32_adc_irq_remove(pdev, priv);
>>> + clk_disable_unprepare(priv->aclk);
>>> + regulator_disable(priv->vref);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_adc_of_match[] = {
>>> + { .compatible = "st,stm32f4-adc-core" },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>>> +
>>> +static struct platform_driver stm32_adc_driver = {
>>> + .probe = stm32_adc_probe,
>>> + .remove = stm32_adc_remove,
>>> + .driver = {
>>> + .name = "stm32-adc-core",
>>> + .of_match_table = stm32_adc_of_match,
>>> + },
>>> +};
>>> +module_platform_driver(stm32_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC MFD driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>> diff --git a/include/linux/mfd/stm32-adc-core.h b/include/linux/mfd/stm32-adc-core.h
>>> new file mode 100644
>>> index 0000000..081fa5f
>>> --- /dev/null
>>> +++ b/include/linux/mfd/stm32-adc-core.h
>>> @@ -0,0 +1,52 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __STM32_ADC_H
>>> +#define __STM32_ADC_H
>>> +
>>> +/*
>>> + * STM32 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset | Register |
>>> + * --------------------------------------------------------
>>> + * | 0x000 | Master ADC1 |
>>> + * --------------------------------------------------------
>>> + * | 0x100 | Slave ADC2 |
>>> + * --------------------------------------------------------
>>> + * | 0x200 | Slave ADC3 |
>>> + * --------------------------------------------------------
>>> + * | 0x300 | Master & Slave common regs |
>>> + * --------------------------------------------------------
>>> + */
>>> +#define STM32_ADC_MAX_ADCS 3
>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>> +
>>> +/**
>>> + * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>>> + * @base: control registers base cpu addr
>>> + * @vref_mv: vref voltage (mv)
>>> + */
>>> +struct stm32_adc_common {
>>> + void __iomem *base;
>>> + int vref_mv;
>>> +};
>>> +
>>> +#endif
>>>
^ permalink raw reply
* Re: [PATCH v4 4/4] ARM: dts: da850: Add the usb otg device node
From: Sekhar Nori @ 2016-11-15 10:46 UTC (permalink / raw)
To: Alexandre Bailon, khilman, robh+dt, b-liu
Cc: linux-kernel, linux-usb, devicetree, linux-arm-kernel
In-Reply-To: <1478188752-22447-5-git-send-email-abailon@baylibre.com>
On Thursday 03 November 2016 09:29 PM, Alexandre Bailon wrote:
> This adds the device tree node for the usb otg
> controller present in the da850 family of SoC's.
> This also enables the otg usb controller for the lcdk board.
>
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
> ---
> arch/arm/boot/dts/da850-lcdk.dts | 8 ++++++++
> arch/arm/boot/dts/da850.dtsi | 15 +++++++++++++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/arch/arm/boot/dts/da850-lcdk.dts b/arch/arm/boot/dts/da850-lcdk.dts
> index 7b8ab21..9f5040c 100644
> --- a/arch/arm/boot/dts/da850-lcdk.dts
> +++ b/arch/arm/boot/dts/da850-lcdk.dts
> @@ -158,6 +158,14 @@
> rx-num-evt = <32>;
> };
>
> +&usb_phy {
> + status = "okay";
> + };
As mentioned by David already, this node needs to be removed. Please
rebase this on top of latest linux-davinci/master when ready for merging
(driver changes accepted).
> +
> +&usb0 {
> + status = "okay";
> +};
> +
> &aemif {
> pinctrl-names = "default";
> pinctrl-0 = <&nand_pins>;
> diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
> index f79e1b9..322a31a 100644
> --- a/arch/arm/boot/dts/da850.dtsi
> +++ b/arch/arm/boot/dts/da850.dtsi
> @@ -372,6 +372,21 @@
> >;
> status = "disabled";
> };
> + usb_phy: usb-phy {
> + compatible = "ti,da830-usb-phy";
> + #phy-cells = <1>;
> + status = "disabled";
> + };
> + usb0: usb@200000 {
> + compatible = "ti,da830-musb";
> + reg = <0x200000 0x10000>;
> + interrupts = <58>;
> + interrupt-names = "mc";
> + dr_mode = "otg";
> + phys = <&usb_phy 0>;
> + phy-names = "usb-phy";
> + status = "disabled";
> + };
Can you separate out the soc specific changes from board changes? Please
place the usb0 node above the mdio node. I am trying to get to a rough
ordering based on reg property.
Thanks,
Sekhar
^ permalink raw reply
* Re: [PATCH v9 2/4] soc: mediatek: Init MT8173 scpsys driver earlier
From: Honghui Zhang @ 2016-11-15 10:45 UTC (permalink / raw)
To: James Liao
Cc: Matthias Brugger, Yong Wu, Rob Herring,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sascha Hauer,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1477879702.6649.4.camel@mtksdaap41>
On Mon, 2016-10-31 at 10:08 +0800, James Liao wrote:
> On Mon, 2016-10-31 at 01:08 +0100, Matthias Brugger wrote:
> > Hi James,
> >
> > On 10/20/2016 10:56 AM, James Liao wrote:
> > > Some power domain comsumers may init before module_init.
> > > So the power domain provider (scpsys) need to be initialized
> > > earlier too.
> > >
> > > Take an example for our IOMMU (M4U) and SMI. SMI is a bridge
> > > between IOMMU and multimedia HW. SMI is responsible to
> > > enable/disable iommu and help transfer data for each multimedia
> > > HW. Both of them have to wait until the power and clocks are
> > > enabled.
> > >
> > > So scpsys driver should be initialized before SMI, and SMI should
> > > be initialized before IOMMU, and then init IOMMU consumers
> > > (display/vdec/venc/camera etc.).
> > >
> > > IOMMU is subsys_init by default. So we need to init scpsys driver
> > > before subsys_init.
> > >
> > > Signed-off-by: James Liao <jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > > Reviewed-by: Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> > > ---
> >
> > I didn't applied this patch for now.
> > I answered you in v7 of this series [1]. I would prefer to see if we can
> > fix that otherwise.
> >
> > Would be great if you or Yong could provide some feedback.
> >
Hi, Matthias,
Yong had verified your propose and it seems didn't work[1].
But I'm looking at the recently merged device link patches[2], it maybe
could help with the probe sequence issue.
If we set the iommu client as the smi larb's device consumer of
device_link with the DL_DEV_PROBING flags, it will be wait for smi
larb's probe done. But I'm not test that solution yet, need to do a bit
more test to verify this.
thanks.
[1]
https://lists.linuxfoundation.org/pipermail/iommu/2016-July/018034.html
[2]https://www.mail-archive.com/linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg1261311.html
> > Thanks,
> > Matthias
> >
> > [1] https://patchwork.kernel.org/patch/9397405/
> >
> > > drivers/soc/mediatek/mtk-scpsys.c | 19 ++++++++++++++++++-
> > > 1 file changed, 18 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
> > > index fa9ee69..dd7a07d 100644
> > > --- a/drivers/soc/mediatek/mtk-scpsys.c
> > > +++ b/drivers/soc/mediatek/mtk-scpsys.c
> > > @@ -613,4 +613,21 @@ static int scpsys_probe(struct platform_device *pdev)
> > > .of_match_table = of_match_ptr(of_scpsys_match_tbl),
> > > },
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v7 0/3] Add clockevent for timer-nps driver to NPS400 SoC
From: Daniel Lezcano @ 2016-11-15 10:33 UTC (permalink / raw)
To: Noam Camus
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Vineet Gupta
In-Reply-To: <DB6PR0501MB2518DAE81C1F5F87A6A1A2DDAABF0-wTfl6qNNZ1PL+HUNrKNnF8DSnupUy6xnnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
On Tue, Nov 15, 2016 at 10:02:01AM +0000, Noam Camus wrote:
> > From: Daniel Lezcano [mailto:daniel.lezcano-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org]
> > Sent: Tuesday, November 15, 2016 11:58 AM
>
> >Do you want me to take the entire series in my tree, or do you want my acked-by to push them in your tree ?
> I do not have tree of my own.
> Will appreciate if you take entire series.
> I believe it is same as you do with Synopsys ARC driver (by Vineet Gupta)
>
> Many Thanks
> Noam
Ok, I will sort it out with Vineet.
Thanks.
-- Daniel
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 2/3] vcodec: mediatek: Add Mediatek JPEG Decoder Driver
From: Rick Chang @ 2016-11-15 10:31 UTC (permalink / raw)
To: Hans Verkuil
Cc: Hans Verkuil, Laurent Pinchart, Mauro Carvalho Chehab,
Matthias Brugger, Rob Herring,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Minghsiu Tsai
In-Reply-To: <c0c2be8e-51ff-843a-6f3d-1fa0170cb209-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
Hi Hans,
Thank you for your review. I will fix those problems in patch v6.
On Fri, 2016-11-11 at 16:10 +0100, Hans Verkuil wrote:
> A quick review:
>
> On 11/08/2016 07:34 AM, Rick Chang wrote:
> > Add v4l2 driver for Mediatek JPEG Decoder
> >
> > Signed-off-by: Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > Signed-off-by: Minghsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > ---
> > drivers/media/platform/Kconfig | 15 +
> > drivers/media/platform/Makefile | 2 +
> > drivers/media/platform/mtk-jpeg/Makefile | 2 +
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 1275 ++++++++++++++++++++++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h | 141 +++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c | 417 +++++++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h | 91 ++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c | 160 +++
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h | 25 +
> > drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h | 58 +
> > 10 files changed, 2186 insertions(+)
> > create mode 100644 drivers/media/platform/mtk-jpeg/Makefile
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h
> > create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index 754edbf1..96c9887 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -162,6 +162,21 @@ config VIDEO_CODA
> > Coda is a range of video codec IPs that supports
> > H.264, MPEG-4, and other video formats.
> >
> > +config VIDEO_MEDIATEK_JPEG
> > + tristate "Mediatek JPEG Codec driver"
> > + depends on MTK_IOMMU_V1 || COMPILE_TEST
> > + depends on VIDEO_DEV && VIDEO_V4L2
> > + depends on ARCH_MEDIATEK || COMPILE_TEST
> > + depends on HAS_DMA
> > + select VIDEOBUF2_DMA_CONTIG
> > + select V4L2_MEM2MEM_DEV
> > + ---help---
> > + Mediatek jpeg codec driver provides HW capability to decode
> > + JPEG format
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called mtk-jpeg
> > +
> > config VIDEO_MEDIATEK_VPU
> > tristate "Mediatek Video Processor Unit"
> > depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index f842933..cf701e3 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -68,3 +68,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
> > obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/
> >
> > obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/
> > +
> > +obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/
> > diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mtk-jpeg/Makefile
> > new file mode 100644
> > index 0000000..b2e6069
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/Makefile
> > @@ -0,0 +1,2 @@
> > +mtk_jpeg-objs := mtk_jpeg_core.o mtk_jpeg_hw.o mtk_jpeg_parse.o
> > +obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk_jpeg.o
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > new file mode 100644
> > index 0000000..33ddf79
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> > @@ -0,0 +1,1275 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/err.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/spinlock.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-mem2mem.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/videobuf2-core.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +#include <soc/mediatek/smi.h>
> > +#include <asm/dma-iommu.h>
> > +
> > +#include "mtk_jpeg_hw.h"
> > +#include "mtk_jpeg_core.h"
> > +#include "mtk_jpeg_parse.h"
> > +
> > +static struct mtk_jpeg_fmt mtk_jpeg_formats[] = {
> > + {
> > + .name = "JPEG JFIF",
> > + .fourcc = V4L2_PIX_FMT_JPEG,
> > + .colplanes = 1,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT,
> > + },
> > + {
> > + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> > + .fourcc = V4L2_PIX_FMT_YUV420M,
> > + .h_sample = {4, 2, 2},
> > + .v_sample = {4, 2, 2},
> > + .colplanes = 3,
> > + .h_align = 5,
> > + .v_align = 4,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
> > + },
> > + {
> > + .name = "YUV 4:2:2 non-contiguous 3-planar, Y/Cb/Cr",
> > + .fourcc = V4L2_PIX_FMT_YUV422M,
> > + .h_sample = {4, 2, 2},
> > + .v_sample = {4, 4, 4},
> > + .colplanes = 3,
> > + .h_align = 5,
> > + .v_align = 3,
> > + .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
>
> You probably don't need the name since it is filled in by the v4l2 core
> (v4l2-ioctls.c).
Ok.
> > + },
> > +};
> > +
> > +#define MTK_JPEG_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_formats)
> > +
> > +enum {
> > + MTK_JPEG_BUF_FLAGS_INIT = 0,
> > + MTK_JPEG_BUF_FLAGS_LAST_FRAME = 1,
> > +};
> > +
> > +struct mtk_jpeg_src_buf {
> > + struct vb2_v4l2_buffer b;
> > + struct list_head list;
> > + int flags;
> > + struct mtk_jpeg_dec_param dec_param;
> > +};
> > +
> > +static int debug;
> > +module_param(debug, int, 0644);
> > +
> > +static inline struct mtk_jpeg_ctx *mtk_jpeg_fh_to_ctx(struct v4l2_fh *fh)
> > +{
> > + return container_of(fh, struct mtk_jpeg_ctx, fh);
> > +}
> > +
> > +static inline struct mtk_jpeg_src_buf *mtk_jpeg_vb2_to_srcbuf(
> > + struct vb2_buffer *vb)
> > +{
> > + return container_of(to_vb2_v4l2_buffer(vb), struct mtk_jpeg_src_buf, b);
> > +}
> > +
> > +static int mtk_jpeg_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > +
> > + strlcpy(cap->driver, MTK_JPEG_NAME " decoder", sizeof(cap->driver));
> > + strlcpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card));
> > + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> > + dev_name(jpeg->dev));
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n,
> > + struct v4l2_fmtdesc *f, u32 type)
> > +{
> > + int i, num = 0;
> > +
> > + for (i = 0; i < n; ++i) {
> > + if (mtk_jpeg_formats[i].flags & type) {
> > + if (num == f->index)
> > + break;
> > + ++num;
> > + }
> > + }
> > +
> > + if (i >= n)
> > + return -EINVAL;
> > +
> > + f->pixelformat = mtk_jpeg_formats[i].fourcc;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> > + MTK_JPEG_FMT_FLAG_DEC_CAPTURE);
> > +}
> > +
> > +static int mtk_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> > + MTK_JPEG_FMT_FLAG_DEC_OUTPUT);
> > +}
> > +
> > +static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx,
> > + enum v4l2_buf_type type)
> > +{
> > + if (V4L2_TYPE_IS_OUTPUT(type))
> > + return &ctx->out_q;
> > + else
>
> No need for 'else'.
Ok.
> > + return &ctx->cap_q;
> > +}
> > +
> > +static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx,
> > + u32 pixelformat,
> > + unsigned int fmt_type)
> > +{
> > + unsigned int k, fmt_flag;
> > +
> > + fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
> > + MTK_JPEG_FMT_FLAG_DEC_OUTPUT :
> > + MTK_JPEG_FMT_FLAG_DEC_CAPTURE;
> > +
> > + for (k = 0; k < MTK_JPEG_NUM_FORMATS; k++) {
> > + struct mtk_jpeg_fmt *fmt = &mtk_jpeg_formats[k];
> > +
> > + if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag)
> > + return fmt;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static void mtk_jpeg_bound_align_image(u32 *w, unsigned int wmin,
> > + unsigned int wmax, unsigned int walign,
> > + u32 *h, unsigned int hmin,
> > + unsigned int hmax, unsigned int halign)
> > +{
> > + int width, height, w_step, h_step;
> > +
> > + width = *w;
> > + height = *h;
> > + w_step = 1 << walign;
> > + h_step = 1 << halign;
> > +
> > + v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
> > + if (*w < width && (*w + w_step) <= wmax)
> > + *w += w_step;
> > + if (*h < height && (*h + h_step) <= hmax)
> > + *h += h_step;
> > +}
> > +
> > +static void mtk_jpeg_adjust_fmt_mplane(struct mtk_jpeg_ctx *ctx,
> > + struct v4l2_format *f)
> > +{
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_q_data *q_data;
> > + int i;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + pix_mp->width = q_data->w;
> > + pix_mp->height = q_data->h;
> > + pix_mp->pixelformat = q_data->fmt->fourcc;
> > + pix_mp->num_planes = q_data->fmt->colplanes;
> > +
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i];
> > + pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i];
> > + }
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f,
> > + struct mtk_jpeg_fmt *fmt,
> > + struct mtk_jpeg_ctx *ctx, int q_type)
> > +{
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> > + pix_mp->field = V4L2_FIELD_NONE;
> > +
> > + if (ctx->state != MTK_JPEG_INIT) {
> > + mtk_jpeg_adjust_fmt_mplane(ctx, f);
> > + goto end;
> > + }
> > +
> > + pix_mp->num_planes = fmt->colplanes;
> > + pix_mp->pixelformat = fmt->fourcc;
> > +
> > + if (q_type == MTK_JPEG_FMT_TYPE_OUTPUT) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[0];
> > +
> > + mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> > + MTK_JPEG_MAX_WIDTH, 0,
> > + &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> > + MTK_JPEG_MAX_HEIGHT, 0);
> > +
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > + pfmt->bytesperline = 0;
> > + /* Source size must be aligned to 128 */
> > + pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128);
> > + if (pfmt->sizeimage == 0)
> > + pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE;
> > + goto end;
> > + }
> > +
> > + /* type is MTK_JPEG_FMT_TYPE_CAPTURE */
> > + mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> > + MTK_JPEG_MAX_WIDTH, fmt->h_align,
> > + &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> > + MTK_JPEG_MAX_HEIGHT, fmt->v_align);
> > +
> > + for (i = 0; i < fmt->colplanes; i++) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
> > + u32 stride = pix_mp->width * fmt->h_sample[i] / 4;
> > + u32 h = pix_mp->height * fmt->v_sample[i] / 4;
> > +
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > + pfmt->bytesperline = stride;
> > + pfmt->sizeimage = stride * h;
> > + }
> > +end:
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "wxh:%ux%u\n",
> > + pix_mp->width, pix_mp->height);
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i,
> > + pix_mp->plane_fmt[i].bytesperline,
> > + pix_mp->plane_fmt[i].sizeimage);
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct vb2_queue *vq;
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (!vq)
> > + return -EINVAL;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> > + pix_mp->width = q_data->w;
> > + pix_mp->height = q_data->h;
> > + pix_mp->field = V4L2_FIELD_NONE;
> > + pix_mp->pixelformat = q_data->fmt->fourcc;
> > + pix_mp->num_planes = q_data->fmt->colplanes;
> > + pix_mp->colorspace = ctx->colorspace;
> > + pix_mp->ycbcr_enc = ctx->ycbcr_enc;
> > + pix_mp->xfer_func = ctx->xfer_func;
> > + pix_mp->quantization = ctx->quantization;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) g_fmt:%s wxh:%ux%u\n",
> > + f->type, q_data->fmt->name, pix_mp->width, pix_mp->height);
> > +
> > + for (i = 0; i < pix_mp->num_planes; i++) {
> > + struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
> > +
> > + pfmt->bytesperline = q_data->bytesperline[i];
> > + pfmt->sizeimage = q_data->sizeimage[i];
> > + memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i,
> > + pfmt->bytesperline,
> > + pfmt->sizeimage);
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_vid_cap_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_fmt *fmt;
> > +
> > + fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > + if (!fmt)
> > + fmt = ctx->cap_q.fmt;
> > +
> > + v4l2_dbg(2, debug, &ctx->jpeg->v4l2_dev, "(%d) try_fmt:%s\n",
> > + f->type, fmt->name);
> > +
> > + return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_CAPTURE);
> > +}
> > +
> > +static int mtk_jpeg_try_fmt_vid_out_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > + struct mtk_jpeg_fmt *fmt;
> > +
> > + fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat,
> > + MTK_JPEG_FMT_TYPE_OUTPUT);
> > + if (!fmt)
> > + fmt = ctx->out_q.fmt;
> > +
> > + v4l2_dbg(2, debug, &ctx->jpeg->v4l2_dev, "(%d) try_fmt:%s\n",
> > + f->type, fmt->name);
> > +
> > + return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_OUTPUT);
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx,
> > + struct v4l2_format *f)
> > +{
> > + struct vb2_queue *vq;
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + unsigned int f_type;
> > + int i;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (!vq)
> > + return -EINVAL;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, f->type);
> > +
> > + if (vb2_is_busy(vq)) {
> > + v4l2_err(&jpeg->v4l2_dev, "queue busy\n");
> > + return -EBUSY;
> > + }
> > +
> > + f_type = V4L2_TYPE_IS_OUTPUT(f->type) ?
> > + MTK_JPEG_FMT_TYPE_OUTPUT : MTK_JPEG_FMT_TYPE_CAPTURE;
> > +
> > + q_data->fmt = mtk_jpeg_find_format(ctx, pix_mp->pixelformat, f_type);
> > + q_data->w = pix_mp->width;
> > + q_data->h = pix_mp->height;
> > + ctx->colorspace = pix_mp->colorspace;
> > + ctx->ycbcr_enc = pix_mp->ycbcr_enc;
> > + ctx->xfer_func = pix_mp->xfer_func;
> > + ctx->quantization = pix_mp->quantization;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) s_fmt:%s wxh:%ux%u\n",
> > + f->type, q_data->fmt->name, q_data->w, q_data->h);
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
> > + q_data->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "plane[%d] bpl=%u, size=%u\n",
> > + i, q_data->bytesperline[i], q_data->sizeimage[i]);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_vid_out_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + int ret;
> > +
> > + ret = mtk_jpeg_try_fmt_vid_out_mplane(file, priv, f);
> > + if (ret)
> > + return ret;
> > +
> > + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
> > +}
> > +
> > +static int mtk_jpeg_s_fmt_vid_cap_mplane(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + int ret;
> > +
> > + ret = mtk_jpeg_try_fmt_vid_cap_mplane(file, priv, f);
> > + if (ret)
> > + return ret;
> > +
> > + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f);
> > +}
> > +
> > +static void mtk_jpeg_queue_src_chg_event(struct mtk_jpeg_ctx *ctx)
> > +{
> > + static const struct v4l2_event ev_src_ch = {
> > + .type = V4L2_EVENT_SOURCE_CHANGE,
> > + .u.src_change.changes =
> > + V4L2_EVENT_SRC_CH_RESOLUTION,
> > + };
> > +
> > + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
> > +}
> > +
> > +static int mtk_jpeg_subscribe_event(struct v4l2_fh *fh,
> > + const struct v4l2_event_subscription *sub)
> > +{
> > + switch (sub->type) {
> > + case V4L2_EVENT_SOURCE_CHANGE:
> > + return v4l2_src_change_event_subscribe(fh, sub);
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int mtk_jpeg_g_selection(struct file *file, void *priv,
> > + struct v4l2_selection *s)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > +
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_COMPOSE:
> > + case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> > + s->r.width = ctx->out_q.w;
> > + s->r.height = ctx->out_q.h;
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + break;
> > + case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> > + case V4L2_SEL_TGT_COMPOSE_PADDED:
> > + s->r.width = ctx->cap_q.w;
> > + s->r.height = ctx->cap_q.h;
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_s_selection(struct file *file, void *priv,
> > + struct v4l2_selection *s)
> > +{
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > +
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_COMPOSE:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = ctx->out_q.w;
> > + s->r.height = ctx->out_q.h;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> > +{
> > + struct v4l2_fh *fh = file->private_data;
> > + struct vb2_queue *vq;
> > + struct vb2_buffer *vb;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > +
> > + if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + goto end;
> > +
> > + vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type);
> > + vb = vq->bufs[buf->index];
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + jpeg_src_buf->flags = (buf->m.planes[0].bytesused == 0) ?
> > + MTK_JPEG_BUF_FLAGS_LAST_FRAME : MTK_JPEG_BUF_FLAGS_INIT;
> > +end:
> > + return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf);
> > +}
> > +
> > +static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = {
> > + .vidioc_querycap = mtk_jpeg_querycap,
> > + .vidioc_enum_fmt_vid_cap_mplane = mtk_jpeg_enum_fmt_vid_cap,
> > + .vidioc_enum_fmt_vid_out_mplane = mtk_jpeg_enum_fmt_vid_out,
> > + .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane,
> > + .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane,
> > + .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane,
> > + .vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane,
> > + .vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane,
> > + .vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane,
> > + .vidioc_qbuf = mtk_jpeg_qbuf,
> > + .vidioc_subscribe_event = mtk_jpeg_subscribe_event,
> > + .vidioc_g_selection = mtk_jpeg_g_selection,
> > + .vidioc_s_selection = mtk_jpeg_s_selection,
> > +
> > + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> > + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> > +
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static int mtk_jpeg_queue_setup(struct vb2_queue *q,
> > + unsigned int *num_buffers,
> > + unsigned int *num_planes,
> > + unsigned int sizes[],
> > + struct device *alloc_ctxs[])
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + int i;
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) buf_req count=%u\n",
> > + q->type, *num_buffers);
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, q->type);
> > + if (!q_data)
> > + return -EINVAL;
> > +
> > + *num_planes = q_data->fmt->colplanes;
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + sizes[i] = q_data->sizeimage[i];
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "sizeimage[%d]=%u\n",
> > + i, sizes[i]);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_buf_prepare(struct vb2_buffer *vb)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct mtk_jpeg_q_data *q_data = NULL;
> > + int i;
> > +
> > + q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type);
> > + if (!q_data)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++)
> > + vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
> > +
> > + return 0;
> > +}
> > +
> > +static bool mtk_jpeg_check_resolution_change(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param)
> > +{
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_q_data *q_data;
> > +
> > + q_data = &ctx->out_q;
> > + if (q_data->w != param->pic_w || q_data->h != param->pic_h) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Picture size change\n");
> > + return true;
> > + }
> > +
> > + q_data = &ctx->cap_q;
> > + if (q_data->fmt != mtk_jpeg_find_format(ctx, param->dst_fourcc,
> > + MTK_JPEG_FMT_TYPE_CAPTURE)) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "format change\n");
> > + return true;
> > + }
> > + return false;
> > +}
> > +
> > +static void mtk_jpeg_set_queue_data(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param)
> > +{
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_q_data *q_data;
> > + int i;
> > +
> > + q_data = &ctx->out_q;
> > + q_data->w = param->pic_w;
> > + q_data->h = param->pic_h;
> > +
> > + q_data = &ctx->cap_q;
> > + q_data->w = param->dec_w;
> > + q_data->h = param->dec_h;
> > + q_data->fmt = mtk_jpeg_find_format(ctx,
> > + param->dst_fourcc,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > +
> > + for (i = 0; i < q_data->fmt->colplanes; i++) {
> > + q_data->bytesperline[i] = param->mem_stride[i];
> > + q_data->sizeimage[i] = param->comp_size[i];
> > + }
> > +
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev,
> > + "set_parse cap:%s pic(%u, %u), buf(%u, %u)\n",
> > + q_data->fmt->name, param->pic_w, param->pic_h,
> > + param->dec_w, param->dec_h);
> > +}
> > +
> > +static void mtk_jpeg_buf_queue(struct vb2_buffer *vb)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct mtk_jpeg_dec_param *param;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + bool header_valid;
> > +
> > + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "(%d) buf_q id=%d, vb=%p",
> > + vb->vb2_queue->type, vb->index, vb);
> > +
> > + if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + goto end;
> > +
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + param = &jpeg_src_buf->dec_param;
> > + memset(param, 0, sizeof(*param));
> > +
> > + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> > + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos");
> > + goto end;
> > + }
> > + header_valid = mtk_jpeg_parse(param, (u8 *)vb2_plane_vaddr(vb, 0),
> > + vb2_get_plane_payload(vb, 0));
> > + if (!header_valid) {
> > + v4l2_err(&jpeg->v4l2_dev, "Header invalid.\n");
> > + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> > + return;
> > + }
> > +
> > + if (ctx->state == MTK_JPEG_INIT) {
> > + mtk_jpeg_queue_src_chg_event(ctx);
> > + mtk_jpeg_set_queue_data(ctx, param);
> > + ctx->state = MTK_JPEG_RUNNING;
> > + }
> > +end:
> > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
> > +}
> > +
> > +static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx,
> > + enum v4l2_buf_type type)
> > +{
> > + if (V4L2_TYPE_IS_OUTPUT(type))
> > + return v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + else
> > + return v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > +}
> > +
> > +static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + int ret = 0;
> > +
> > + ret = pm_runtime_get_sync(ctx->jpeg->dev);
> > +
>
> If start_streaming returns an error, then you must call
> v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_QUEUED) for all
> buffers. Similar to what happens in stop_streaming, but with a different VB2_BUF_STATE.
Ok.
> > + return ret > 0 ? 0 : ret;
> > +}
> > +
> > +static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
> > +{
> > + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q);
> > + struct vb2_buffer *vb;
> > +
> > + /*
> > + * STREAMOFF is an acknowledgment for source change event.
> > + * Before STREAMOFF, we still have to return the old resolution and
> > + * subsampling. Update capture queue when the stream is off.
> > + */
> > + if (ctx->state == MTK_JPEG_SOURCE_CHANGE &&
> > + !V4L2_TYPE_IS_OUTPUT(q->type)) {
> > + struct mtk_jpeg_src_buf *src_buf;
> > +
> > + vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> > + src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> > + mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param);
> > + ctx->state = MTK_JPEG_RUNNING;
> > + } else if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> > + ctx->state = MTK_JPEG_INIT;
> > + }
> > +
> > + vb = mtk_jpeg_buf_remove(ctx, q->type);
> > + while (vb) {
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR);
> > + vb = mtk_jpeg_buf_remove(ctx, q->type);
> > + }
> > +
> > + pm_runtime_put_sync(ctx->jpeg->dev);
> > +}
> > +
> > +static struct vb2_ops mtk_jpeg_qops = {
> > + .queue_setup = mtk_jpeg_queue_setup,
> > + .buf_prepare = mtk_jpeg_buf_prepare,
> > + .buf_queue = mtk_jpeg_buf_queue,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > + .start_streaming = mtk_jpeg_start_streaming,
> > + .stop_streaming = mtk_jpeg_stop_streaming,
> > +};
> > +
> > +static void mtk_jpeg_set_dec_src(struct mtk_jpeg_ctx *ctx,
> > + struct vb2_buffer *src_buf,
> > + struct mtk_jpeg_bs *bs)
> > +{
> > + bs->str_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
> > + bs->end_addr = bs->str_addr +
> > + mtk_jpeg_align(vb2_get_plane_payload(src_buf, 0), 16);
> > + bs->size = mtk_jpeg_align(vb2_plane_size(src_buf, 0), 128);
> > +}
> > +
> > +static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx,
> > + struct mtk_jpeg_dec_param *param,
> > + struct vb2_buffer *dst_buf,
> > + struct mtk_jpeg_fb *fb)
> > +{
> > + int i;
> > +
> > + if (param->comp_num != dst_buf->num_planes) {
> > + dev_err(ctx->jpeg->dev, "plane number mismatch (%u != %u)\n",
> > + param->comp_num, dst_buf->num_planes);
> > + return -EINVAL;
> > + }
> > +
> > + for (i = 0; i < dst_buf->num_planes; i++) {
> > + if (vb2_plane_size(dst_buf, i) < param->comp_size[i]) {
> > + dev_err(ctx->jpeg->dev,
> > + "buffer size is underflow (%lu < %u)\n",
> > + vb2_plane_size(dst_buf, 0),
> > + param->comp_size[i]);
> > + return -EINVAL;
> > + }
> > + fb->plane_addr[i] = vb2_dma_contig_plane_dma_addr(dst_buf, i);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void mtk_jpeg_device_run(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
> > + unsigned long flags;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + struct mtk_jpeg_bs bs;
> > + struct mtk_jpeg_fb fb;
> > + int i;
> > +
> > + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
> > +
> > + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> > + for (i = 0; i < dst_buf->num_planes; i++)
> > + vb2_set_plane_payload(dst_buf, i, 0);
> > + buf_state = VB2_BUF_STATE_DONE;
> > + goto dec_end;
> > + }
> > +
> > + if (mtk_jpeg_check_resolution_change(ctx, &jpeg_src_buf->dec_param)) {
> > + mtk_jpeg_queue_src_chg_event(ctx);
> > + ctx->state = MTK_JPEG_SOURCE_CHANGE;
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > + return;
> > + }
> > +
> > + mtk_jpeg_set_dec_src(ctx, src_buf, &bs);
> > + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, dst_buf, &fb))
> > + goto dec_end;
> > +
> > + spin_lock_irqsave(&jpeg->hw_lock, flags);
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > + mtk_jpeg_dec_set_config(jpeg->dec_reg_base,
> > + &jpeg_src_buf->dec_param, &bs, &fb);
> > +
> > + mtk_jpeg_dec_start(jpeg->dec_reg_base);
> > + spin_unlock_irqrestore(&jpeg->hw_lock, flags);
> > + return;
> > +
> > +dec_end:
> > + v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > +}
> > +
> > +static int mtk_jpeg_job_ready(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > +
> > + return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0;
> > +}
> > +
> > +static void mtk_jpeg_job_abort(void *priv)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > +
> > + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), VB2_BUF_STATE_ERROR);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_ERROR);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > +}
> > +
> > +static struct v4l2_m2m_ops mtk_jpeg_m2m_ops = {
> > + .device_run = mtk_jpeg_device_run,
> > + .job_ready = mtk_jpeg_job_ready,
> > + .job_abort = mtk_jpeg_job_abort,
> > +};
> > +
> > +static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq,
> > + struct vb2_queue *dst_vq)
> > +{
> > + struct mtk_jpeg_ctx *ctx = priv;
> > + int ret;
> > +
> > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
>
> I would drop USERPTR, it really makes little sense for dma_contig.
Ok.
> > + src_vq->drv_priv = ctx;
> > + src_vq->buf_struct_size = sizeof(struct mtk_jpeg_src_buf);
> > + src_vq->ops = &mtk_jpeg_qops;
> > + src_vq->mem_ops = &vb2_dma_contig_memops;
> > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + src_vq->lock = &ctx->jpeg->lock;
> > + src_vq->dev = ctx->jpeg->dev;
> > + ret = vb2_queue_init(src_vq);
> > + if (ret)
> > + return ret;
> > +
> > + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
>
> Ditto.
Ok.
> > + dst_vq->drv_priv = ctx;
> > + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> > + dst_vq->ops = &mtk_jpeg_qops;
> > + dst_vq->mem_ops = &vb2_dma_contig_memops;
> > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + dst_vq->lock = &ctx->jpeg->lock;
> > + dst_vq->dev = ctx->jpeg->dev;
> > + ret = vb2_queue_init(dst_vq);
> > +
> > + return ret;
> > +}
> > +
> > +static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg)
> > +{
> > + int ret;
> > +
> > + ret = mtk_smi_larb_get(jpeg->larb);
> > + if (ret)
> > + dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret);
> > + clk_prepare_enable(jpeg->clk_jdec_smi);
> > + clk_prepare_enable(jpeg->clk_jdec);
> > +}
> > +
> > +static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg)
> > +{
> > + clk_disable_unprepare(jpeg->clk_jdec);
> > + clk_disable_unprepare(jpeg->clk_jdec_smi);
> > + mtk_smi_larb_put(jpeg->larb);
> > +}
> > +
> > +static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
> > +{
> > + struct mtk_jpeg_dev *jpeg = priv;
> > + struct mtk_jpeg_ctx *ctx;
> > + struct vb2_buffer *src_buf, *dst_buf;
> > + struct mtk_jpeg_src_buf *jpeg_src_buf;
> > + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
> > + u32 dec_irq_ret;
> > + u32 dec_ret;
> > + int i;
> > +
> > + ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
> > + if (!ctx) {
> > + v4l2_err(&jpeg->v4l2_dev, "Context is NULL\n");
> > + return IRQ_HANDLED;
> > + }
> > +
> > + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf);
> > +
> > + dec_ret = mtk_jpeg_dec_get_int_status(jpeg->dec_reg_base);
> > + dec_irq_ret = mtk_jpeg_dec_enum_result(dec_ret);
> > +
> > + if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > +
> > + if (dec_irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) {
> > + dev_err(jpeg->dev, "decode failed\n");
> > + goto dec_end;
> > + }
> > +
> > + for (i = 0; i < dst_buf->num_planes; i++)
> > + vb2_set_plane_payload(dst_buf, i,
> > + jpeg_src_buf->dec_param.comp_size[i]);
> > +
> > + buf_state = VB2_BUF_STATE_DONE;
> > +
> > +dec_end:
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
> > + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
> > + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static void mtk_jpeg_set_default_params(struct mtk_jpeg_ctx *ctx)
> > +{
> > + struct mtk_jpeg_q_data *q = &ctx->out_q;
> > + int i;
> > +
> > + ctx->colorspace = V4L2_COLORSPACE_JPEG,
> > + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> > + ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
> > + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> > +
> > + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
> > + MTK_JPEG_FMT_TYPE_OUTPUT);
> > + q->w = MTK_JPEG_MIN_WIDTH;
> > + q->h = MTK_JPEG_MIN_HEIGHT;
> > + q->bytesperline[0] = 0;
> > + q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
> > +
> > + q = &ctx->cap_q;
> > + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M,
> > + MTK_JPEG_FMT_TYPE_CAPTURE);
> > + q->w = MTK_JPEG_MIN_WIDTH;
> > + q->h = MTK_JPEG_MIN_HEIGHT;
> > +
> > + for (i = 0; i < q->fmt->colplanes; i++) {
> > + u32 stride = q->w * q->fmt->h_sample[i] / 4;
> > + u32 h = q->h * q->fmt->v_sample[i] / 4;
> > +
> > + q->bytesperline[i] = stride;
> > + q->sizeimage[i] = stride * h;
> > + }
> > +}
> > +
> > +static int mtk_jpeg_open(struct file *file)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > + struct video_device *vfd = video_devdata(file);
> > + struct mtk_jpeg_ctx *ctx;
> > + int ret = 0;
> > +
> > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > +
> > + if (mutex_lock_interruptible(&jpeg->lock)) {
> > + ret = -ERESTARTSYS;
> > + goto free;
> > + }
> > +
> > + v4l2_fh_init(&ctx->fh, vfd);
> > + file->private_data = &ctx->fh;
> > + v4l2_fh_add(&ctx->fh);
> > +
> > + ctx->jpeg = jpeg;
> > + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx,
> > + mtk_jpeg_queue_init);
> > + if (IS_ERR(ctx->fh.m2m_ctx)) {
> > + ret = PTR_ERR(ctx->fh.m2m_ctx);
> > + goto error;
> > + }
> > +
> > + mtk_jpeg_set_default_params(ctx);
> > + mutex_unlock(&jpeg->lock);
> > + return 0;
> > +
> > +error:
> > + v4l2_fh_del(&ctx->fh);
> > + v4l2_fh_exit(&ctx->fh);
> > + mutex_unlock(&jpeg->lock);
> > +free:
> > + kfree(ctx);
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_release(struct file *file)
> > +{
> > + struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> > + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(file->private_data);
> > +
> > + mutex_lock(&jpeg->lock);
> > + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> > + v4l2_fh_del(&ctx->fh);
> > + v4l2_fh_exit(&ctx->fh);
> > + kfree(ctx);
> > + mutex_unlock(&jpeg->lock);
> > + return 0;
> > +}
> > +
> > +static const struct v4l2_file_operations mtk_jpeg_fops = {
> > + .owner = THIS_MODULE,
> > + .open = mtk_jpeg_open,
> > + .release = mtk_jpeg_release,
> > + .poll = v4l2_m2m_fop_poll,
> > + .unlocked_ioctl = video_ioctl2,
> > + .mmap = v4l2_m2m_fop_mmap,
> > +};
> > +
> > +static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
> > +{
> > + struct device_node *node;
> > + struct platform_device *pdev;
> > +
> > + node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0);
> > + if (!node)
> > + return -EINVAL;
> > + pdev = of_find_device_by_node(node);
> > + if (WARN_ON(!pdev)) {
> > + of_node_put(node);
> > + return -EINVAL;
> > + }
> > + of_node_put(node);
> > +
> > + jpeg->larb = &pdev->dev;
> > +
> > + jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec");
> > + if (IS_ERR(jpeg->clk_jdec))
> > + return -EINVAL;
> > +
> > + jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
> > + if (IS_ERR(jpeg->clk_jdec_smi))
> > + return -EINVAL;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_probe(struct platform_device *pdev)
> > +{
> > + struct mtk_jpeg_dev *jpeg;
> > + struct resource *res;
> > + int dec_irq;
> > + int ret;
> > +
> > + jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL);
> > + if (!jpeg)
> > + return -ENOMEM;
> > +
> > + mutex_init(&jpeg->lock);
> > + spin_lock_init(&jpeg->hw_lock);
> > + jpeg->dev = &pdev->dev;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + jpeg->dec_reg_base = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(jpeg->dec_reg_base)) {
> > + ret = PTR_ERR(jpeg->dec_reg_base);
> > + return ret;
> > + }
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > + dec_irq = platform_get_irq(pdev, 0);
> > + if (!res || dec_irq < 0) {
> > + dev_err(&pdev->dev, "Failed to get dec_irq %d.\n", dec_irq);
> > + ret = -EINVAL;
> > + return ret;
> > + }
> > +
> > + ret = devm_request_irq(&pdev->dev, dec_irq, mtk_jpeg_dec_irq, 0,
> > + pdev->name, jpeg);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to request dec_irq %d (%d)\n",
> > + dec_irq, ret);
> > + ret = -EINVAL;
> > + goto err_req_irq;
> > + }
> > +
> > + ret = mtk_jpeg_clk_init(jpeg);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to init clk, err %d\n", ret);
> > + goto err_clk_init;
> > + }
> > +
> > + ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Failed to register v4l2 device\n");
> > + ret = -EINVAL;
> > + goto err_dev_register;
> > + }
> > +
> > + jpeg->m2m_dev = v4l2_m2m_init(&mtk_jpeg_m2m_ops);
> > + if (IS_ERR(jpeg->m2m_dev)) {
> > + v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
> > + ret = PTR_ERR(jpeg->m2m_dev);
> > + goto err_m2m_init;
> > + }
> > +
> > + jpeg->dec_vdev = video_device_alloc();
> > + if (!jpeg->dec_vdev) {
> > + ret = -ENOMEM;
> > + goto err_dec_vdev_alloc;
> > + }
> > + snprintf(jpeg->dec_vdev->name, sizeof(jpeg->dec_vdev->name),
> > + "%s-dec", MTK_JPEG_NAME);
> > + jpeg->dec_vdev->fops = &mtk_jpeg_fops;
> > + jpeg->dec_vdev->ioctl_ops = &mtk_jpeg_ioctl_ops;
> > + jpeg->dec_vdev->minor = -1;
> > + jpeg->dec_vdev->release = video_device_release;
> > + jpeg->dec_vdev->lock = &jpeg->lock;
> > + jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev;
> > + jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
> > + jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
> > + V4L2_CAP_VIDEO_M2M_MPLANE;
> > +
> > + ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3);
> > + if (ret) {
> > + v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
> > + goto err_dec_vdev_register;
> > + }
> > +
> > + video_set_drvdata(jpeg->dec_vdev, jpeg);
> > + v4l2_info(&jpeg->v4l2_dev,
> > + "decoder device registered as /dev/video%d (%d,%d)\n",
> > + jpeg->dec_vdev->num, VIDEO_MAJOR, jpeg->dec_vdev->minor);
> > +
> > + platform_set_drvdata(pdev, jpeg);
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +
> > +err_dec_vdev_register:
> > + video_device_release(jpeg->dec_vdev);
> > +
> > +err_dec_vdev_alloc:
> > + v4l2_m2m_release(jpeg->m2m_dev);
> > +
> > +err_m2m_init:
> > + v4l2_device_unregister(&jpeg->v4l2_dev);
> > +
> > +err_dev_register:
> > +
> > +err_clk_init:
> > +
> > +err_req_irq:
> > +
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_remove(struct platform_device *pdev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev);
> > +
> > + pm_runtime_disable(&pdev->dev);
> > + video_unregister_device(jpeg->dec_vdev);
> > + video_device_release(jpeg->dec_vdev);
> > + v4l2_m2m_release(jpeg->m2m_dev);
> > + v4l2_device_unregister(&jpeg->v4l2_dev);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int mtk_jpeg_pm_suspend(struct device *dev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
> > +
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > + mtk_jpeg_clk_off(jpeg);
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_pm_resume(struct device *dev)
> > +{
> > + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
> > +
> > + mtk_jpeg_clk_on(jpeg);
> > + mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> > +
> > + return 0;
> > +}
> > +#endif /* CONFIG_PM */
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_jpeg_suspend(struct device *dev)
> > +{
> > + int ret;
> > +
> > + if (pm_runtime_suspended(dev))
> > + return 0;
> > +
> > + ret = mtk_jpeg_pm_suspend(dev);
> > + return ret;
> > +}
> > +
> > +static int mtk_jpeg_resume(struct device *dev)
> > +{
> > + int ret;
> > +
> > + if (pm_runtime_suspended(dev))
> > + return 0;
> > +
> > + ret = mtk_jpeg_pm_resume(dev);
> > +
> > + return ret;
> > +}
> > +#endif /* CONFIG_PM_SLEEP */
> > +
> > +static const struct dev_pm_ops mtk_jpeg_pm_ops = {
> > + SET_SYSTEM_SLEEP_PM_OPS(mtk_jpeg_suspend, mtk_jpeg_resume)
> > + SET_RUNTIME_PM_OPS(mtk_jpeg_pm_suspend, mtk_jpeg_pm_resume, NULL)
> > +};
> > +
> > +static const struct of_device_id mtk_jpeg_match[] = {
> > + {
> > + .compatible = "mediatek,mt8173-jpgdec",
> > + .data = NULL,
> > + },
> > + {
> > + .compatible = "mediatek,mt2701-jpgdec",
> > + .data = NULL,
> > + },
> > + {},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, mtk_jpeg_match);
> > +
> > +static struct platform_driver mtk_jpeg_driver = {
> > + .probe = mtk_jpeg_probe,
> > + .remove = mtk_jpeg_remove,
> > + .driver = {
> > + .owner = THIS_MODULE,
> > + .name = MTK_JPEG_NAME,
> > + .of_match_table = mtk_jpeg_match,
> > + .pm = &mtk_jpeg_pm_ops,
> > + },
> > +};
> > +
> > +module_platform_driver(mtk_jpeg_driver);
> > +
> > +MODULE_DESCRIPTION("MediaTek JPEG codec driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > new file mode 100644
> > index 0000000..d862e3b
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
> > @@ -0,0 +1,141 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef _MTK_JPEG_CORE_H
> > +#define _MTK_JPEG_CORE_H
> > +
> > +#include <linux/interrupt.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-fh.h>
> > +
> > +#define MTK_JPEG_NAME "mtk-jpeg"
> > +
> > +#define MTK_JPEG_FMT_FLAG_DEC_OUTPUT BIT(0)
> > +#define MTK_JPEG_FMT_FLAG_DEC_CAPTURE BIT(1)
> > +
> > +#define MTK_JPEG_FMT_TYPE_OUTPUT 1
> > +#define MTK_JPEG_FMT_TYPE_CAPTURE 2
> > +
> > +#define MTK_JPEG_MIN_WIDTH 32
> > +#define MTK_JPEG_MIN_HEIGHT 32
> > +#define MTK_JPEG_MAX_WIDTH 8192
> > +#define MTK_JPEG_MAX_HEIGHT 8192
> > +
> > +#define MTK_JPEG_DEFAULT_SIZEIMAGE (1 * 1024 * 1024)
> > +
> > +enum mtk_jpeg_ctx_state {
> > + MTK_JPEG_INIT = 0,
> > + MTK_JPEG_RUNNING,
> > + MTK_JPEG_SOURCE_CHANGE,
> > +};
> > +
> > +/**
> > + * struct mt_jpeg - JPEG IP abstraction
> > + * @lock: the mutex protecting this structure
> > + * @hw_lock: spinlock protecting the hw device resource
> > + * @workqueue: decode work queue
> > + * @dev: JPEG device
> > + * @v4l2_dev: v4l2 device for mem2mem mode
> > + * @m2m_dev: v4l2 mem2mem device data
> > + * @alloc_ctx: videobuf2 memory allocator's context
> > + * @dec_vdev: video device node for decoder mem2mem mode
> > + * @dec_reg_base: JPEG registers mapping
> > + * @clk_jdec: JPEG hw working clock
> > + * @clk_jdec_smi: JPEG SMI bus clock
> > + * @larb: SMI device
> > + */
> > +struct mtk_jpeg_dev {
> > + struct mutex lock;
> > + spinlock_t hw_lock;
> > + struct workqueue_struct *workqueue;
> > + struct device *dev;
> > + struct v4l2_device v4l2_dev;
> > + struct v4l2_m2m_dev *m2m_dev;
> > + void *alloc_ctx;
> > + struct video_device *dec_vdev;
> > + void __iomem *dec_reg_base;
> > + struct clk *clk_jdec;
> > + struct clk *clk_jdec_smi;
> > + struct device *larb;
> > +};
> > +
> > +/**
> > + * struct jpeg_fmt - driver's internal color format data
> > + * @name: format descritpion
> > + * @fourcc: the fourcc code, 0 if not applicable
> > + * @h_sample: horizontal sample count of plane in 4 * 4 pixel image
> > + * @v_sample: vertical sample count of plane in 4 * 4 pixel image
> > + * @colplanes: number of color planes (1 for packed formats)
> > + * @h_align: horizontal alignment order (align to 2^h_align)
> > + * @v_align: vertical alignment order (align to 2^v_align)
> > + * @flags: flags describing format applicability
> > + */
> > +struct mtk_jpeg_fmt {
> > + char *name;
> > + u32 fourcc;
> > + int h_sample[VIDEO_MAX_PLANES];
> > + int v_sample[VIDEO_MAX_PLANES];
> > + int colplanes;
> > + int h_align;
> > + int v_align;
> > + u32 flags;
> > +};
> > +
> > +/**
> > + * mtk_jpeg_q_data - parameters of one queue
> > + * @fmt: driver-specific format of this queue
> > + * @w: image width
> > + * @h: image height
> > + * @bytesperline: distance in bytes between the leftmost pixels in two adjacent
> > + * lines
> > + * @sizeimage: image buffer size in bytes
> > + */
> > +struct mtk_jpeg_q_data {
> > + struct mtk_jpeg_fmt *fmt;
> > + u32 w;
> > + u32 h;
> > + u32 bytesperline[VIDEO_MAX_PLANES];
> > + u32 sizeimage[VIDEO_MAX_PLANES];
> > +};
> > +
> > +/**
> > + * mtk_jpeg_ctx - the device context data
> > + * @jpeg: JPEG IP device for this context
> > + * @out_q: source (output) queue information
> > + * @cap_q: destination (capture) queue queue information
> > + * @fh: V4L2 file handle
> > + * @dec_param parameters for HW decoding
> > + * @state: state of the context
> > + * @header_valid: set if header has been parsed and valid
> > + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat
> > + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
> > + * @quantization: enum v4l2_quantization, colorspace quantization
> > + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function
> > + */
> > +struct mtk_jpeg_ctx {
> > + struct mtk_jpeg_dev *jpeg;
> > + struct mtk_jpeg_q_data out_q;
> > + struct mtk_jpeg_q_data cap_q;
> > + struct v4l2_fh fh;
> > + enum mtk_jpeg_ctx_state state;
> > +
> > + enum v4l2_colorspace colorspace;
> > + enum v4l2_ycbcr_encoding ycbcr_enc;
> > + enum v4l2_quantization quantization;
> > + enum v4l2_xfer_func xfer_func;
> > +};
> > +
> > +#endif /* _MTK_JPEG_CORE_H */
> > diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > new file mode 100644
> > index 0000000..a6315f3
> > --- /dev/null
> > +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c
> > @@ -0,0 +1,417 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Ming Hsiu Tsai <minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + * Rick Chang <rick.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <media/videobuf2-core.h>
> > +
> > +#include "mtk_jpeg_hw.h"
> > +
> > +#define MTK_JPEG_DUNUM_MASK(val) (((val) - 1) & 0x3)
> > +
> > +enum mtk_jpeg_color {
> > + MTK_JPEG_COLOR_420 = 0x00221111,
> > + MTK_JPEG_COLOR_422 = 0x00211111,
> > + MTK_JPEG_COLOR_444 = 0x00111111,
> > + MTK_JPEG_COLOR_422V = 0x00121111,
> > + MTK_JPEG_COLOR_422X2 = 0x00412121,
> > + MTK_JPEG_COLOR_422VX2 = 0x00222121,
> > + MTK_JPEG_COLOR_400 = 0x00110000
> > +};
> > +
> > +static inline int mtk_jpeg_verify_align(u32 val, int align, u32 reg)
> > +{
> > + if (val & (align - 1)) {
> > + pr_err("mtk-jpeg: write reg %x without %d align\n", reg, align);
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_jpeg_decide_format(struct mtk_jpeg_dec_param *param)
> > +{
> > + param->src_color = (param->sampling_w[0] << 20) |
> > + (param->sampling_h[0] << 16) |
> > + (param->sampling_w[1] << 12) |
> > + (param->sampling_h[1] << 8) |
> > + (param->sampling_w[2] << 4) |
> > + (param->sampling_h[2]);
> > +
> > + param->uv_brz_w = 0;
> > + switch (param->src_color) {
> > + case MTK_JPEG_COLOR_444:
> > + param->uv_brz_w = 1;
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV422M;
> > + break;
> > + case MTK_JPEG_COLOR_422X2:
> > + case MTK_JPEG_COLOR_422:
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV422M;
> > + break;
> > + case MTK_JPEG_COLOR_422V:
> > + case MTK_JPEG_COLOR_422VX2:
> > + param->uv_brz_w = 1;
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV420M;
> > + break;
> > + case MTK_JPEG_COLOR_420:
> > + param->dst_fourcc = V4L2_PIX_FMT_YUV420M;
> > + break;
> > + case MTK_JPEG_COLOR_400:
> > + param->dst_fourcc = V4L2_PIX_FMT_GREY;
> > + break;
> > + default:
> > + param->dst_fourcc = 0;
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void mtk_jpeg_calc_mcu(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 factor_w, factor_h;
> > + u32 i, comp, blk;
> > +
> > + factor_w = 2 + param->sampling_w[0];
> > + factor_h = 2 + param->sampling_h[0];
> > + param->mcu_w = (param->pic_w + (1 << factor_w) - 1) >> factor_w;
> > + param->mcu_h = (param->pic_h + (1 << factor_h) - 1) >> factor_h;
> > + param->total_mcu = param->mcu_w * param->mcu_h;
> > + param->unit_num = ((param->pic_w + 7) >> 3) * ((param->pic_h + 7) >> 3);
> > + param->blk_num = 0;
> > + for (i = 0; i < MTK_JPEG_COMP_MAX; i++) {
> > + param->blk_comp[i] = 0;
> > + if (i >= param->comp_num)
> > + continue;
> > + param->blk_comp[i] = param->sampling_w[i] *
> > + param->sampling_h[i];
> > + param->blk_num += param->blk_comp[i];
> > + }
> > +
> > + param->membership = 0;
> > + for (i = 0, blk = 0, comp = 0; i < MTK_JPEG_BLOCK_MAX; i++) {
> > + if (i < param->blk_num && comp < param->comp_num) {
> > + u32 tmp;
> > +
> > + tmp = (0x04 + (comp & 0x3));
> > + param->membership |= tmp << (i * 3);
> > + if (++blk == param->blk_comp[comp]) {
> > + comp++;
> > + blk = 0;
> > + }
> > + } else {
> > + param->membership |= 7 << (i * 3);
> > + }
> > + }
> > +}
> > +
> > +static void mtk_jpeg_calc_dma_group(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 factor_mcu = 3;
> > +
> > + if (param->src_color == MTK_JPEG_COLOR_444 &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV422M)
> > + factor_mcu = 4;
> > + else if (param->src_color == MTK_JPEG_COLOR_422V &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV420M)
> > + factor_mcu = 4;
> > + else if (param->src_color == MTK_JPEG_COLOR_422X2 &&
> > + param->dst_fourcc == V4L2_PIX_FMT_YUV422M)
> > + factor_mcu = 2;
> > + else if (param->src_color == MTK_JPEG_COLOR_400 ||
> > + (param->src_color & 0x0FFFF) == 0)
> > + factor_mcu = 4;
> > +
> > + param->dma_mcu = 1 << factor_mcu;
> > + param->dma_group = param->mcu_w / param->dma_mcu;
> > + param->dma_last_mcu = param->mcu_w % param->dma_mcu;
> > + if (param->dma_last_mcu)
> > + param->dma_group++;
> > + else
> > + param->dma_last_mcu = param->dma_mcu;
> > +}
> > +
> > +static int mtk_jpeg_calc_dst_size(struct mtk_jpeg_dec_param *param)
> > +{
> > + u32 i, padding_w;
> > + u32 ds_row_h[3];
> > + u32 brz_w[3];
> > +
> > + brz_w[0] = 0;
> > + brz_w[1] = param->uv_brz_w;
> > + brz_w[2] = brz_w[1];
> > +
> > + for (i = 0; i < param->comp_num; i++) {
> > + if (brz_w[i] > 3)
> > + return -1;
> > +
> > + padding_w = param->mcu_w * MTK_JPEG_DCTSIZE *
> > + param->sampling_w[i];
> > + /* output format is 420/422 */
> > + param->comp_w[i] = padding_w >> brz_w[i];
> > + param->comp_w[i] = mtk_jpeg_align(param->comp_w[i],
> > + MTK_JPEG_DCTSIZE);
> > + param->img_stride[i] = i ? mtk_jpeg_align(param->comp_w[i], 16)
> > + : mtk_jpeg_align(param->comp_w[i], 32);
> > + ds_row_h[i] = (MTK_JPEG_DCTSIZE * param->sampling_h[i]);
> > + }
> > + param->dec_w = param->img_stride[0];
> > + param->dec_h = ds_row_h[0] * param->mcu_h;
> > +
> > + for (i = 0; i < MTK_JPEG_COMP_MAX; i++) {
> > + /* They must be equal in frame mode. */
> > + param->mem_stride[i] = param->img_stride[i];
> > + param->comp_size[i] = param->mem_stride[i] * ds_row_h[i] *
> > + param->mcu_h;
> > + }
> > +
> > + param->y_size = param->comp_size[0];
> > + param->uv_size = param->comp_size[1];
> > + param->dec_size = param->y_size + (param->uv_size << 1);
> > +
> > + return 0;
> > +}
> > +
> > +int mtk_jpeg_dec_fill_param(struct mtk_jpeg_dec_param *param)
> > +{
> > + if (mtk_jpeg_decide_format(param))
> > + return -1;
> > +
> > + mtk_jpeg_calc_mcu(param);
> > + mtk_jpeg_calc_dma_group(param);
> > + if (mtk_jpeg_calc_dst_size(param))
> > + return -2;
> > +
> > + return 0;
> > +}
> > +
> > +u32 mtk_jpeg_dec_get_int_status(void __iomem *base)
> > +{
> > + u32 ret;
> > +
> > + ret = readl(base + JPGDEC_REG_INTERRUPT_STATUS) & BIT_INQST_MASK_ALLIRQ;
> > + if (ret)
> > + writel(ret, base + JPGDEC_REG_INTERRUPT_STATUS);
> > +
> > + return ret;
> > +}
> > +
> > +u32 mtk_jpeg_dec_enum_result(u32 irq_result)
> > +{
> > + if (irq_result & BIT_INQST_MASK_EOF)
> > + return MTK_JPEG_DEC_RESULT_EOF_DONE;
> > + else if (irq_result & BIT_INQST_MASK_PAUSE)
> > + return MTK_JPEG_DEC_RESULT_PAUSE;
> > + else if (irq_result & BIT_INQST_MASK_UNDERFLOW)
> > + return MTK_JPEG_DEC_RESULT_UNDERFLOW;
> > + else if (irq_result & BIT_INQST_MASK_OVERFLOW)
> > + return MTK_JPEG_DEC_RESULT_OVERFLOW;
> > + else if (irq_result & BIT_INQST_MASK_ERROR_BS)
> > + return MTK_JPEG_DEC_RESULT_ERROR_BS;
>
> No need for 'else' here since the previous 'if' always returns if true.
Ok.
> > +
> > + return MTK_JPEG_DEC_RESULT_ERROR_UNKNOWN;
> > +}
> > +
> > +void mtk_jpeg_dec_start(void __iomem *base)
> > +{
> > + writel(0, base + JPGDEC_REG_TRIG);
> > +}
> > +
> > +static void mtk_jpeg_dec_soft_reset(void __iomem *base)
> > +{
> > + writel(0x0000FFFF, base + JPGDEC_REG_INTERRUPT_STATUS);
> > + writel(0x00, base + JPGDEC_REG_RESET);
> > + writel(0x01, base + JPGDEC_REG_RESET);
> > +}
> > +
> > +static void mtk_jpeg_dec_hard_reset(void __iomem *base)
> > +{
> > + writel(0x00, base + JPGDEC_REG_RESET);
> > + writel(0x10, base + JPGDEC_REG_RESET);
> > +}
> > +
> > +void mtk_jpeg_dec_reset(void __iomem *base)
> > +{
> > + mtk_jpeg_dec_soft_reset(base);
> > + mtk_jpeg_dec_hard_reset(base);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_brz_factor(void __iomem *base, u8 yscale_w,
> > + u8 yscale_h, u8 uvscale_w, u8 uvscale_h)
> > +{
> > + u32 val;
> > +
> > + val = (uvscale_h << 12) | (uvscale_w << 8) |
> > + (yscale_h << 4) | yscale_w;
> > + writel(val, base + JPGDEC_REG_BRZ_FACTOR);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, u32 addr_y,
> > + u32 addr_u, u32 addr_v)
> > +{
> > + mtk_jpeg_verify_align(addr_y, 16, JPGDEC_REG_DEST_ADDR0_Y);
> > + writel(addr_y, base + JPGDEC_REG_DEST_ADDR0_Y);
> > + mtk_jpeg_verify_align(addr_u, 16, JPGDEC_REG_DEST_ADDR0_U);
> > + writel(addr_u, base + JPGDEC_REG_DEST_ADDR0_U);
> > + mtk_jpeg_verify_align(addr_v, 16, JPGDEC_REG_DEST_ADDR0_V);
> > + writel(addr_v, base + JPGDEC_REG_DEST_ADDR0_V);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, u32 addr_y,
> > + u32 addr_u, u32 addr_v)
> > +{
> > + writel(addr_y, base + JPGDEC_REG_DEST_ADDR1_Y);
> > + writel(addr_u, base + JPGDEC_REG_DEST_ADDR1_U);
> > + writel(addr_v, base + JPGDEC_REG_DEST_ADDR1_V);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_mem_stride(void __iomem *base, u32 stride_y,
> > + u32 stride_uv)
> > +{
> > + writel((stride_y & 0xFFFF), base + JPGDEC_REG_STRIDE_Y);
> > + writel((stride_uv & 0xFFFF), base + JPGDEC_REG_STRIDE_UV);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_img_stride(void __iomem *base, u32 stride_y,
> > + u32 stride_uv)
> > +{
> > + writel((stride_y & 0xFFFF), base + JPGDEC_REG_IMG_STRIDE_Y);
> > + writel((stride_uv & 0xFFFF), base + JPGDEC_REG_IMG_STRIDE_UV);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_pause_mcu_idx(void __iomem *base, u32 idx)
> > +{
> > + writel(idx & 0x0003FFFFFF, base + JPGDEC_REG_PAUSE_MCU_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dec_mode(void __iomem *base, u32 mode)
> > +{
> > + writel(mode & 0x03, base + JPGDEC_REG_OPERATION_MODE);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, u32 ptr)
> > +{
> > + mtk_jpeg_verify_align(ptr, 16, JPGDEC_REG_FILE_BRP);
> > + writel(ptr, base + JPGDEC_REG_FILE_BRP);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_bs_info(void __iomem *base, u32 addr, u32 size)
> > +{
> > + mtk_jpeg_verify_align(addr, 16, JPGDEC_REG_FILE_ADDR);
> > + mtk_jpeg_verify_align(size, 128, JPGDEC_REG_FILE_TOTAL_SIZE);
> > + writel(addr, base + JPGDEC_REG_FILE_ADDR);
> > + writel(size, base + JPGDEC_REG_FILE_TOTAL_SIZE);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_comp_id(void __iomem *base, u32 id_y, u32 id_u,
> > + u32 id_v)
> > +{
> > + u32 val;
> > +
> > + val = ((id_y & 0x00FF) << 24) | ((id_u & 0x00FF) << 16) |
> > + ((id_v & 0x00FF) << 8);
> > + writel(val, base + JPGDEC_REG_COMP_ID);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_total_mcu(void __iomem *base, u32 num)
> > +{
> > + writel(num - 1, base + JPGDEC_REG_TOTAL_MCU_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_comp0_du(void __iomem *base, u32 num)
> > +{
> > + writel(num - 1, base + JPGDEC_REG_COMP0_DATA_UNIT_NUM);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_du_membership(void __iomem *base, u32 member,
> > + u32 gmc, u32 isgray)
> > +{
> > + if (isgray)
> > + member = 0x3FFFFFFC;
> > + member |= (isgray << 31) | (gmc << 30);
> > + writel(member, base + JPGDEC_REG_DU_CTRL);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_q_table(void __iomem *base, u32 id0, u32 id1,
> > + u32 id2)
> > +{
> > + u32 val;
> > +
> > + val = ((id0 & 0x0f) << 8) | ((id1 & 0x0f) << 4) | ((id2 & 0x0f) << 0);
> > + writel(val, base + JPGDEC_REG_QT_ID);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_dma_group(void __iomem *base, u32 mcu_group,
> > + u32 group_num, u32 last_mcu)
> > +{
> > + u32 val;
> > +
> > + val = (((mcu_group - 1) & 0x00FF) << 16) |
> > + (((group_num - 1) & 0x007F) << 8) |
> > + ((last_mcu - 1) & 0x00FF);
> > + writel(val, base + JPGDEC_REG_WDMA_CTRL);
> > +}
> > +
> > +static void mtk_jpeg_dec_set_sampling_factor(void __iomem *base, u32 comp_num,
> > + u32 y_w, u32 y_h, u32 u_w,
> > + u32 u_h, u32 v_w, u32 v_h)
> > +{
> > + u32 val;
> > + u32 y_wh = (MTK_JPEG_DUNUM_MASK(y_w) << 2) | MTK_JPEG_DUNUM_MASK(y_h);
> > + u32 u_wh = (MTK_JPEG_DUNUM_MASK(u_w) << 2) | MTK_JPEG_DUNUM_MASK(u_h);
> > + u32 v_wh = (MTK_JPEG_DUNUM_MASK(v_w) << 2) | MTK_JPEG_DUNUM_MASK(v_h);
> > +
> > + if (comp_num == 1)
> > + val = 0;
> > + else
> > + val = (y_wh << 8) | (u_wh << 4) | v_wh;
> > + writel(val, base + JPGDEC_REG_DU_NUM);
> > +}
> > +
> > +void mtk_jpeg_dec_set_config(void __iomem *base,
> > + struct mtk_jpeg_dec_param *config,
> > + struct mtk_jpeg_bs *bs,
> > + struct mtk_jpeg_fb *fb)
> > +{
> > + mtk_jpeg_dec_set_brz_factor(base, 0, 0, config->uv_brz_w, 0);
> > + mtk_jpeg_dec_set_dec_mode(base, 0);
> > + mtk_jpeg_dec_set_comp0_du(base, config->unit_num);
> > + mtk_jpeg_dec_set_total_mcu(base, config->total_mcu);
> > + mtk_jpeg_dec_set_bs_info(base, bs->str_addr, bs->size);
> > + mtk_jpeg_dec_set_bs_write_ptr(base, bs->end_addr);
> > + mtk_jpeg_dec_set_du_membership(base, config->membership, 1,
> > + (config->comp_num == 1) ? 1 : 0);
> > + mtk_jpeg_dec_set_comp_id(base, config->comp_id[0], config->comp_id[1],
> > + config->comp_id[2]);
> > + mtk_jpeg_dec_set_q_table(base, config->qtbl_num[0],
> > + config->qtbl_num[1], config->qtbl_num[2]);
> > + mtk_jpeg_dec_set_sampling_factor(base, config->comp_num,
> > + config->sampling_w[0],
> > + config->sampling_h[0],
> > + config->sampling_w[1],
> > + config->sampling_h[1],
> > + config->sampling_w[2],
> > + config->sampling_h[2]);
> > + mtk_jpeg_dec_set_mem_stride(base, config->mem_stride[0],
> > + config->mem_stride[1]);
> > + mtk_jpeg_dec_set_img_stride(base, config->img_stride[0],
> > + config->img_stride[1]);
> > + mtk_jpeg_dec_set_dst_bank0(base, fb->plane_addr[0],
> > + fb->plane_addr[1], fb->plane_addr[2]);
> > + mtk_jpeg_dec_set_dst_bank1(base, 0, 0, 0);
> > + mtk_jpeg_dec_set_dma_group(base, config->dma_mcu, config->dma_group,
> > + config->dma_last_mcu);
> > + mtk_jpeg_dec_set_pause_mcu_idx(base, config->total_mcu);
> > +}
>
> Regards,
>
> Hans
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
From: Anurup M @ 2016-11-15 10:15 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
anurup.m-hv44wF8Li93QT0dZR+AlfA,
zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
john.garry-hv44wF8Li93QT0dZR+AlfA,
gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
shiju.jose-hv44wF8Li93QT0dZR+AlfA,
wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
guohanjun-hv44wF8Li93QT0dZR+AlfA, shyju.pv-hv44wF8Li93QT0dZR+AlfA,
linuxarm-hv44wF8Li93QT0dZR+AlfA
In-Reply-To: <20161110175510.GB10137@leverpostej>
On Thursday 10 November 2016 11:25 PM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>>
>> The Hisilicon Djtag is an independent component which connects
>> with some other components in the SoC by Debug Bus. This driver
>> can be configured to access the registers of connecting components
>> (like L3 cache) during real time debugging.
>>
> Just to check, is this likely to be used in multi-socket hardware, and
> if so, are instances always-on?
Yes, It could be used in multi-socket hardware also.
The sockets are always enabled after bootup. Sorry I didn't get the
>> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> ---
>> drivers/soc/Kconfig | 1 +
>> drivers/soc/Makefile | 1 +
>> drivers/soc/hisilicon/Kconfig | 12 +
>> drivers/soc/hisilicon/Makefile | 1 +
>> drivers/soc/hisilicon/djtag.c | 639 ++++++++++++++++++++++++++++++++++++
>> include/linux/soc/hisilicon/djtag.h | 38 +++
>> 6 files changed, 692 insertions(+)
>> create mode 100644 drivers/soc/hisilicon/Kconfig
>> create mode 100644 drivers/soc/hisilicon/Makefile
>> create mode 100644 drivers/soc/hisilicon/djtag.c
>> create mode 100644 include/linux/soc/hisilicon/djtag.h
> Other than the PMU driver(s), what is going to use this?
>
> If you don't have something in particular, please also place this under
> drivers/perf/hisilicon, along with the PMU driver(s).
>
> We can always move it later if necessary.
>
> [...]
Arnd also suggested the same. Currently as there are no other users I
shall move it to
drivers/perf/hisilicon.
>> +#define SC_DJTAG_TIMEOUT 100000 /* 100ms */
> This would be better as:
>
> #define SC_DJTAG_TIMEOUT_US (100 * USEC_PER_MSEC)
>
> (you'll need to include <linux/time64.h>)
>
> [...]
OK.
>> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
>> +{
>> + void __iomem *reg_addr = regs_base + off;
>> +
>> + *value = readl_relaxed(reg_addr);
>> +}
>> +
>> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
>> +{
>> + void __iomem *reg_addr = regs_base + off;
>> +
>> + writel(val, reg_addr);
>> +}
> I think these make the driver harder to read, especially given the read
> function is void and takes an output pointer.
>
> In either case you can call readl/writel directly with base + off for
> the __iomem ptr. Please do that.
Ok.
>> +
>> +/*
>> + * djtag_readwrite_v1/v2: djtag read/write interface
>> + * @reg_base: djtag register base address
>> + * @offset: register's offset
>> + * @mod_sel: module selection
>> + * @mod_mask: mask to select specific modules for write
>> + * @is_w: write -> true, read -> false
>> + * @wval: value to register for write
>> + * @chain_id: which sub module for read
>> + * @rval: value in register for read
>> + *
>> + * Return non-zero if error, else return 0.
>> + */
>> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
>> + u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
>> +{
>> + u32 rd;
>> + int timeout = SC_DJTAG_TIMEOUT;
>> +
>> + if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
>> + pr_warn("djtag: do nothing.\n");
>> + return 0;
>> + }
>> +
>> + /* djtag mster enable & accelerate R,W */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
>> + DJTAG_NOR_CFG | DJTAG_MSTR_EN);
>> +
>> + /* select module */
>> + djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
>> + djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
>> + mod_mask & CHAIN_UNIT_CFG_EN);
>> +
>> + if (is_w) {
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
>> + } else
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
>> +
>> + /* address offset */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
>> +
>> + /* start to write to djtag register */
>> + djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
>> +
>> + /* ensure the djtag operation is done */
>> + do {
>> + djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
>> + if (!(rd & DJTAG_MSTR_EN))
>> + break;
>> +
>> + udelay(1);
>> + } while (timeout--);
>> +
>> + if (timeout < 0) {
>> + pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
>> + return -EBUSY;
>> + }
>> +
>> + if (!is_w)
>> + djtag_read32_relaxed(regs_base,
>> + SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
>> +
>> + return 0;
>> +}
> Please factor out the common bits into helpers and have separate
> read/write functions. It's incredibly difficult to follow the code with
> read/write hidden behind a boolean parameter.
Ok. Shall change it.
>> +static const struct of_device_id djtag_of_match[] = {
>> + /* for hip05(D02) cpu die */
>> + { .compatible = "hisilicon,hip05-cpu-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip05(D02) io die */
>> + { .compatible = "hisilicon,hip05-io-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip06(D03) cpu die */
>> + { .compatible = "hisilicon,hip06-cpu-djtag-v1",
>> + .data = (void *)djtag_readwrite_v1 },
>> + /* for hip06(D03) io die */
>> + { .compatible = "hisilicon,hip06-io-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + /* for hip07(D05) cpu die */
>> + { .compatible = "hisilicon,hip07-cpu-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + /* for hip07(D05) io die */
>> + { .compatible = "hisilicon,hip07-io-djtag-v2",
>> + .data = (void *)djtag_readwrite_v2 },
>> + {},
>> +};
> Binding documentation for all of these should be added *before* this
> patch, per Documentation/devicetree/bindings/submitting-patches.txt.
> Please move any relevant binding documentation earlier in the series.
The binding documentation added in "[RESEND PATCH v1 02/11] dt-bindings:
hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings]".
>> +MODULE_DEVICE_TABLE(of, djtag_of_match);
>> +
>> +static ssize_t
>> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
>> +{
>> + struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
>> +
>> + return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
>> +}
>> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
>> +
>> +static struct attribute *hisi_djtag_dev_attrs[] = {
>> + NULL,
>> + /* modalias helps coldplug: modprobe $(cat .../modalias) */
>> + &dev_attr_modalias.attr,
>> + NULL
>> +};
>> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
> Why do we need to expose this under sysfs?
This is to know the djtag clients registered with the bus.
>> +struct bus_type hisi_djtag_bus = {
>> + .name = "hisi-djtag",
>> + .match = hisi_djtag_device_match,
>> + .probe = hisi_djtag_device_probe,
>> + .remove = hisi_djtag_device_remove,
>> +};
> I take it the core bus code handles reference-counting users of the bus?
The bus_register registers with kobject. Did It answer the question?
>> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
>> + struct device_node *node)
>> +{
>> + struct hisi_djtag_client *client;
>> + int status;
>> +
>> + client = kzalloc(sizeof(*client), GFP_KERNEL);
>> + if (!client)
>> + return NULL;
>> +
>> + client->host = host;
>> +
>> + client->dev.parent = &client->host->dev;
>> + client->dev.bus = &hisi_djtag_bus;
>> + client->dev.type = &hisi_djtag_client_type;
>> + client->dev.of_node = node;
> I suspect that we should do:
>
> client->dev.of_node = of_node_get(node);
>
> ... so that it's guaranteed we retain a reference.
Ok.
>> + snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
>> + DJTAG_PREFIX, node->name);
>> + dev_set_name(&client->dev, "%s", client->name);
>> +
>> + status = device_register(&client->dev);
>> + if (status < 0) {
>> + pr_err("error adding new device, status=%d\n", status);
>> + kfree(client);
>> + return NULL;
>> + }
>> +
>> + return client;
>> +}
>> +
>> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
>> + struct hisi_djtag_host *host,
>> + struct device_node *node)
>> +{
>> + struct hisi_djtag_client *client;
>> +
>> + client = hisi_djtag_new_device(host, node);
>> + if (client == NULL) {
>> + dev_err(&host->dev, "error registering device %s\n",
>> + node->full_name);
>> + of_node_put(node);
> I don't think this should be here, given what djtag_register_devices()
> does.
Will move this to djtag_register_devices.
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + return client;
>> +}
>> +
>> +static void djtag_register_devices(struct hisi_djtag_host *host)
>> +{
>> + struct device_node *node;
>> + struct hisi_djtag_client *client;
>> +
>> + if (!host->of_node)
>> + return;
>> +
>> + for_each_available_child_of_node(host->of_node, node) {
>> + if (of_node_test_and_set_flag(node, OF_POPULATED))
>> + continue;
>> + client = hisi_djtag_of_register_device(host, node);
>> + list_add(&client->next, &host->client_list);
>> + }
>> +}
> Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
> list_add is not safe.
Shall add the check and handle accordingly.
>> +static int djtag_host_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct hisi_djtag_host *host;
>> + const struct of_device_id *of_id;
>> + struct resource *res;
>> + int rc;
>> +
>> + of_id = of_match_device(djtag_of_match, dev);
>> + if (!of_id)
>> + return -EINVAL;
>> +
>> + host = kzalloc(sizeof(*host), GFP_KERNEL);
>> + if (!host)
>> + return -ENOMEM;
>> +
>> + host->of_node = dev->of_node;
> host->of_node = of_node_get(dev->of_node);
>
>> + host->djtag_readwrite = of_id->data;
>> + spin_lock_init(&host->lock);
>> +
>> + INIT_LIST_HEAD(&host->client_list);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res) {
>> + dev_err(&pdev->dev, "No reg resorces!\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
>> +
>> + if (!resource_size(res)) {
>> + dev_err(&pdev->dev, "Zero reg entry!\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
>> +
>> + host->sysctl_reg_map = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(host->sysctl_reg_map)) {
>> + dev_warn(dev, "Unable to map sysctl registers.\n");
>> + kfree(host);
>> + return -EINVAL;
>> + }
> Please have a common error path rather than duplicating this free.
>
> e.g. at the end of the function have:
>
> err_free:
> kfree(host);
> return err;
>
> ... and in cases like this, have:
>
> if (whatever()) {
> dev_warn(dev, "this failed xxx\n");
> err = -EINVAL;
> goto err_free;
> }
Ok. thanks. will change it.
Thanks,
Anurup
> Thanks,
> Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] ARM: dts: sun5i: Add touchscreen node to reference-design-tablet.dtsi
From: Hans de Goede @ 2016-11-15 10:12 UTC (permalink / raw)
To: Maxime Ripard; +Cc: devicetree, Chen-Yu Tsai, linux-arm-kernel
In-Reply-To: <20161114200802.vdduhyxnkgmpsyd5@lukather>
Hi,
On 14-11-16 21:08, Maxime Ripard wrote:
> Hi,
>
> On Sun, Nov 13, 2016 at 08:22:02PM +0100, Hans de Goede wrote:
>> Just like on sun8i all sun5i tablets use the same interrupt and power
>> gpios for their touchscreens. I've checked all known a13 fex files and
>> only the UTOO P66 uses a different gpio for the interrupt.
>>
>> Add a touchscreen node to sun5i-reference-design-tablet.dtsi, which
>> fills in the necessary gpios to avoid duplication in the tablet dts files,
>> just like we do in sun8i-reference-design-tablet.dtsi.
>>
>> This will make future patches adding touchscreen nodes to a13 tablets
>> simpler.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> arch/arm/boot/dts/sun5i-a13-utoo-p66.dts | 38 ++++++++--------------
>> .../boot/dts/sun5i-reference-design-tablet.dtsi | 25 ++++++++++++++
>> 2 files changed, 39 insertions(+), 24 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> index a8b0bcc..3d7ff10 100644
>> --- a/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> +++ b/arch/arm/boot/dts/sun5i-a13-utoo-p66.dts
>> @@ -83,22 +83,6 @@
>> allwinner,pins = "PG3";
>> };
>>
>> -&i2c1 {
>> - icn8318: touchscreen@40 {
>> - compatible = "chipone,icn8318";
>> - reg = <0x40>;
>> - interrupt-parent = <&pio>;
>> - interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
>> - pinctrl-names = "default";
>> - pinctrl-0 = <&ts_wake_pin_p66>;
>> - wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> - touchscreen-size-x = <800>;
>> - touchscreen-size-y = <480>;
>> - touchscreen-inverted-x;
>> - touchscreen-swapped-x-y;
>> - };
>> -};
>> -
>> &mmc2 {
>> pinctrl-names = "default";
>> pinctrl-0 = <&mmc2_pins_a>;
>> @@ -121,20 +105,26 @@
>> allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
>> };
>> -
>> - ts_wake_pin_p66: ts_wake_pin@0 {
>> - allwinner,pins = "PB3";
>> - allwinner,function = "gpio_out";
>> - allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> - allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> - };
>> -
>> };
>>
>> ®_usb0_vbus {
>> gpio = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */
>> };
>>
>> +&touchscreen {
>> + compatible = "chipone,icn8318";
>> + reg = <0x40>;
>> + /* The P66 uses a different EINT then the reference design */
>> + interrupts = <6 9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
>> + /* The icn8318 binding expects wake-gpios instead of power-gpios */
>> + wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> + touchscreen-size-x = <800>;
>> + touchscreen-size-y = <480>;
>> + touchscreen-inverted-x;
>> + touchscreen-swapped-x-y;
>> + status = "okay";
>> +};
>> +
>> &uart1 {
>> /* The P66 uses the uart pins as gpios */
>> status = "disabled";
>> diff --git a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> index 20cc940..7af488a 100644
>> --- a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> +++ b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi
>> @@ -41,6 +41,7 @@
>> */
>> #include "sunxi-reference-design-tablet.dtsi"
>>
>> +#include <dt-bindings/interrupt-controller/irq.h>
>> #include <dt-bindings/pwm/pwm.h>
>>
>> / {
>> @@ -84,6 +85,23 @@
>> };
>>
>> &i2c1 {
>> + /*
>> + * The gsl1680 is rated at 400KHz and it will not work reliable at
>> + * 100KHz, this has been confirmed on multiple different q8 tablets.
>> + * All other devices on this bus are also rated for 400KHz.
>> + */
>> + clock-frequency = <400000>;
>> +
>> + touchscreen: touchscreen {
>> + interrupt-parent = <&pio>;
>> + interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>; /* EINT11 (PG11) */
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&ts_power_pin>;
>> + power-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
>> + /* Tablet dts must provide reg and compatible */
>> + status = "disabled";
>> + };
>> +
>> pcf8563: rtc@51 {
>> compatible = "nxp,pcf8563";
>> reg = <0x51>;
>> @@ -125,6 +143,13 @@
>> allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
>> };
>>
>> + ts_power_pin: ts_power_pin {
>> + allwinner,pins = "PB3";
>> + allwinner,function = "gpio_out";
>> + allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> + allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> + };
>> +
>
> For the next release, we'll switch to the generic pin mux properties
> ("pins" and "function"), and we actually implemented the fact that the
> drive and pull properties are optional, so you can drop them both.
>
> You'll need next + http://lists.infradead.org/pipermail/linux-arm-kernel/2016-November/467123.html
Ok, before I send a v2 first a question about this, for the touchscreen
case I actually need:
allwinner,drive = <SUN4I_PINCTRL_10_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
Because otherwise when the touchscreen controller is powered by a separate
regulator and that regulator is off, then it may draw just enough current
from its enable pin to be sort-of listening to the i2c bus and mess up
that bus.
So is this the default, or do we get the power-on default when not
specifying these? If it is the power-on default then we do need to
specify these, because AFAICT the power-on drive strength typically
is 20 mA.
Regards,
Hans
^ permalink raw reply
* [PATCHv2 10/10] Documentation: synopsys-dw-mshc: remove the unused properties
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
"support-highspeed" was the obsoleted property.
And "broken-cd" is not synopsys specific property.
It can be referred to mmc.txt binding Documentation.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 -----
1 file changed, 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 1279a22..7fd17c3 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -75,11 +75,6 @@ Optional properties:
* card-detect-delay: Delay in milli-seconds before detecting card after card
insert event. The default value is 0.
-* supports-highspeed (DEPRECATED): Enables support for high speed cards (up to 50MHz)
- (use "cap-mmc-highspeed" or "cap-sd-highspeed" instead)
-
-* broken-cd: as documented in mmc core bindings.
-
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
specified we'll defer probe until we can find this regulator.
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 09/10] mmc: dw_mmc: The "clock-freq-min-max" property was deprecated
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
The "clock-freq-min-max" property was deprecated.
There is "max-frequency" property in drivers/mmc/core/host.c
"max-frequency" can be replaced with "clock-freq-min-max".
Minimum clock value might be set to 100K by default.
Then MMC core should try to find the correct value from 400K to 100K.
So it just needs to set Maximum clock value.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 3 ++-
drivers/mmc/host/dw_mmc.c | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index bfa461a..1279a22 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -59,8 +59,9 @@ Optional properties:
is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time.
-* clock-freq-min-max: Minimum and Maximum clock frequency for card output
+* clock-freq-min-max (DEPRECATED): Minimum and Maximum clock frequency for card output
clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
+ (Use the "max-frequency" instead of "clock-freq-min-max".)
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index ec0ba79..48e968a 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2608,6 +2608,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->f_min = DW_MCI_FREQ_MIN;
mmc->f_max = DW_MCI_FREQ_MAX;
} else {
+ dev_info(host->dev,
+ "'clock-freq-min-max' property was deprecated.\n");
mmc->f_min = freq[0];
mmc->f_max = freq[1];
}
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 08/10] mmc: dw_mmc: remove the unnecessary mmc_data structure
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Remove the unnecessary mmc_data structure.
Instead, cmd->data can be used.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Tested-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
drivers/mmc/host/dw_mmc.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index e912de6..ec0ba79 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -236,7 +236,6 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{
- struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
u32 cmdr;
@@ -291,10 +290,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
if (cmd->flags & MMC_RSP_CRC)
cmdr |= SDMMC_CMD_RESP_CRC;
- data = cmd->data;
- if (data) {
+ if (cmd->data) {
cmdr |= SDMMC_CMD_DAT_EXP;
- if (data->flags & MMC_DATA_WRITE)
+ if (cmd->data->flags & MMC_DATA_WRITE)
cmdr |= SDMMC_CMD_DAT_WR;
}
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 07/10] mmc: dw_mmc: use the cookie's enum values for post/pre_req()
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung@samsung.com>
This patch removed the meaningless value. Instead, use the cookie's enum
values for executing correctly.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/host/dw_mmc.c | 39 +++++++++++++++++++--------------------
1 file changed, 19 insertions(+), 20 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 12e1107..e912de6 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -414,12 +414,13 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
{
struct mmc_data *data = host->data;
- if (data)
- if (!data->host_cookie)
- dma_unmap_sg(host->dev,
- data->sg,
- data->sg_len,
- dw_mci_get_dma_dir(data));
+ if (data && data->host_cookie == COOKIE_MAPPED) {
+ dma_unmap_sg(host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data));
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
}
static void dw_mci_idmac_reset(struct dw_mci *host)
@@ -850,13 +851,13 @@ static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
static int dw_mci_pre_dma_transfer(struct dw_mci *host,
struct mmc_data *data,
- bool next)
+ int cookie)
{
struct scatterlist *sg;
unsigned int i, sg_len;
- if (!next && data->host_cookie)
- return data->host_cookie;
+ if (data->host_cookie == COOKIE_PRE_MAPPED)
+ return data->sg_len;
/*
* We don't do DMA on "complex" transfers, i.e. with
@@ -881,8 +882,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
if (sg_len == 0)
return -EINVAL;
- if (next)
- data->host_cookie = sg_len;
+ data->host_cookie = cookie;
return sg_len;
}
@@ -897,13 +897,12 @@ static void dw_mci_pre_req(struct mmc_host *mmc,
if (!slot->host->use_dma || !data)
return;
- if (data->host_cookie) {
- data->host_cookie = 0;
- return;
- }
+ /* This data might be unmapped at this time */
+ data->host_cookie = COOKIE_UNMAPPED;
- if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0)
- data->host_cookie = 0;
+ if (dw_mci_pre_dma_transfer(slot->host, mrq->data,
+ COOKIE_PRE_MAPPED) < 0)
+ data->host_cookie = COOKIE_UNMAPPED;
}
static void dw_mci_post_req(struct mmc_host *mmc,
@@ -916,12 +915,12 @@ static void dw_mci_post_req(struct mmc_host *mmc,
if (!slot->host->use_dma || !data)
return;
- if (data->host_cookie)
+ if (data->host_cookie != COOKIE_UNMAPPED)
dma_unmap_sg(slot->host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
- data->host_cookie = 0;
+ data->host_cookie = COOKIE_UNMAPPED;
}
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
@@ -1027,7 +1026,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
if (!host->use_dma)
return -ENODEV;
- sg_len = dw_mci_pre_dma_transfer(host, data, 0);
+ sg_len = dw_mci_pre_dma_transfer(host, data, COOKIE_MAPPED);
if (sg_len < 0) {
host->dma_ops->stop(host);
return sg_len;
--
2.10.1
^ permalink raw reply related
* [PATCHv2 06/10] mmc: core: move the cookie's enum values from sdhci.h to mmc.h
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
It's not for only sdhci controller.
So it can be moved from sdhci.h to mmc.h. And renamed from sdhci_cookie
to mmc_cookie.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Tested-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
drivers/mmc/host/sdhci.h | 6 ------
include/linux/mmc/core.h | 6 ++++++
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 766df17..325663b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -321,12 +321,6 @@ struct sdhci_adma2_64_desc {
/* Allow for a a command request and a data request at the same time */
#define SDHCI_MAX_MRQS 2
-enum sdhci_cookie {
- COOKIE_UNMAPPED,
- COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
- COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */
-};
-
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 0ce928b..82d707f 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -118,6 +118,12 @@ struct mmc_command {
struct mmc_request *mrq; /* associated request */
};
+enum mmc_cookie {
+ COOKIE_UNMAPPED,
+ COOKIE_PRE_MAPPED, /* mapped by pre_req() of controller */
+ COOKIE_MAPPED, /* mapped by prepare_data() of controller */
+};
+
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */
unsigned int timeout_clks; /* data timeout (in clocks) */
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 05/10] mmc: dw_mmc: call the dw_mci_prep_stop_abort() by default
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung@samsung.com>
stop_cmdr should be set to values relevant to stop command.
It migth be assigned to values whatever there is mrq->stop or not.
Then it doesn't need to use dw_mci_prepare_command().
It's enough to use the prep_stop_abort for preparing stop command.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/host/dw_mmc.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 3cda68c..12e1107 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -385,7 +385,7 @@ static void dw_mci_start_command(struct dw_mci *host,
static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
{
- struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
+ struct mmc_command *stop = &host->stop_abort;
dw_mci_start_command(host, stop, host->stop_cmdr);
}
@@ -1277,10 +1277,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
- if (mrq->stop)
- host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
- else
- host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
+ host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
}
static void dw_mci_start_request(struct dw_mci *host,
@@ -1890,8 +1887,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- if (data->stop ||
- !(host->data_status & (SDMMC_INT_DRTO |
+ if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
@@ -1927,8 +1923,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- if (data->stop ||
- !(host->data_status & (SDMMC_INT_DRTO |
+ if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
@@ -2004,7 +1999,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->cmd = NULL;
host->data = NULL;
- if (mrq->stop)
+ if (!mrq->sbc && mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
--
2.10.1
^ permalink raw reply related
* [PATCHv2 04/10] mmc: dw_mmc: use the hold register when send stop command
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung@samsung.com>
If DW_MMC_CARD_NO_USE_HOLD isn't set, it's usesd by default.
Enve if SDMMC_CMD_USB_HOLD_REG is set in prepare_command(), but it
doesn't set in pre_stop_abort().
To maintain the consistency, add the checking condition for this.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/host/dw_mmc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index be36f48..3cda68c 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -337,6 +337,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
cmdr = stop->opcode | SDMMC_CMD_STOP |
SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
+ if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags))
+ cmdr |= SDMMC_CMD_USE_HOLD_REG;
+
return cmdr;
}
--
2.10.1
^ permalink raw reply related
* [PATCHv2 03/10] mmc: dw_mmc: change the DW_MCI_FREQ_MIN from 400K to 100K
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung@samsung.com>
If there is no property "clock-freq-min-max", mmc->f_min should be set
to 400K by default. But Some SoC can be used 100K.
When 100K is used, MMC core will try to check from 400K to 100K.
Reported-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/host/dw_mmc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 6c0c4c5..be36f48 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -54,7 +54,7 @@
#define DW_MCI_DMA_THRESHOLD 16
#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */
-#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */
+#define DW_MCI_FREQ_MIN 100000 /* unit: HZ */
#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
--
2.10.1
^ permalink raw reply related
* [PATCHv2 02/10] mmc: dw_mmc: fix the debug message for checking card's present
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung@samsung.com>
If display the debug message, this message should be spamming.
If flags is maintained the previous value, didn't display the debug
message.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/mmc/host/dw_mmc.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index e53899e..6c0c4c5 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1537,13 +1537,10 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
== 0 ? 1 : 0;
spin_lock_bh(&host->lock);
- if (present) {
- set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
dev_dbg(&mmc->class_dev, "card is present\n");
- } else {
- clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ else if (!test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
dev_dbg(&mmc->class_dev, "card is not present\n");
- }
spin_unlock_bh(&host->lock);
return present;
--
2.10.1
^ permalink raw reply related
* Re: [PATCH 2/2] ARM: davinci_all_defconfig: enable the mstpri and ddrctl drivers
From: Sekhar Nori @ 2016-11-15 10:12 UTC (permalink / raw)
To: Bartosz Golaszewski, Kevin Hilman, Michael Turquette, Rob Herring,
Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
Tomi Valkeinen, David Airlie, Laurent Pinchart
In-Reply-To: <1479144724-14231-3-git-send-email-bgolaszewski@baylibre.com>
On Monday 14 November 2016 11:02 PM, Bartosz Golaszewski wrote:
> With the da8xx memory controller and master peripheral priority
> drivers merged and corresponding device tree changes in place we can
> now enable appropriate options by default.
>
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Applied to v4.10/defconfig
Thanks,
Sekhar
^ permalink raw reply
* [PATCHv2 01/10] mmc: dw_mmc: display the real register value on debugfs
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, heiko-4mtYJXux2i+zQB+pC5nmwQ,
shawn.lin-TNX95d0MmH7DzftRWevZcw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Jaehoon Chung
In-Reply-To: <20161115101232.3854-1-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Developer wants to see the real register value, not register offset.
This patch fixed to display the real value of register.
Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Tested-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
drivers/mmc/host/dw_mmc.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a16c537..e53899e 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -165,12 +165,14 @@ static const struct file_operations dw_mci_req_fops = {
static int dw_mci_regs_show(struct seq_file *s, void *v)
{
- seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
- seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
- seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
- seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
- seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
- seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+ struct dw_mci *host = s->private;
+
+ seq_printf(s, "STATUS:\t0x%08x\n", mci_readl(host, STATUS));
+ seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS));
+ seq_printf(s, "CMD:\t0x%08x\n", mci_readl(host, CMD));
+ seq_printf(s, "CTRL:\t0x%08x\n", mci_readl(host, CTRL));
+ seq_printf(s, "INTMASK:\t0x%08x\n", mci_readl(host, INTMASK));
+ seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA));
return 0;
}
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 00/10] mmc: dw_mmc: clean the codes for dwmmc controller
From: Jaehoon Chung @ 2016-11-15 10:12 UTC (permalink / raw)
To: linux-mmc
Cc: devicetree, ulf.hansson, heiko, shawn.lin, robh+dt, Jaehoon Chung
This patchset is modified the some minor fixing and cleaning codes.
* Major changes
- Use the cookie enum values like sdhci controller.
- Remove the unnecessary codes and use_stop_abort() by default.
- Remove the obsoleted property "supports-highspeed"
- Deprecated the "clock-freq-min-max" property. Instead, use "max-frequency"
- Minimum clock value is set to 100K by default.
Change in v2:
- Applied patches relevant to dt files
- Use "deperecated" instead of removing about "clock-freq-min-max"
- Added Heiko's Tested-by tag
Jaehoon Chung (10):
mmc: dw_mmc: display the real register value on debugfs
mmc: dw_mmc: fix the debug message for checking card's present
mmc: dw_mmc: change the DW_MCI_FREQ_MIN from 400K to 100K
mmc: dw_mmc: use the hold register when send stop command
mmc: dw_mmc: call the dw_mci_prep_stop_abort() by default
mmc: core: move the cookie's enum values from sdhci.h to mmc.h
mmc: dw_mmc: use the cookie's enum values for post/pre_req()
mmc: dw_mmc: remove the unnecessary mmc_data structure
mmc: dw_mmc: The "clock-freq-min-max" property was deprecated
Documentation: synopsys-dw-mshc: remove the unused properties
.../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 8 +-
drivers/mmc/host/dw_mmc.c | 88 +++++++++++-----------
drivers/mmc/host/sdhci.h | 6 --
include/linux/mmc/core.h | 6 ++
4 files changed, 50 insertions(+), 58 deletions(-)
--
2.10.1
^ permalink raw reply
* Re: [PATCH 1/2] ARM: dts: da850: add the mstpri and ddrctl nodes
From: Sekhar Nori @ 2016-11-15 10:05 UTC (permalink / raw)
To: Bartosz Golaszewski, Kevin Hilman, Michael Turquette, Rob Herring,
Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
Tomi Valkeinen, David Airlie, Laurent Pinchart
In-Reply-To: <1479144724-14231-2-git-send-email-bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On Monday 14 November 2016 11:02 PM, Bartosz Golaszewski wrote:
> Add the nodes for the MSTPRI configuration and DDR2/mDDR memory
> controller drivers to da850.dtsi.
>
> Signed-off-by: Bartosz Golaszewski <bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> ---
> arch/arm/boot/dts/da850.dtsi | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
> index 1bb1f6d..1635218 100644
> --- a/arch/arm/boot/dts/da850.dtsi
> +++ b/arch/arm/boot/dts/da850.dtsi
> @@ -440,6 +440,11 @@
> interrupts = <52>;
> status = "disabled";
> };
> +
> + mstpri: mstpri@14110 {
> + compatible = "ti,da850-mstpri";
> + reg = <0x14110 0x0c>;
> + };
Instead of adding the node to the end, can you place it above the
cfgchip node to keep it sorted by reg. We have not really followed that
in this file. May be we should have. But lets start with this.
> };
> aemif: aemif@68000000 {
> compatible = "ti,da850-aemif";
> @@ -451,4 +456,8 @@
> 1 0 0x68000000 0x00008000>;
> status = "disabled";
> };
> + ddrctl: ddrctl@b0000000 {
> + compatible = "ti,da850-ddr-controller";
> + reg = <0xb0000000 0xe8>;
> + };
Can you name the node memory-controller@b0000000? Thats the generic name
suggested by ePAPR 1.1 for memory controllers.
I could not find any naming suggestions for the bus master priority
controller above, but based on the pattern used for other controllers,
may be it should be called priority-controller@14110 instead of mstpri@14110
Thanks,
Sekhar
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH v7 0/3] Add clockevent for timer-nps driver to NPS400 SoC
From: Noam Camus @ 2016-11-15 10:02 UTC (permalink / raw)
To: Daniel Lezcano
Cc: robh+dt@kernel.org, mark.rutland@arm.com, tglx@linutronix.de,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20161115095754.GA1835@mai>
> From: Daniel Lezcano [mailto:daniel.lezcano@linaro.org]
> Sent: Tuesday, November 15, 2016 11:58 AM
>Do you want me to take the entire series in my tree, or do you want my acked-by to push them in your tree ?
I do not have tree of my own.
Will appreciate if you take entire series.
I believe it is same as you do with Synopsys ARC driver (by Vineet Gupta)
Many Thanks
Noam
^ permalink raw reply
* Re: [PATCH v7 0/3] Add clockevent for timer-nps driver to NPS400 SoC
From: Daniel Lezcano @ 2016-11-15 9:57 UTC (permalink / raw)
To: Noam Camus
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
tglx-hfZtesqFncYOwBW4kG4KsQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479199859-980-1-git-send-email-noamca-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
On Tue, Nov 15, 2016 at 10:50:56AM +0200, Noam Camus wrote:
> From: Noam Camus <noamca-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
>
> Change log
> ---
> V6 --> V7
> Apply several comments made by Daniel Lezcano:
> 1) Remove CLOCK_EVT_FEAT_PERIODIC support. This way it is
> pure oneshot driver. This simplifies driver so that:
> nps_clkevent_add_thread()
> nps_clkevent_rm_thread()
> are more clearer without any vague logic if to change
> TSI bit of current HW thread or not.
> 2) tick_resume is also calls nps_clkevent_rm_thread()
> 3) Few (hopefully last) typo fixes.
>
> V5 --> V6
> Apply several comments made by Daniel Lezcano:
> 1) nps_get_timer_clk() - use clk_put() on error scenario
> 2) nps_get_timer_clk() - return EINVAL and not plain 1
> 3) Fix typos in log (double checked with spell checker)
>
> V4 --> V5
> Apply several comments made by Daniel Lezcano:
> 1) Add __init attribute to nps_get_timer_clk()
> 2) Fix return value of nps_get_timer_clk()
> when failing to get clk rate
> 3) Change clocksource rate from 301 -> 300
>
> V3 --> V4
> Main changes are [Thanks for the review]:
> Fix many typos at log [Daniel]
> Add handling for bad return values [Daniel and Thomas]
> Replace use of internal irqchip pointers with existing IRQ API [Thomas]
> Provide interrupt handler (percpu) with dev_id equal to evt [Thomas]
> Fix passing *clk by reference to nps_get_timer_clk() [Daniel]
>
> V2 --> V3
> Apply Rob Herring comment about backword compatibility
>
> V1 --> V2
> Apply Daniel Lezcano comments:
> CLOCKSOURCE_OF_DECLARE return value
> update hotplug callbacks usage
> squash of 2 first commits.
> In this version I created new commit to serve as preperation for adding clockevents.
> This way the last patch is more readable with clockevent content.
> ---
Do you want me to take the entire series in my tree, or do you want my acked-by
to push them in your tree ?
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox