* Re: [PATCH 6/6] ARM: configs: keystone_defconfig: Enable few peripheral drivers
From: Vignesh R @ 2017-12-04 9:00 UTC (permalink / raw)
To: santosh.shilimkar-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org,
Santosh Shilimkar
Cc: Rob Herring, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <fb0c662b-d462-c663-7cf0-443ace3565ba-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
Hi,
On Sunday 03 December 2017 09:30 AM, santosh.shilimkar-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org wrote:
> On 11/22/17 11:51 PM, Vignesh R wrote:
>> Enable drivers for QSPI, LEDS, gpio-decoder that are present on 66AK2G
>> EVM
>> and 66AK2G ICE boards.
>>
>> Signed-off-by: Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org>
>> ---
> Please submit a patch also to enable all these peripherals
> in multi-v7 config. Just enable all remainder
> options enabled in keystone config also in multi-v7.
K2 Platforms don't boot out of multi_v7_defconfig by default, because K2
platforms require CONFIG_ARM_LPAE(which is disabled in multi-v7).
If the intention is to just have all drivers enabled in
keystone_defconfig to be added to multi_v7, I can prepare a patch for
that and test with local patch to enable LPAE.
>
> Am pushing this series to next now.
>
Thanks!
--
Regards
Vignesh
--
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 v6 13/13] ASoC: stm32: add DFSDM DAI support
From: Arnaud Pouliquen @ 2017-12-04 8:58 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Lars-Peter Clausen, Maxime Coquelin,
Liam Girdwood, linux-iio@vger.kernel.org, Mark Brown,
Takashi Iwai, Rob Herring, Peter Meerwald-Stadler, Hartmut Knaack,
linux-arm-kernel@lists.infradead.org, Alexandre TORGUE
In-Reply-To: <20171202150952.1b5f3bfd@archlinux>
On 12/02/2017 04:09 PM, Jonathan Cameron wrote:
> On Fri, 1 Dec 2017 18:40:20 +0100
> Arnaud Pouliquen <arnaud.pouliquen@st.com> wrote:
>
>> Add driver to handle DAI interface for PDM microphones connected
>> to Digital Filter for Sigma Delta Modulators IP.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>
> include tied up with the move of that enum in the earlier patch.
> Other than that the IIO stuff looks fine to me.
>
> Mark, given this set is moderately invasive on the IIO side and
> should just drop in cleanly on the sound side of things, either
> I could take it via IIO or one of us can do an immutable branch
> and we take it through both trees.
Don't know if you saw the reply from Mark on V5:
"This is basically fine, if someone could send me a pull request and the
relevant patches when the IIO stuff is sorted out I'll give it a final
check and apply then."
>
> Don't mind which but if I'm either taking the series or doing
> an immutable branch I'll obviously be waiting on your review!
>
> Also need time for Rob to check the updating bindings.
>
> Anyhow, it's coming together reasonably nicely in the end.
> Good work Arnaud!
Thanks! it will be a good achievement for me, not trivial this
peripheral to integrate :)
I'm waiting Rob's ack or remarks on patch 12/13 to send the next version.
Thanks and Regards
Arnaud
>
> Jonathan
>> ---
>> V5 to V6 update:
>> fix build warning
>>
>> sound/soc/stm/Kconfig | 11 ++
>> sound/soc/stm/Makefile | 3 +
>> sound/soc/stm/stm32_adfsdm.c | 386 +++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 400 insertions(+)
>> create mode 100644 sound/soc/stm/stm32_adfsdm.c
>>
>> diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
>> index 3398e6c..a78f770 100644
>> --- a/sound/soc/stm/Kconfig
>> +++ b/sound/soc/stm/Kconfig
>> @@ -28,4 +28,15 @@ config SND_SOC_STM32_SPDIFRX
>> help
>> Say Y if you want to enable S/PDIF capture for STM32
>>
>> +config SND_SOC_STM32_DFSDM
>> + tristate "SoC Audio support for STM32 DFSDM"
>> + depends on (ARCH_STM32 && OF && STM32_DFSDM_ADC) || COMPILE_TEST
>> + depends on SND_SOC
>> + select SND_SOC_GENERIC_DMAENGINE_PCM
>> + select SND_SOC_DMIC
>> + select IIO_BUFFER_CB
>> + help
>> + Select this option to enable the STM32 Digital Filter
>> + for Sigma Delta Modulators (DFSDM) driver used
>> + in various STM32 series for digital microphone capture.
>> endmenu
>> diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
>> index 4ed22e6..53e90e6 100644
>> --- a/sound/soc/stm/Makefile
>> +++ b/sound/soc/stm/Makefile
>> @@ -12,3 +12,6 @@ obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o
>> # SPDIFRX
>> snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o
>> obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o
>> +
>> +#DFSDM
>> +obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
>> diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
>> new file mode 100644
>> index 0000000..890ec24
>> --- /dev/null
>> +++ b/sound/soc/stm/stm32_adfsdm.c
>> @@ -0,0 +1,386 @@
>> +/*
>> + * This file is part of STM32 DFSDM ASoC DAI driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> + * Olivier Moysan <olivier.moysan@st.com>
>> + *
>> + * License terms: GPL V2.0.
>> + *
>> + * 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/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/iio.h>
>
> I missed this before but a consumer should not need to include iio.h.
> I would guess it was about that enum that I want you to move to types.h?
>
>> +#include <linux/iio/consumer.h>
>> +#include <linux/iio/adc/stm32-dfsdm-adc.h>
>> +
>> +#include <sound/pcm.h>
>> +#include <sound/soc.h>
>> +
>> +#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
>> +
>> +#define DFSDM_MAX_PERIOD_SIZE (PAGE_SIZE / 2)
>> +#define DFSDM_MAX_PERIODS 6
>> +
>> +struct stm32_adfsdm_priv {
>> + struct snd_soc_dai_driver dai_drv;
>> + struct snd_pcm_substream *substream;
>> + struct device *dev;
>> +
>> + /* IIO */
>> + struct iio_channel *iio_ch;
>> + struct iio_cb_buffer *iio_cb;
>> + bool iio_active;
>> +
>> + /* PCM buffer */
>> + unsigned char *pcm_buff;
>> + unsigned int pos;
>> + bool allocated;
>> +};
>> +
>> +struct stm32_adfsdm_data {
>> + unsigned int rate; /* SNDRV_PCM_RATE value */
>> + unsigned int freq; /* frequency in Hz */
>> +};
>> +
>> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
>> + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> + SNDRV_PCM_INFO_PAUSE,
>> + .formats = SNDRV_PCM_FMTBIT_S32_LE,
>> +
>> + .rate_min = 8000,
>> + .rate_max = 32000,
>> +
>> + .channels_min = 1,
>> + .channels_max = 1,
>> +
>> + .periods_min = 2,
>> + .periods_max = DFSDM_MAX_PERIODS,
>> +
>> + .period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
>> + .buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
>> +};
>> +
>> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
>> + struct snd_soc_dai *dai)
>> +{
>> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> + if (priv->iio_active) {
>> + iio_channel_stop_all_cb(priv->iio_cb);
>> + priv->iio_active = false;
>> + }
>> +}
>> +
>> +static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
>> + struct snd_soc_dai *dai)
>> +{
>> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> + int ret;
>> +
>> + ret = iio_write_channel_attribute(priv->iio_ch,
>> + substream->runtime->rate, 0,
>> + IIO_CHAN_INFO_SAMP_FREQ);
>> + if (ret < 0) {
>> + dev_err(dai->dev, "%s: Failed to set %d sampling rate\n",
>> + __func__, substream->runtime->rate);
>> + return ret;
>> + }
>> +
>> + if (!priv->iio_active) {
>> + ret = iio_channel_start_all_cb(priv->iio_cb);
>> + if (!ret)
>> + priv->iio_active = true;
>> + else
>> + dev_err(dai->dev, "%s: IIO channel start failed (%d)\n",
>> + __func__, ret);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
>> + unsigned int freq, int dir)
>> +{
>> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> + ssize_t size;
>> +
>> + dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
>> +
>> + /* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
>> + if (dir == SND_SOC_CLOCK_IN) {
>> + char str_freq[10];
>> +
>> + snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
>> + size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
>> + str_freq, sizeof(str_freq));
>> + if (size != sizeof(str_freq)) {
>> + dev_err(dai->dev, "%s: Failed to set SPI clock\n",
>> + __func__);
>> + return -EINVAL;
>> + }
>> + }
>> + return 0;
>> +}
>> +
>> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
>> + .shutdown = stm32_adfsdm_shutdown,
>> + .prepare = stm32_adfsdm_dai_prepare,
>> + .set_sysclk = stm32_adfsdm_set_sysclk,
>> +};
>> +
>> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
>> + .capture = {
>> + .channels_min = 1,
>> + .channels_max = 1,
>> + .formats = SNDRV_PCM_FMTBIT_S32_LE,
>> + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
>> + SNDRV_PCM_RATE_32000),
>> + },
>> + .ops = &stm32_adfsdm_dai_ops,
>> +};
>> +
>> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
>> + .name = "stm32_dfsdm_audio",
>> +};
>> +
>> +static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
>> +{
>> + struct stm32_adfsdm_priv *priv = private;
>> + struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
>> + u8 *pcm_buff = priv->pcm_buff;
>> + u8 *src_buff = (u8 *)data;
>> + unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
>> + unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
>> + unsigned int old_pos = priv->pos;
>> + unsigned int cur_size = size;
>> +
>> + dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %lu\n",
>> + __func__, &pcm_buff[priv->pos], priv->pos, size);
>> +
>> + if ((priv->pos + size) > buff_size) {
>> + memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
>> + cur_size -= buff_size - priv->pos;
>> + priv->pos = 0;
>> + }
>> +
>> + memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
>> + priv->pos = (priv->pos + cur_size) % buff_size;
>> +
>> + if (cur_size != size || (old_pos && (old_pos % period_size < size)))
>> + snd_pcm_period_elapsed(priv->substream);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
>> +{
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct stm32_adfsdm_priv *priv =
>> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +
>> + switch (cmd) {
>> + case SNDRV_PCM_TRIGGER_START:
>> + case SNDRV_PCM_TRIGGER_RESUME:
>> + priv->pos = 0;
>> + return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
>> + stm32_afsdm_pcm_cb, priv);
>> + case SNDRV_PCM_TRIGGER_SUSPEND:
>> + case SNDRV_PCM_TRIGGER_STOP:
>> + return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
>> +{
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> + int ret;
>> +
>> + ret = snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
>> + if (!ret)
>> + priv->substream = substream;
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
>> +{
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct stm32_adfsdm_priv *priv =
>> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +
>> + snd_pcm_lib_free_pages(substream);
>> + priv->substream = NULL;
>> +
>> + return 0;
>> +}
>> +
>> +static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
>> + struct snd_pcm_substream *substream)
>> +{
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct stm32_adfsdm_priv *priv =
>> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +
>> + return bytes_to_frames(substream->runtime, priv->pos);
>> +}
>> +
>> +static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
>> + struct snd_pcm_hw_params *params)
>> +{
>> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + struct stm32_adfsdm_priv *priv =
>> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> + int ret;
>> +
>> + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
>> + if (ret < 0)
>> + return ret;
>> + priv->pcm_buff = substream->runtime->dma_area;
>> +
>> + return iio_channel_cb_set_buffer_watermark(priv->iio_cb,
>> + params_period_size(params));
>> +}
>> +
>> +static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
>> +{
>> + snd_pcm_lib_free_pages(substream);
>> +
>> + return 0;
>> +}
>> +
>> +static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
>> + .open = stm32_adfsdm_pcm_open,
>> + .close = stm32_adfsdm_pcm_close,
>> + .hw_params = stm32_adfsdm_pcm_hw_params,
>> + .hw_free = stm32_adfsdm_pcm_hw_free,
>> + .trigger = stm32_adfsdm_trigger,
>> + .pointer = stm32_adfsdm_pcm_pointer,
>> +};
>> +
>> +static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
>> +{
>> + struct snd_pcm *pcm = rtd->pcm;
>> + struct stm32_adfsdm_priv *priv =
>> + snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> + unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
>> + int ret;
>> +
>> + /*
>> + * FIXME :
>> + * A platform as been registered per DAI.
>> + * In soc_new_pcm function, pcm_new callback is called for each
>> + * component of the sound card. So if n dai links are created this
>> + * function is called n times.
>> + */
>> + if (priv->allocated)
>> + return 0;
>> +
>> + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
>> + priv->dev, size, size);
>> + if (!ret)
>> + priv->allocated = true;
>> +
>> + return ret;
>> +}
>> +
>> +static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm)
>> +{
>> + struct snd_pcm_substream *substream;
>> + struct snd_soc_pcm_runtime *rtd;
>> + struct stm32_adfsdm_priv *priv;
>> +
>> + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
>> + if (substream) {
>> + rtd = substream->private_data;
>> + priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +
>> + snd_pcm_lib_preallocate_free_for_all(pcm);
>> + priv->allocated = false;
>> + }
>> +}
>> +
>> +static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
>> + .ops = &stm32_adfsdm_pcm_ops,
>> + .pcm_new = stm32_adfsdm_pcm_new,
>> + .pcm_free = stm32_adfsdm_pcm_free,
>> +};
>> +
>> +static const struct of_device_id stm32_adfsdm_of_match[] = {
>> + {.compatible = "st,stm32h7-dfsdm-dai"},
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
>> +
>> +static int stm32_adfsdm_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_adfsdm_priv *priv;
>> + int ret;
>> +
>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + priv->dev = &pdev->dev;
>> + priv->dai_drv = stm32_adfsdm_dai;
>> +
>> + dev_set_drvdata(&pdev->dev, priv);
>> +
>> + ret = devm_snd_soc_register_component(&pdev->dev,
>> + &stm32_adfsdm_dai_component,
>> + &priv->dai_drv, 1);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Associate iio channel */
>> + priv->iio_ch = devm_iio_channel_get_all(&pdev->dev);
>> + if (IS_ERR(priv->iio_ch))
>> + return PTR_ERR(priv->iio_ch);
>> +
>> + priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
>> + if (IS_ERR(priv->iio_cb))
>> + return PTR_ERR(priv->iio_ch);
>> +
>> + ret = devm_snd_soc_register_platform(&pdev->dev,
>> + &stm32_adfsdm_soc_platform);
>> + if (ret < 0)
>> + dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
>> + __func__);
>> +
>> + return ret;
>> +}
>> +
>> +static struct platform_driver stm32_adfsdm_driver = {
>> + .driver = {
>> + .name = STM32_ADFSDM_DRV_NAME,
>> + .of_match_table = stm32_adfsdm_of_match,
>> + },
>> + .probe = stm32_adfsdm_probe,
>> +};
>> +
>> +module_platform_driver(stm32_adfsdm_driver);
>> +
>> +MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
>
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply
* Re: [PATCH v3] usb: xhci: allow imod-interval to be configurable
From: Mathias Nyman @ 2017-12-04 8:57 UTC (permalink / raw)
To: Chunfeng Yun, Adam Wallis
Cc: Greg Kroah-Hartman, Rob Herring, Mark Rutland, Matthias Brugger,
Mathias Nyman, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
timur-sgV2jX0FEOL9JmXXK+q4OQ
In-Reply-To: <1512271374.17567.164.camel@mhfsdcap03>
On 03.12.2017 05:22, Chunfeng Yun wrote:
> On Fri, 2017-12-01 at 10:44 -0500, Adam Wallis wrote:
>> The xHCI driver currently has the IMOD set to 160, which
>> translates to an IMOD interval of 40,000ns (160 * 250)ns
>>
>> Commit 0cbd4b34cda9 ("xhci: mediatek: support MTK xHCI host controller")
>> introduced a QUIRK for the MTK platform to adjust this interval to 20,
>> which translates to an IMOD interval of 5,000ns (20 * 250)ns. This is
>> due to the fact that the MTK controller IMOD interval is 8 times
>> as much as defined in xHCI spec.
>>
>> Instead of adding more quirk bits for additional platforms, this patch
>> introduces the ability for vendors to set the IMOD_INTERVAL as is
>> optimal for their platform. By using device_property_read_u32() on
>> "imod-interval", the IMOD INTERVAL can be specified in nano seconds. If
>> no interval is specified, the default of 40,000ns (IMOD=160) will be
>> used.
>>
>> No bounds checking has been implemented due to the fact that a vendor
>> may have violated the spec and would need to specify a value outside of
>> the max 8,000 IRQs/second limit specified in the xHCI spec.
>>
>> Signed-off-by: Adam Wallis <awallis-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> ---
>> changes from v2:
>> * Added PCI default value [Mathias]
>> * Removed xhci-mtk.h from xhci-plat.c [Chunfeng Yun]
>> * Removed MTK quirk from xhci-plat and moved logic to xhci-mtk [Chunfeng]
>> * Updated bindings Documentation to use proper units [Rob Herring]
>> * Added imod-interval description and example to MTK binding documentation
>> changes from v1:
>> * Removed device_property_read_u32() per suggestion from greg k-h
>> * Used ER_IRQ_INTERVAL_MASK in place of (u16) cast
>>
>> Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt | 2 ++
>> Documentation/devicetree/bindings/usb/usb-xhci.txt | 1 +
>> drivers/usb/host/xhci-mtk.c | 9 +++++++++
>> drivers/usb/host/xhci-pci.c | 3 +++
>> drivers/usb/host/xhci-plat.c | 4 ++++
>> drivers/usb/host/xhci.c | 7 ++-----
>> drivers/usb/host/xhci.h | 2 ++
>> 7 files changed, 23 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> index 3059596..45bbf18 100644
>> --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
>> @@ -46,6 +46,7 @@ Optional properties:
>> - pinctrl-names : a pinctrl state named "default" must be defined
>> - pinctrl-0 : pin control group
>> See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
>> + - imod-interval: Default interval is 5000ns
> I think, as Rob suggested before, recommend to have a unit suffix
> appended to the property name.
> s/imod-interval/imod-interval-ns
>
>>
>> Example:
>> usb30: usb@11270000 {
>> @@ -66,6 +67,7 @@ usb30: usb@11270000 {
>> usb3-lpm-capable;
>> mediatek,syscon-wakeup = <&pericfg>;
>> mediatek,wakeup-src = <1>;
>> + imod-interval = <10000>;
>> };
>>
>> 2nd: dual-role mode with xHCI driver
>> diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> index ae6e484..89b68f1 100644
>> --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
>> @@ -29,6 +29,7 @@ Optional properties:
>> - usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
>> - usb3-lpm-capable: determines if platform is USB3 LPM capable
>> - quirk-broken-port-ped: set if the controller has broken port disable mechanism
>> + - imod-interval: Default interval is 40000ns
>>
>> Example:
>> usb@f0931000 {
>> diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
>> index b62a1d2..278ea3b 100644
>> --- a/drivers/usb/host/xhci-mtk.c
>> +++ b/drivers/usb/host/xhci-mtk.c
>> @@ -674,6 +674,15 @@ static int xhci_mtk_probe(struct platform_device *pdev)
>>
>> xhci = hcd_to_xhci(hcd);
>> xhci->main_hcd = hcd;
>> +
>> + /*
>> + * imod_interval is the interrupt modulation value in nanoseconds.
>> + * The increment interval is 8 times as much as that defined in
>> + * the xHCI spec on MTK's controller.
>> + */
>> + xhci->imod_interval = 5000;
>> + device_property_read_u32(dev, "imod-interval", &xhci->imod_interval);
>> +
>> xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
>> dev_name(dev), hcd);
>> if (!xhci->shared_hcd) {
>> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
>> index 7ef1274..efbe57b 100644
>> --- a/drivers/usb/host/xhci-pci.c
>> +++ b/drivers/usb/host/xhci-pci.c
>> @@ -234,6 +234,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
>> if (!xhci->sbrn)
>> pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
>>
>> + /* imod_interval is the interrupt modulation value in nanoseconds. */
>> + xhci->imod_interval = 40000;
>> +
>> retval = xhci_gen_setup(hcd, xhci_pci_quirks);
>> if (retval)
>> return retval;
>> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
>> index 09f164f..b78be87 100644
>> --- a/drivers/usb/host/xhci-plat.c
>> +++ b/drivers/usb/host/xhci-plat.c
>> @@ -269,6 +269,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
>> if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
>> xhci->quirks |= XHCI_BROKEN_PORT_PED;
>>
>> + /* imod_interval is the interrupt modulation value in nanoseconds. */
>> + xhci->imod_interval = 40000;
>> + device_property_read_u32(sysdev, "imod-interval", &xhci->imod_interval);
>> +
>> hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
>> if (IS_ERR(hcd->usb_phy)) {
>> ret = PTR_ERR(hcd->usb_phy);
>> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
>> index 2424d30..0b7755b 100644
>> --- a/drivers/usb/host/xhci.c
>> +++ b/drivers/usb/host/xhci.c
>> @@ -586,11 +586,8 @@ int xhci_run(struct usb_hcd *hcd)
>> "// Set the interrupt modulation register");
Just noticed the driver has all the time incorrectly used the word "modulation" instead
of "moderation".
If you do the bindings change that Chunfeng pointed out above could you also change
the "modulation" to "moderation" in this patch.
Don't worry about changing the old ones. There's a cleanup patch on its way that
will remove most of them anyway.
Otherwise the xhci parts look good to me.
-Mathias
--
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.1 1/2] ARM64: dts: meson-gx: use stable UART bindings with correct gate clock
From: Neil Armstrong @ 2017-12-04 8:52 UTC (permalink / raw)
To: Andreas Färber, khilman-rdvid1DuHRBWk0Htik3J/w, Helmut Klein
Cc: Martin Blumenstingl, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <61a59a32-a812-cc88-79f9-02c13f1901d7-l3A5Bk7waGM@public.gmane.org>
On 04/12/2017 01:16, Andreas Färber wrote:
> Am 03.12.2017 um 15:15 schrieb Andreas Färber:
>> Hi,
>>
>> Am 21.06.2017 um 16:42 schrieb Neil Armstrong:
>>> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
>>> index 17d3efd..ea53cc2 100644
>>> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
>>> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
>>> @@ -682,6 +682,31 @@
>>> clocks = <&clkc CLKID_SPI>;
>>> };
>>>
>>> +&uart_A {
>>> + clocks = <&xtal>, <&clkc CLKID_UART0>, <&xtal>;
>>> + clock-names = "xtal", "pclk", "baud";
>>> +};
>>> +
>>> +&uart_AO {
>>> + clocks = <&xtal>, <&clkc CLKID_CLK81>, <&xtal>;
>>> + clock-names = "xtal", "pclk", "baud";
>>> +};
>>> +
>>> +&uart_AO_B {
>>> + clocks = <&xtal>, <&clkc CLKID_CLK81>, <&xtal>;
>>> + clock-names = "xtal", "pclk", "baud";
>>> +};
>>> +
>>> +&uart_B {
>>> + clocks = <&xtal>, <&clkc CLKID_UART1>, <&xtal>;
>>> + clock-names = "xtal", "core", "baud";
>>
>> Looking at the meson_uart driver, it only looks for a "pclk" clock,
>> never for "core", and the only unnamed clock used should be the first.
>>
>> There is no bindings documentation for "core", so I assume this was an
>> oversight and should be "pclk" everywhere?
>>
>>> +};
>>> +
>>> +&uart_C {
>>> + clocks = <&xtal>, <&clkc CLKID_UART2>, <&xtal>;
>>> + clock-names = "xtal", "core", "baud";
>>> +};
>>
>> The issue I'm facing is that uart_C on NanoPi K2 is not working in
>> 4.14.1. To my surprise it appears to be probing okay though.
>
> In 4.14 the serial driver checks for the legacy compatible and if
> present (even alongside the new compatible) always takes the legacy code
> path, using only the first clock, thus not tripping over "core" and not
> enabling the "pclk" if supplied.
I only used UART_A, this explains a lot.
>
> This was changed for v4.15-rc1, so we should start seeing errors for
> missing "pclk" clocks.
>
>> Sadly just overwriting the clock-names property via overlay does not fix
>> my issue. Any ideas?
You need to rebind the devices.
>
> For reference here's my latest overlay, overriding compatible, too:
> https://github.com/afaerber/dt-overlays/blob/master/meson-gxbb-nanopi-k2%2Barpi600%2Bnucleo-lrwan1.dts
>
> Regards,
> Andreas
>
Wow I must have been very tired when pushing this....
Thanks for spotting this.
I'll send a fix asap.
Neil
--
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] ARM: dts: sunxi: Convert to CCU index macros for HDMI controller
From: Chen-Yu Tsai @ 2017-12-04 8:44 UTC (permalink / raw)
To: Maxime Ripard
Cc: Chen-Yu Tsai, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
When the HDMI controller device node was added, the needed PLL clock
macros were not exported. A separate patch addresses that, but it is
merged through a different tree.
Now that both patches are in mainline proper, we can convert the raw
numbers to proper macros.
Signed-off-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
---
This is the remain bits of the HDMI series, converting raw index
numbers in the device tree to ccu macros. This is a fix for 4.15.
I've put all the platforms in one patch, though we should have done
sun5i for the previous release.
---
arch/arm/boot/dts/sun4i-a10.dtsi | 4 ++--
arch/arm/boot/dts/sun5i-a10s.dtsi | 4 ++--
arch/arm/boot/dts/sun6i-a31.dtsi | 4 ++--
arch/arm/boot/dts/sun7i-a20.dtsi | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index b91300d49a31..5840f5c75c3b 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -502,8 +502,8 @@
reg = <0x01c16000 0x1000>;
interrupts = <58>;
clocks = <&ccu CLK_AHB_HDMI0>, <&ccu CLK_HDMI>,
- <&ccu 9>,
- <&ccu 18>;
+ <&ccu CLK_PLL_VIDEO0_2X>,
+ <&ccu CLK_PLL_VIDEO1_2X>;
clock-names = "ahb", "mod", "pll-0", "pll-1";
dmas = <&dma SUN4I_DMA_NORMAL 16>,
<&dma SUN4I_DMA_NORMAL 16>,
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 6ae4d95e230e..316cb8b2945b 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -82,8 +82,8 @@
reg = <0x01c16000 0x1000>;
interrupts = <58>;
clocks = <&ccu CLK_AHB_HDMI>, <&ccu CLK_HDMI>,
- <&ccu 9>,
- <&ccu 16>;
+ <&ccu CLK_PLL_VIDEO0_2X>,
+ <&ccu CLK_PLL_VIDEO1_2X>;
clock-names = "ahb", "mod", "pll-0", "pll-1";
dmas = <&dma SUN4I_DMA_NORMAL 16>,
<&dma SUN4I_DMA_NORMAL 16>,
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 8bfa12b548e0..72d3fe44ecaf 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -429,8 +429,8 @@
interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_AHB1_HDMI>, <&ccu CLK_HDMI>,
<&ccu CLK_HDMI_DDC>,
- <&ccu 7>,
- <&ccu 13>;
+ <&ccu CLK_PLL_VIDEO0_2X>,
+ <&ccu CLK_PLL_VIDEO1_2X>;
clock-names = "ahb", "mod", "ddc", "pll-0", "pll-1";
resets = <&ccu RST_AHB1_HDMI>;
reset-names = "ahb";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 68dfa82544fc..59655e42e4b0 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -581,8 +581,8 @@
reg = <0x01c16000 0x1000>;
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_AHB_HDMI0>, <&ccu CLK_HDMI>,
- <&ccu 9>,
- <&ccu 18>;
+ <&ccu CLK_PLL_VIDEO0_2X>,
+ <&ccu CLK_PLL_VIDEO1_2X>;
clock-names = "ahb", "mod", "pll-0", "pll-1";
dmas = <&dma SUN4I_DMA_NORMAL 16>,
<&dma SUN4I_DMA_NORMAL 16>,
--
2.15.0
--
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
* Re: [PATCH 2/6] cpufreq: ARM: sort the Kconfig menu
From: Viresh Kumar @ 2017-12-04 8:41 UTC (permalink / raw)
To: Gregory CLEMENT
Cc: Rafael J. Wysocki, linux-pm, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Rob Herring, devicetree, Thomas Petazzoni,
linux-arm-kernel, Antoine Tenart, Miquèl Raynal,
Nadav Haklai, Victor Gu, Marcin Wojtas, Wilson Ding, Hua Jing,
Neta Zur Hershkovits, Evan Wang
In-Reply-To: <20171201112508.14121-3-gregory.clement@free-electrons.com>
On 01-12-17, 12:25, Gregory CLEMENT wrote:
> +config ARM_VEXPRESS_SPC_CPUFREQ
> + tristate "Versatile Express SPC based CPUfreq driver"
> + depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
> + help
> + This add the CPUfreq driver support for Versatile Express
> + big.LITTLE platforms using SPC for power management.
> +
> +config ARM_SCPI_CPUFREQ
The order of above two must be reversed ?
--
viresh
^ permalink raw reply
* Re: [PATCH v2 0/4] ARM: dts: renesas: Convert to named i2c-gpio bindings
From: Simon Horman @ 2017-12-04 8:35 UTC (permalink / raw)
To: Wolfram Sang
Cc: Geert Uytterhoeven, Magnus Damm, Wolfram Sang, Linus Walleij,
linux-renesas-soc, linux-arm-kernel, linux-i2c, devicetree
In-Reply-To: <20171201110908.u7xjrt7xmlqgigvy@ninjato>
On Fri, Dec 01, 2017 at 12:09:08PM +0100, Wolfram Sang wrote:
> On Fri, Dec 01, 2017 at 09:18:55AM +0100, Simon Horman wrote:
> > On Thu, Nov 30, 2017 at 01:57:22PM +0100, Geert Uytterhoeven wrote:
> > > Hi Simon, Magnus,
> > >
> > > Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
> > > named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
> > > gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
> > > more error-prone unnamed variant.
> > >
> > > This patch series switches all Renesas boards to the new bindings, and
> > > adds the missing GPIO_OPEN_DRAIN I/O flags, which were implicitly
> > > assumed before. The latter gets rid of messages like:
> > >
> > > gpio-208 (?): enforced open drain please flag it properly in DT/ACPI DSDT/board file
> > > gpio-91 (?): enforced open drain please flag it properly in DT/ACPI DSDT/board file
> > >
> > > Patch 1 was extracted from series "[PATCH/RFC 0/3] i2c: gpio: Add
> > > support for named gpios in DT", hence the v2. All other patches are
> > > new.
> > >
> > > Note that after this series is applied, the i2c-gpio buses are no longer
> > > detected when booting new DTBs on old (v4.14 and older) kernels, which
> > > should not be an issue. Booting old DTBs on new kernels is not
> > > affected.
> > >
> > > Thanks for applying!
> >
> > Thanks, applied.
>
> Phew, you guys are fast. For the record:
>
> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Thanks, I'll see about adding your tag.
^ permalink raw reply
* Re: [PATCH 2/2] arm64: dts: renesas: v3msk: add EtherAVB support
From: Geert Uytterhoeven @ 2017-12-04 8:21 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Simon Horman, Rob Herring, Catalin Marinas, Will Deacon,
Linux-Renesas, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Magnus Damm, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Vladimir Barinov
In-Reply-To: <20171124210317.285946395-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
On Fri, Nov 24, 2017 at 9:59 PM, Sergei Shtylyov
<sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org> wrote:
> Define the V3M Starter Kit board dependent part of the EtherAVB
> device node.
>
> Based on the original (and large) patch by Vladimir Barinov.
>
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
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] arm: dts: uniphier: add efuse node for UniPhier 32bit SoC
From: Keiji Hayashibara @ 2017-12-04 8:12 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
masami.hiramatsu-QSEj5FYQhm4dnm+yROfE0A,
jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
hayashi.kunihiko-uWyLwvC0a2jby3iVrkZq2A,
owada.kiyoshi-uWyLwvC0a2jby3iVrkZq2A, Keiji Hayashibara
Add efuse node for UniPhier LD4, Pro4, sLD8, Pro5 and PXs2.
This efuse node is included in soc-glue.
Signed-off-by: Keiji Hayashibara <hayashibara.keiji-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---
arch/arm/boot/dts/uniphier-ld4.dtsi | 18 ++++++++++++++++++
arch/arm/boot/dts/uniphier-pro4.dtsi | 23 +++++++++++++++++++++++
arch/arm/boot/dts/uniphier-pro5.dtsi | 33 +++++++++++++++++++++++++++++++++
arch/arm/boot/dts/uniphier-pxs2.dtsi | 18 ++++++++++++++++++
arch/arm/boot/dts/uniphier-sld8.dtsi | 18 ++++++++++++++++++
5 files changed, 110 insertions(+)
diff --git a/arch/arm/boot/dts/uniphier-ld4.dtsi b/arch/arm/boot/dts/uniphier-ld4.dtsi
index 01fc3e1..6883f3b 100644
--- a/arch/arm/boot/dts/uniphier-ld4.dtsi
+++ b/arch/arm/boot/dts/uniphier-ld4.dtsi
@@ -273,6 +273,24 @@
};
};
+ soc-glue@5f900000 {
+ compatible = "socionext,uniphier-ld4-soc-glue-debug",
+ "simple-mfd";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x5f900000 0x2000>;
+
+ efuse@100 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x100 0x28>;
+ };
+
+ efuse@130 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x130 0x8>;
+ };
+ };
+
timer@60000200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0x60000200 0x20>;
diff --git a/arch/arm/boot/dts/uniphier-pro4.dtsi b/arch/arm/boot/dts/uniphier-pro4.dtsi
index 7955c3a..150726b 100644
--- a/arch/arm/boot/dts/uniphier-pro4.dtsi
+++ b/arch/arm/boot/dts/uniphier-pro4.dtsi
@@ -294,6 +294,29 @@
};
};
+ soc-glue@5f900000 {
+ compatible = "socionext,uniphier-pro4-soc-glue-debug",
+ "simple-mfd";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x5f900000 0x2000>;
+
+ efuse@100 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x100 0x28>;
+ };
+
+ efuse@130 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x130 0x8>;
+ };
+
+ efuse@200 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x200 0x14>;
+ };
+ };
+
aidet: aidet@5fc20000 {
compatible = "socionext,uniphier-pro4-aidet";
reg = <0x5fc20000 0x200>;
diff --git a/arch/arm/boot/dts/uniphier-pro5.dtsi b/arch/arm/boot/dts/uniphier-pro5.dtsi
index 6589b8a..f291dd6 100644
--- a/arch/arm/boot/dts/uniphier-pro5.dtsi
+++ b/arch/arm/boot/dts/uniphier-pro5.dtsi
@@ -355,6 +355,39 @@
};
};
+ soc-glue@5f900000 {
+ compatible = "socionext,uniphier-pro5-soc-glue-debug",
+ "simple-mfd";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x5f900000 0x2000>;
+
+ efuse@100 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x100 0x28>;
+ };
+
+ efuse@130 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x130 0x8>;
+ };
+
+ efuse@200 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x200 0x28>;
+ };
+
+ efuse@300 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x300 0x14>;
+ };
+
+ efuse@400 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x400 0x8>;
+ };
+ };
+
aidet: aidet@5fc20000 {
compatible = "socionext,uniphier-pro5-aidet";
reg = <0x5fc20000 0x200>;
diff --git a/arch/arm/boot/dts/uniphier-pxs2.dtsi b/arch/arm/boot/dts/uniphier-pxs2.dtsi
index d82d6d8..8e54e87 100644
--- a/arch/arm/boot/dts/uniphier-pxs2.dtsi
+++ b/arch/arm/boot/dts/uniphier-pxs2.dtsi
@@ -375,6 +375,24 @@
};
};
+ soc-glue@5f900000 {
+ compatible = "socionext,uniphier-pxs2-soc-glue-debug",
+ "simple-mfd";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x5f900000 0x2000>;
+
+ efuse@100 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x100 0x28>;
+ };
+
+ efuse@200 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x200 0x58>;
+ };
+ };
+
aidet: aidet@5fc20000 {
compatible = "socionext,uniphier-pxs2-aidet";
reg = <0x5fc20000 0x200>;
diff --git a/arch/arm/boot/dts/uniphier-sld8.dtsi b/arch/arm/boot/dts/uniphier-sld8.dtsi
index 7188536..afafe7c 100644
--- a/arch/arm/boot/dts/uniphier-sld8.dtsi
+++ b/arch/arm/boot/dts/uniphier-sld8.dtsi
@@ -277,6 +277,24 @@
};
};
+ soc-glue@5f900000 {
+ compatible = "socionext,uniphier-sld8-soc-glue-debug",
+ "simple-mfd";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x5f900000 0x2000>;
+
+ efuse@100 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x100 0x28>;
+ };
+
+ efuse@200 {
+ compatible = "socionext,uniphier-efuse";
+ reg = <0x200 0x14>;
+ };
+ };
+
timer@60000200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0x60000200 0x20>;
--
2.7.4
--
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
* Re: [PATCH 1/2] arm64: dts: renesas: initial V3MSK board device tree
From: Geert Uytterhoeven @ 2017-12-04 8:11 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Simon Horman, Rob Herring, Catalin Marinas, Will Deacon,
Linux-Renesas, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Magnus Damm, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Vladimir Barinov
In-Reply-To: <20171124210313.219761525-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
On Fri, Nov 24, 2017 at 9:59 PM, Sergei Shtylyov
<sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org> wrote:
> Add the initial device tree for the V3M Starter Kit board.
> The board has 1 debug serial port (SCIF0); include support for it,
> so that the serial console can work.
>
> Based on the original (and large) patch by Vladimir Barinov.
>
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
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 v4 02/10] pinctrl: axp209: add pinctrl features
From: Quentin Schulz @ 2017-12-04 8:07 UTC (permalink / raw)
To: Maxime Ripard
Cc: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20171201155702.irfox7vf3kfjvpjx-ZC1Zs529Oq4@public.gmane.org>
[-- Attachment #1.1: Type: text/plain, Size: 2507 bytes --]
Hi Maxime,
On 01/12/2017 16:57, Maxime Ripard wrote:
> On Fri, Dec 01, 2017 at 02:44:43PM +0100, Quentin Schulz wrote:
>> +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset,
>> + int value)
>> +{
>
> checkpatch output:
> WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
>
>> +static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev,
>> + unsigned int function, unsigned int group)
>> +{
>> + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev);
>> + unsigned int mask;
>> +
>> + /* Every pin supports GPIO_OUT and GPIO_IN functions */
>> + if (function <= AXP20X_FUNC_GPIO_IN)
>> + return axp20x_pmx_set(pctldev, group,
>> + gpio->funcs[function].muxval);
>> +
>> + if (function == AXP20X_FUNC_LDO)
>> + mask = gpio->desc->ldo_mask;
>> + else
>> + mask = gpio->desc->adc_mask;
>
> What is the point of this test...
>
>> + if (!(BIT(group) & mask))
>> + return -EINVAL;
>> +
>> + /*
>> + * We let the regulator framework handle the LDO muxing as muxing bits
>> + * are basically also regulators on/off bits. It's better not to enforce
>> + * any state of the regulator when selecting LDO mux so that we don't
>> + * interfere with the regulator driver.
>> + */
>> + if (function == AXP20X_FUNC_LDO)
>> + return 0;
>
> ... if you know that you're not going to do anything with one of the
> outcomes. It would be better to just move that part above, instead of
> doing the same test twice.
>
Return value is different. In one case, it is an error to request "ldo"
for a pin that does not support it. In the other case, the ldo request
is valid but nothing's done on driver side.
Both cases are handled differently by the core:
http://elixir.free-electrons.com/linux/latest/source/drivers/pinctrl/pinmux.c#L439
I think that's the behavior we're expecting from this driver.
Or maybe you're asking to do:
+ if (function == AXP20X_FUNC_LDO) {
+ if (!(BIT(group) & gpio->desc->ldo_mask))
+ return -EINVAL;
+ return 0;
+ } else if (!(BIT(group) & gpio->desc->adc_mask)) {
+ return -EINVAL;
+ }
?
Thanks,
Quentin
--
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH 0/4] Move DP phy switch to PHY driver
From: Heiko Stübner @ 2017-12-04 7:46 UTC (permalink / raw)
To: Chris Zhong
Cc: Mark Rutland, David Airlie, Catalin Marinas, Shawn Lin,
Will Deacon, Kever Yang, dri-devel, Doug Anderson, Guenter Roeck,
Brian Norris, Kishon Vijay Abraham I,
open list:ARM/Rockchip SoC..., Jianqun Xu, Caesar Wang,
devicetree, Elaine Zhang, Rob Herring, William wu, Linux ARM,
LKML, Tomasz Figa, David Wu,
Enric Balletbo i Serra <enric.ba>
In-Reply-To: <8042d73f-c1de-da41-9066-2377de1f521c@rock-chips.com>
Hi Chris,
Am Montag, 4. Dezember 2017, 10:47:08 CET schrieb Chris Zhong:
> On 2017年12月02日 05:58, Heiko Stuebner wrote:
> > Am Freitag, 1. Dezember 2017, 13:42:46 CET schrieb Doug Anderson:
> >> Hi,
> >>
> >> On Wed, Nov 29, 2017 at 6:27 PM, Chris Zhong <zyw@rock-chips.com> wrote:
> >>> Hi Doug
> >>>
> >>> Thank you for mentioning this patch.
> >>>
> >>> I think the focus of the discussion is: can we put the grf control bit
> >>> to
> >>> dts.
> >>>
> >>> The RK3399 has 2 Type-C phy, but only one DP controller, this
> >>> "uphy_dp_sel"
> >>>
> >>> can help to switch these 2 phy. So I think this bit can be considered as
> >>> a
> >>> part of
> >>>
> >>> Type-C phy, these 2 phy have different bits, just similar to other bits
> >>> (such as "pipe-status").
> >>>
> >>> Put them to DTS file might be a accepted practice.
> >>
> >> I guess the first step would be finding the person to make a decision.
> >> Is that Heiko? Olof? Kishon? Rob?. As I see it there are a few
> >> options:
> >>
> >> 1. Land this series as-is. This makes the new bit work just like all
> >> the other ones next to it. If anyone happens to try to use an old
> >> device tree on a new kernel they'll break. Seems rather unlikely
> >> given that the whole type C PHY is not really fully functional
> >> upstream, but technically this is a no-no from a device tree
> >> perspective.
> >>
> >> 2. Change the series to make this property optional. If it's not
> >> there then the code behaves like it always did. This would address
> >> the "compatibility" problem but likely wouldn't actually help any real
> >> people, and it would be extra work.
> >>
> >> 3. Redo the driver to deprecate all the old offsets / bits and just
> >> put the table in the driver, keyed off the compatible string and base
> >> address if the IO memory.
> >>
> >>
> >> I can't make this decision. It's up to those folks who would be
> >> landing the patch and I'd be happy with any of them. What I'm less
> >> happy with, however, is the indecision preventing forward progress.
> >> We should pick one of the above things and land it. My own personal
> >> bias is #1: just land the series. No real people will be hurt and
> >> it's just adding another property that matches the ones next to it.
> >
> > I'd second that #1 . That whole type-c phy thingy never fully worked in
> > the past (some for the never used dp output), so personally I don't have
> > issues with going that route.
> >
> >> From a long term perspective (AKA how I'd write the next driver like
> >>
> >> this) I personally lean towards to "tables in the driver, not in the
> >> device tree" but quite honestly I'm happy to take whatever direction
> >> the maintainers give.
> >
> > It looks like we're in agreement here :-) . GRF stuff should not leak into
> > the devicetree, as it causes endless headaches later. But I guess we'll
> > need to live with the ones that happened so far.
>
> So, the first step is: move all the private property of tcphy to
> drivers/phy/rockchip/phy-rockchip-typec.c.
> Second step: new a member: uphy-dp-sel.
> In my mind, we should have discussed these properties before, and then I
> moved them all into DTS.
Actually, I was agreeing with Doug, that we probably don't need to rework the
type-c phy driver. As most properties for it are in the devicetree right now
we'll need to support them for backwards-compatiblity anyway.
And yes, there probably was discussion over dts vs. driver-table when the
type-c driver was introduced, but I either missed it or wasn't firm enough
back then ;-) .
Hence the "we'll need to live with it" for the type-c phy, but should not
do similar things in future drivers.
Heiko
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v1 2/2] drm/tinydrm: add driver for ILI9225 panels
From: Daniel Vetter @ 2017-12-04 7:45 UTC (permalink / raw)
To: Linus Walleij
Cc: Mark Rutland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
David Lechner, linux-kernel@vger.kernel.org,
open list:DRM PANEL DRIVERS, Rob Herring, Stefano Babic
In-Reply-To: <CACRpkdYRQoTyzTOK=8n7sawDZAWS3sXRDdDdFpxhZKuV+DE5oQ@mail.gmail.com>
On Fri, Dec 01, 2017 at 03:03:30PM +0100, Linus Walleij wrote:
> On Wed, Nov 8, 2017 at 4:52 AM, David Lechner <david@lechnology.com> wrote:
>
> > This adds a new driver for display panels based on the Ilitek ILI9225
> > controller.
> >
> > This was developed for a no-name panel with a red PCB that is commonly
> > marketed for Arduino. See <https://github.com/Nkawu/TFT_22_ILI9225>.
> >
> > I really did try very hard to find a make and model for this panel, but
> > there doesn't seem to be one, so the best I can do is offer the picture
> > in the link above for identification.
> >
> > Signed-off-by: David Lechner <david@lechnology.com>
>
> Can you explain why tinydrm is not putting its panel drivers in
> drivers/gpu/drm/panel?
>
> I guess everybody knows except me, it's usually like that :(
>
> I am anyways working on a driver for Ilitek 9322 that I want
> to land in drivers/gpu/drm/panel. Here is the last iteration:
> https://lists.freedesktop.org/archives/dri-devel/2017-August/150205.html
> Yeah I got sidetracked. OK I will get to it now.
>
> There are some similarities with the code I'm seeing here
> but I believe they are essentially different. But it will be hard
> to share code if you put the driver in the tinydrm framework.
>
> I guess you have also seen:
> drivers/video/backlight/ili922x.c
> ?
>
> Stefano Babic who wrote the backlight driver is available for
> reviewing, so includ him in follow-ups (added to To: line).
>
> I'm putting you on CC as I'm rewriting it a bit after the DT
> maintainers review, will try to repost ASAP.
Bit more historical context: We tried using drm_panel in tinydrm, but that
didn't really fit to well (as Noralf explains, tinydrm is kinda more for
stand-alone panels). But tinydrm is also a bit too much midlayer-y still,
so there's a bunch of todo items capture in Documentation/gpu/todo.rst. In
the end we shouldn't need a special tinydrm driver, that should be covered
by the usual drm helpers.
Might be worth it to at least capture/summarize some of the reasons for
why tinydrm doesn't use drm_panel, and what it would take to better share
code (or maybe that's just a silly idea, not the first duplicated driver
in drm).
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* [PATCH v6 2/2] media: i2c: Add the ov7740 image sensor driver
From: Wenyou Yang @ 2017-12-04 6:58 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Nicolas Ferre,
devicetree-u79uwXL29TY76Z2rM5mHXA, Sakari Ailus, Jonathan Corbet,
Hans Verkuil, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Linux Media Mailing List, Wenyou Yang, Songjun Wu
In-Reply-To: <20171204065858.3138-1-wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
The ov7740 (color) image sensor is a high performance VGA CMOS
image snesor, which supports for output formats: RAW RGB and YUV
and image sizes: VGA, and QVGA, CIF and any size smaller.
Signed-off-by: Songjun Wu <songjun.wu-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Signed-off-by: Wenyou Yang <wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
---
Changes in v6:
- Remove unnecessary #include <linux/init>.
- Remove unnecessary comments and extra newline.
- Add const for some structures.
- Add the check of the return value from regmap_write().
- Simplify the calling of __v4l2_ctrl_handler_setup().
- Add the default format initialization function.
- Integrate the set_power() and enable/disable the clock into
one function.
Changes in v5:
- Squash the driver and MAINTAINERS entry patches to one.
- Precede the driver patch with the bindings patch.
Changes in v4:
- Assign 'val' a initial value to avoid warning: 'val' may be
used uninitialized.
- Rename REG_REG15 to avoid warning: "REG_REG15" redefined.
Changes in v3:
- Put the MAINTAINERS change to a separate patch.
Changes in v2:
- Split off the bindings into a separate patch.
- Add a new entry to the MAINTAINERS file.
MAINTAINERS | 8 +
drivers/media/i2c/Kconfig | 8 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov7740.c | 1226 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1243 insertions(+)
create mode 100644 drivers/media/i2c/ov7740.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7a52a66aa991..1de965009b13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10053,6 +10053,14 @@ S: Maintained
F: drivers/media/i2c/ov7670.c
F: Documentation/devicetree/bindings/media/i2c/ov7670.txt
+OMNIVISION OV7740 SENSOR DRIVER
+M: Wenyou Yang <wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
+L: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/i2c/ov7740.c
+F: Documentation/devicetree/bindings/media/i2c/ov7740.txt
+
ONENAND FLASH DRIVER
M: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
L: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index cb5d7ff82915..00b1c4c031d4 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -665,6 +665,14 @@ config VIDEO_OV7670
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_OV7740
+ tristate "OmniVision OV7740 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV7740 VGA camera sensor.
+
config VIDEO_OV9650
tristate "OmniVision OV9650/OV9652 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 548a9efce966..9b19ec7fcaf4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
new file mode 100644
index 000000000000..42c25277d005
--- /dev/null
+++ b/drivers/media/i2c/ov7740.c
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (c) 2017 Microchip Corporation.
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+
+#define REG_OUTSIZE_LSB 0x34
+
+/* OV7740 register tables */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BGAIN 0x01 /* blue gain */
+#define REG_RGAIN 0x02 /* red gain */
+#define REG_GGAIN 0x03 /* green gain */
+#define REG_REG04 0x04 /* analog setting, dont change*/
+#define REG_BAVG 0x05 /* b channel average */
+#define REG_GAVG 0x06 /* g channel average */
+#define REG_RAVG 0x07 /* r channel average */
+
+#define REG_REG0C 0x0C /* filp enable */
+#define REG0C_IMG_FLIP 0x80
+#define REG0C_IMG_MIRROR 0x40
+
+#define REG_REG0E 0x0E /* blc line */
+#define REG_HAEC 0x0F /* auto exposure cntrl */
+#define REG_AEC 0x10 /* auto exposure cntrl */
+
+#define REG_CLK 0x11 /* Clock control */
+#define REG_REG55 0x55 /* Clock PLL DIV/PreDiv */
+
+#define REG_REG12 0x12
+
+#define REG_REG13 0x13 /* auto/manual AGC, AEC, Write Balance*/
+#define REG13_AEC_EN 0x01
+#define REG13_AGC_EN 0x04
+
+#define REG_REG14 0x14
+#define REG_CTRL15 0x15
+#define REG15_GAIN_MSB 0x03
+
+#define REG_REG16 0x16
+
+#define REG_MIDH 0x1C /* manufacture id byte */
+#define REG_MIDL 0x1D /* manufacture id byre */
+#define REG_PIDH 0x0A /* Product ID MSB */
+#define REG_PIDL 0x0B /* Product ID LSB */
+
+#define REG_84 0x84 /* lots of stuff */
+#define REG_REG38 0x38 /* sub-addr */
+
+#define REG_AHSTART 0x17 /* Horiz start high bits */
+#define REG_AHSIZE 0x18
+#define REG_AVSTART 0x19 /* Vert start high bits */
+#define REG_AVSIZE 0x1A
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+
+#define REG_HOUTSIZE 0x31
+#define REG_VOUTSIZE 0x32
+#define REG_HVSIZEOFF 0x33
+#define REG_REG34 0x34 /* DSP output size H/V LSB*/
+
+#define REG_ISP_CTRL00 0x80
+#define ISPCTRL00_AWB_EN 0x10
+#define ISPCTRL00_AWB_GAIN_EN 0x04
+
+#define REG_YGAIN 0xE2 /* ygain for contrast control */
+
+#define REG_YBRIGHT 0xE3
+#define REG_SGNSET 0xE4
+#define SGNSET_YBRIGHT_MASK 0x08
+
+#define REG_USAT 0xDD
+#define REG_VSAT 0xDE
+
+
+struct ov7740 {
+ struct v4l2_subdev subdev;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_pad pad;
+#endif
+ struct v4l2_mbus_framefmt format;
+ const struct ov7740_pixfmt *fmt; /* Current format */
+ const struct ov7740_framesize *frmsize;
+ struct regmap *regmap;
+ struct clk *xvclk;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct {
+ /* gain cluster */
+ struct v4l2_ctrl *auto_gain;
+ struct v4l2_ctrl *gain;
+ };
+ struct {
+ struct v4l2_ctrl *auto_wb;
+ struct v4l2_ctrl *blue_balance;
+ struct v4l2_ctrl *red_balance;
+ };
+ struct {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct {
+ /* exposure cluster */
+ struct v4l2_ctrl *auto_exposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* saturation/hue cluster */
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ };
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+
+ struct mutex mutex; /* To serialize asynchronus callbacks */
+ bool streaming; /* Streaming on/off */
+
+ struct gpio_desc *resetb_gpio;
+ struct gpio_desc *pwdn_gpio;
+};
+
+struct ov7740_pixfmt {
+ u32 mbus_code;
+ enum v4l2_colorspace colorspace;
+ const struct reg_sequence *regs;
+ u32 reg_num;
+};
+
+struct ov7740_framesize {
+ u16 width;
+ u16 height;
+ const struct reg_sequence *regs;
+ u32 reg_num;
+};
+
+static const struct reg_sequence ov7740_vga[] = {
+ {0x55, 0x40},
+ {0x11, 0x02},
+
+ {0xd5, 0x10},
+ {0x0c, 0x12},
+ {0x0d, 0x34},
+ {0x17, 0x25},
+ {0x18, 0xa0},
+ {0x19, 0x03},
+ {0x1a, 0xf0},
+ {0x1b, 0x89},
+ {0x22, 0x03},
+ {0x29, 0x18},
+ {0x2b, 0xf8},
+ {0x2c, 0x01},
+ {REG_HOUTSIZE, 0xa0},
+ {REG_VOUTSIZE, 0xf0},
+ {0x33, 0xc4},
+ {REG_OUTSIZE_LSB, 0x0},
+ {0x35, 0x05},
+ {0x04, 0x60},
+ {0x27, 0x80},
+ {0x3d, 0x0f},
+ {0x3e, 0x80},
+ {0x3f, 0x40},
+ {0x40, 0x7f},
+ {0x41, 0x6a},
+ {0x42, 0x29},
+ {0x44, 0x22},
+ {0x45, 0x41},
+ {0x47, 0x02},
+ {0x49, 0x64},
+ {0x4a, 0xa1},
+ {0x4b, 0x40},
+ {0x4c, 0x1a},
+ {0x4d, 0x50},
+ {0x4e, 0x13},
+ {0x64, 0x00},
+ {0x67, 0x88},
+ {0x68, 0x1a},
+
+ {0x14, 0x28},
+ {0x24, 0x3c},
+ {0x25, 0x30},
+ {0x26, 0x72},
+ {0x50, 0x97},
+ {0x51, 0x1f},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x20, 0x00},
+ {0x21, 0xcf},
+ {0x50, 0x4b},
+ {0x38, 0x14},
+ {0xe9, 0x00},
+ {0x56, 0x55},
+ {0x57, 0xff},
+ {0x58, 0xff},
+ {0x59, 0xff},
+ {0x5f, 0x04},
+ {0xec, 0x00},
+ {0x13, 0xff},
+
+ {0x81, 0x3f},
+ {0x82, 0x32},
+ {0x38, 0x11},
+ {0x84, 0x70},
+ {0x85, 0x00},
+ {0x86, 0x03},
+ {0x87, 0x01},
+ {0x88, 0x05},
+ {0x89, 0x30},
+ {0x8d, 0x30},
+ {0x8f, 0x85},
+ {0x93, 0x30},
+ {0x95, 0x85},
+ {0x99, 0x30},
+ {0x9b, 0x85},
+
+ {0x9c, 0x08},
+ {0x9d, 0x12},
+ {0x9e, 0x23},
+ {0x9f, 0x45},
+ {0xa0, 0x55},
+ {0xa1, 0x64},
+ {0xa2, 0x72},
+ {0xa3, 0x7f},
+ {0xa4, 0x8b},
+ {0xa5, 0x95},
+ {0xa6, 0xa7},
+ {0xa7, 0xb5},
+ {0xa8, 0xcb},
+ {0xa9, 0xdd},
+ {0xaa, 0xec},
+ {0xab, 0x1a},
+
+ {0xce, 0x78},
+ {0xcf, 0x6e},
+ {0xd0, 0x0a},
+ {0xd1, 0x0c},
+ {0xd2, 0x84},
+ {0xd3, 0x90},
+ {0xd4, 0x1e},
+
+ {0x5a, 0x24},
+ {0x5b, 0x1f},
+ {0x5c, 0x88},
+ {0x5d, 0x60},
+
+ {0xac, 0x6e},
+ {0xbe, 0xff},
+ {0xbf, 0x00},
+
+ {0x0f, 0x1d},
+ {0x0f, 0x1f},
+};
+
+static const struct ov7740_framesize ov7740_framesizes[] = {
+ {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .regs = ov7740_vga,
+ .reg_num = ARRAY_SIZE(ov7740_vga),
+ },
+};
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov7740_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ struct regmap *regmap = ov7740->regmap;
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_read(regmap, reg->reg & 0xff, &val);
+ reg->val = val;
+ reg->size = 1;
+
+ return 0;
+}
+
+static int ov7740_set_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ struct regmap *regmap = ov7740->regmap;
+
+ regmap_write(regmap, reg->reg & 0xff, reg->val & 0xff);
+
+ return 0;
+}
+#endif
+
+static int ov7740_set_power(struct ov7740 *ov7740, int on)
+{
+ int ret;
+
+ if (on) {
+ ret = clk_prepare_enable(ov7740->xvclk);
+ if (ret)
+ return ret;
+
+ if (ov7740->pwdn_gpio)
+ gpiod_direction_output(ov7740->pwdn_gpio, 0);
+
+ if (ov7740->resetb_gpio) {
+ gpiod_set_value(ov7740->resetb_gpio, 1);
+ usleep_range(500, 1000);
+ gpiod_set_value(ov7740->resetb_gpio, 0);
+ usleep_range(3000, 5000);
+ }
+ } else {
+ clk_disable_unprepare(ov7740->xvclk);
+
+ if (ov7740->pwdn_gpio)
+ gpiod_direction_output(ov7740->pwdn_gpio, 0);
+ }
+
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = {
+ .log_status = v4l2_ctrl_subdev_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov7740_get_register,
+ .s_register = ov7740_set_register,
+#endif
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static int ov7740_set_white_balance(struct ov7740 *ov7740, int awb)
+{
+ struct regmap *regmap = ov7740->regmap;
+ unsigned int value;
+ int ret;
+
+ ret = regmap_read(regmap, REG_ISP_CTRL00, &value);
+ if (!ret) {
+ if (awb)
+ value |= (ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
+ else
+ value &= ~(ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
+ ret = regmap_write(regmap, REG_ISP_CTRL00, value);
+ if (ret)
+ return ret;
+ }
+
+ if (!awb) {
+ ret = regmap_write(regmap, REG_BGAIN,
+ ov7740->blue_balance->val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, REG_RGAIN, ov7740->red_balance->val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov7740_set_saturation(struct regmap *regmap, int value)
+{
+ int ret;
+
+ ret = regmap_write(regmap, REG_USAT, (unsigned char)value);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, REG_VSAT, (unsigned char)value);
+}
+
+static int ov7740_set_gain(struct regmap *regmap, int value)
+{
+ int ret;
+
+ ret = regmap_write(regmap, REG_GAIN, value & 0xff);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, REG_CTRL15,
+ REG15_GAIN_MSB, (value >> 8) & 0x3);
+ if (!ret)
+ ret = regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
+
+ return ret;
+}
+
+static int ov7740_set_autogain(struct regmap *regmap, int value)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, REG_REG13, ®);
+ if (ret)
+ return ret;
+ if (value)
+ reg |= REG13_AGC_EN;
+ else
+ reg &= ~REG13_AGC_EN;
+ return regmap_write(regmap, REG_REG13, reg);
+}
+
+static int ov7740_set_brightness(struct regmap *regmap, int value)
+{
+ /* Turn off AEC/AGC */
+ regmap_update_bits(regmap, REG_REG13, REG13_AEC_EN, 0);
+ regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
+
+ if (value >= 0) {
+ regmap_write(regmap, REG_YBRIGHT, (unsigned char)value);
+ regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 0);
+ } else{
+ regmap_write(regmap, REG_YBRIGHT, (unsigned char)(-value));
+ regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 1);
+ }
+
+ return 0;
+}
+
+static int ov7740_set_contrast(struct regmap *regmap, int value)
+{
+ return regmap_write(regmap, REG_YGAIN, (unsigned char)value);
+}
+
+static int ov7740_get_gain(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl)
+{
+ struct regmap *regmap = ov7740->regmap;
+ unsigned int value0, value1;
+ int ret;
+
+ if (!ctrl->val)
+ return 0;
+
+ ret = regmap_read(regmap, REG_GAIN, &value0);
+ if (ret)
+ return ret;
+ ret = regmap_read(regmap, REG_CTRL15, &value1);
+ if (ret)
+ return ret;
+
+ ov7740->gain->val = (value1 << 8) | (value0 & 0xff);
+
+ return 0;
+}
+
+static int ov7740_set_exp(struct regmap *regmap, int value)
+{
+ int ret;
+
+ /* Turn off AEC/AGC */
+ ret = regmap_update_bits(regmap, REG_REG13,
+ REG13_AEC_EN | REG13_AGC_EN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, REG_AEC, (unsigned char)value);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, REG_HAEC, (unsigned char)(value >> 8));
+}
+
+static int ov7740_set_autoexp(struct regmap *regmap,
+ enum v4l2_exposure_auto_type value)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, REG_REG13, ®);
+ if (!ret) {
+ if (value == V4L2_EXPOSURE_AUTO)
+ reg |= (REG13_AEC_EN | REG13_AGC_EN);
+ else
+ reg &= ~(REG13_AEC_EN | REG13_AGC_EN);
+ ret = regmap_write(regmap, REG_REG13, reg);
+ }
+
+ return ret;
+}
+
+
+static int ov7740_get_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov7740 *ov7740 = container_of(ctrl->handler,
+ struct ov7740, ctrl_handler);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov7740_get_gain(ov7740, ctrl);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov7740 *ov7740 = container_of(ctrl->handler,
+ struct ov7740, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
+ struct regmap *regmap = ov7740->regmap;
+ int ret;
+ u8 val = 0;
+
+ if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov7740_set_white_balance(ov7740, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ ret = ov7740_set_saturation(regmap, ctrl->val);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ ret = ov7740_set_brightness(regmap, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = ov7740_set_contrast(regmap, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = regmap_update_bits(regmap, REG_REG0C,
+ REG0C_IMG_FLIP, val);
+ break;
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? REG0C_IMG_MIRROR : 0x00;
+ ret = regmap_update_bits(regmap, REG_REG0C,
+ REG0C_IMG_MIRROR, val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (!ctrl->val)
+ return ov7740_set_gain(regmap, ov7740->gain->val);
+
+ ret = ov7740_set_autogain(regmap, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL)
+ return ov7740_set_exp(regmap, ov7740->exposure->val);
+
+ ret = ov7740_set_autoexp(regmap, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov7740_ctrl_ops = {
+ .g_volatile_ctrl = ov7740_get_volatile_ctrl,
+ .s_ctrl = ov7740_set_ctrl,
+};
+
+static int ov7740_start_streaming(struct ov7740 *ov7740)
+{
+ return __v4l2_ctrl_handler_setup(ov7740->subdev.ctrl_handler);
+}
+
+static int ov7740_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&ov7740->mutex);
+ if (ov7740->streaming == enable) {
+ mutex_unlock(&ov7740->mutex);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto err_unlock;
+ }
+
+ ret = ov7740_start_streaming(ov7740);
+ if (ret)
+ goto err_rpm_put;
+ } else {
+ pm_runtime_put(&client->dev);
+ }
+
+ ov7740->streaming = enable;
+
+ mutex_unlock(&ov7740->mutex);
+ return ret;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+err_unlock:
+ mutex_unlock(&ov7740->mutex);
+ return ret;
+}
+
+static int ov7740_get_parm(struct v4l2_subdev *sd,
+ struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+
+ tpf->numerator = 1;
+ tpf->denominator = 60;
+
+ return 0;
+}
+
+static int ov7740_set_parm(struct v4l2_subdev *sd,
+ struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+
+ tpf->numerator = 1;
+ tpf->denominator = 60;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = {
+ .s_stream = ov7740_set_stream,
+ .s_parm = ov7740_set_parm,
+ .g_parm = ov7740_get_parm,
+};
+
+static const struct reg_sequence ov7740_format_yuyv[] = {
+ {0x12, 0x00},
+ {0x36, 0x3f},
+ {0x80, 0x7f},
+ {0x83, 0x01},
+};
+
+static const struct reg_sequence ov7740_format_bggr8[] = {
+ {0x36, 0x2f},
+ {0x80, 0x01},
+ {0x83, 0x04},
+};
+
+static const struct ov7740_pixfmt ov7740_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .regs = ov7740_format_yuyv,
+ .reg_num = ARRAY_SIZE(ov7740_format_yuyv),
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .regs = ov7740_format_bggr8,
+ .reg_num = ARRAY_SIZE(ov7740_format_bggr8),
+ }
+};
+#define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats)
+
+static int ov7740_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= N_OV7740_FMTS)
+ return -EINVAL;
+
+ code->code = ov7740_formats[code->index].mbus_code;
+
+ return 0;
+}
+
+static int ov7740_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ if (fie->pad)
+ return -EINVAL;
+
+ if (fie->index >= 1)
+ return -EINVAL;
+
+ if ((fie->width != VGA_WIDTH) || (fie->height != VGA_HEIGHT))
+ return -EINVAL;
+
+ fie->interval.numerator = 1;
+ fie->interval.denominator = 60;
+
+ return 0;
+}
+
+static int ov7740_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->pad)
+ return -EINVAL;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ fse->min_width = fse->max_width = VGA_WIDTH;
+ fse->min_height = fse->max_height = VGA_HEIGHT;
+
+ return 0;
+}
+
+static int ov7740_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ const struct ov7740_pixfmt **ret_fmt,
+ const struct ov7740_framesize **ret_frmsize)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ const struct ov7740_framesize *fsize = &ov7740_framesizes[0];
+ int index, i;
+
+ for (index = 0; index < N_OV7740_FMTS; index++) {
+ if (ov7740_formats[index].mbus_code == fmt->code)
+ break;
+ }
+ if (index >= N_OV7740_FMTS) {
+ /* default to first format */
+ index = 0;
+ fmt->code = ov7740_formats[0].mbus_code;
+ }
+ if (ret_fmt != NULL)
+ *ret_fmt = ov7740_formats + index;
+
+ for (i = 0; i < ARRAY_SIZE(ov7740_framesizes); i++) {
+ if ((fsize->width >= fmt->width) &&
+ (fsize->height >= fmt->height)) {
+ fmt->width = fsize->width;
+ fmt->height = fsize->height;
+ break;
+ }
+
+ fsize++;
+ }
+
+ if (ret_frmsize != NULL)
+ *ret_frmsize = fsize;
+
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = ov7740_formats[index].colorspace;
+
+ ov7740->format = *fmt;
+
+ return 0;
+}
+
+static int ov7740_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ const struct ov7740_pixfmt *ovfmt;
+ const struct ov7740_framesize *fsize;
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ struct v4l2_mbus_framefmt *mbus_fmt;
+#endif
+ int ret;
+
+ mutex_lock(&ov7740->mutex);
+ if (format->pad) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL);
+ if (ret)
+ goto error;
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ *mbus_fmt = format->format;
+
+ mutex_unlock(&ov7740->mutex);
+ return 0;
+#else
+ ret = -ENOTTY;
+ goto error;
+#endif
+ }
+
+ ret = ov7740_try_fmt_internal(sd, &format->format, &ovfmt, &fsize);
+ if (ret)
+ goto error;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ regmap_multi_reg_write(ov7740->regmap,
+ ovfmt->regs, ovfmt->reg_num);
+
+ regmap_multi_reg_write(ov7740->regmap,
+ fsize->regs, fsize->reg_num);
+ }
+
+ ov7740->fmt = ovfmt;
+
+ mutex_unlock(&ov7740->mutex);
+ return 0;
+
+error:
+ mutex_unlock(&ov7740->mutex);
+ return ret;
+}
+
+static int ov7740_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ struct v4l2_mbus_framefmt *mbus_fmt;
+#endif
+ int ret = 0;
+
+ mutex_lock(&ov7740->mutex);
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+ format->format = *mbus_fmt;
+ ret = 0;
+#else
+ ret = -ENOTTY;
+#endif
+ } else {
+ format->format = ov7740->format;
+ }
+ mutex_unlock(&ov7740->mutex);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = {
+ .enum_frame_interval = ov7740_enum_frame_interval,
+ .enum_frame_size = ov7740_enum_frame_size,
+ .enum_mbus_code = ov7740_enum_mbus_code,
+ .get_fmt = ov7740_get_fmt,
+ .set_fmt = ov7740_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov7740_subdev_ops = {
+ .core = &ov7740_subdev_core_ops,
+ .video = &ov7740_subdev_video_ops,
+ .pad = &ov7740_subdev_pad_ops,
+};
+
+static void ov7740_get_default_format(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *format)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+ format->width = ov7740->frmsize->width;
+ format->height = ov7740->frmsize->height;
+ format->colorspace = ov7740->fmt->colorspace;
+ format->code = ov7740->fmt->mbus_code;
+ format->field = V4L2_FIELD_NONE;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+ struct v4l2_mbus_framefmt *format =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+ mutex_lock(&ov7740->mutex);
+ ov7740_get_default_format(sd, format);
+ mutex_unlock(&ov7740->mutex);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = {
+ .open = ov7740_open,
+};
+#endif
+
+static void ov7740_init_default_format(struct ov7740 *ov7740)
+{
+ ov7740->frmsize = &ov7740_framesizes[0];
+ ov7740->fmt = &ov7740_formats[0];
+
+ regmap_multi_reg_write(ov7740->regmap,
+ ov7740->fmt->regs, ov7740->fmt->reg_num);
+
+ regmap_multi_reg_write(ov7740->regmap,
+ ov7740->frmsize->regs, ov7740->frmsize->reg_num);
+}
+
+static int ov7740_probe_dt(struct i2c_client *client,
+ struct ov7740 *ov7740)
+{
+ ov7740->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov7740->resetb_gpio)) {
+ dev_info(&client->dev, "can't get %s GPIO\n", "reset");
+ return PTR_ERR(ov7740->resetb_gpio);
+ }
+
+ ov7740->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov7740->pwdn_gpio)) {
+ dev_info(&client->dev, "can't get %s GPIO\n", "powerdown");
+ return PTR_ERR(ov7740->pwdn_gpio);
+ }
+
+ return 0;
+}
+
+static int ov7740_detect(struct ov7740 *ov7740)
+{
+ struct regmap *regmap = ov7740->regmap;
+ unsigned int midh, midl, pidh, pidl;
+ int ret;
+
+ ret = regmap_read(regmap, REG_MIDH, &midh);
+ if (ret)
+ return ret;
+ if (midh != 0x7f)
+ return -ENODEV;
+
+ ret = regmap_read(regmap, REG_MIDL, &midl);
+ if (ret)
+ return ret;
+ if (midl != 0xa2)
+ return -ENODEV;
+
+ ret = regmap_read(regmap, REG_PIDH, &pidh);
+ if (ret)
+ return ret;
+ if (pidh != 0x77)
+ return -ENODEV;
+
+ ret = regmap_read(regmap, REG_PIDL, &pidl);
+ if (ret)
+ return ret;
+ if ((pidl != 0x40) && (pidl != 0x41) && (pidl != 0x42))
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ov7740_init_controls(struct ov7740 *ov7740)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
+ struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2);
+ if (ret < 0)
+ return ret;
+
+ ctrl_hdlr->lock = &ov7740->mutex;
+ ov7740->auto_wb = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ov7740->blue_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE,
+ 0, 0xff, 1, 0x80);
+ ov7740->red_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_RED_BALANCE,
+ 0, 0xff, 1, 0x80);
+
+ ov7740->brightness = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_BRIGHTNESS,
+ -255, 255, 1, 0);
+ ov7740->contrast = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_CONTRAST,
+ 0, 127, 1, 0x20);
+ ov7740->saturation = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 256, 1, 0x80);
+ ov7740->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_GAIN, 0, 1023, 1, 500);
+ ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
+ ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr,
+ &ov7740_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+
+ ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false);
+ v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true);
+ v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure,
+ V4L2_EXPOSURE_MANUAL, false);
+ v4l2_ctrl_cluster(2, &ov7740->hflip);
+
+ ret = v4l2_ctrl_handler_setup(ctrl_hdlr);
+ if (ret) {
+ dev_err(&client->dev, "%s control init failed (%d)\n",
+ __func__, ret);
+ goto error;
+ }
+
+ ov7740->subdev.ctrl_handler = ctrl_hdlr;
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ mutex_destroy(&ov7740->mutex);
+ return ret;
+}
+
+static void ov7740_free_controls(struct ov7740 *ov7740)
+{
+ v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+ mutex_destroy(&ov7740->mutex);
+}
+
+#define OV7740_MAX_REGISTER 0xff
+static const struct regmap_config ov7740_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = OV7740_MAX_REGISTER,
+};
+
+static int ov7740_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ov7740 *ov7740;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "OV7740: I2C-Adapter doesn't support SMBUS\n");
+ return -EIO;
+ }
+
+ ov7740 = devm_kzalloc(&client->dev, sizeof(*ov7740), GFP_KERNEL);
+ if (!ov7740)
+ return -ENOMEM;
+
+ ov7740->xvclk = devm_clk_get(&client->dev, "xvclk");
+ if (IS_ERR(ov7740->xvclk)) {
+ ret = PTR_ERR(ov7740->xvclk);
+ dev_err(&client->dev,
+ "OV7740: fail to get xvclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = ov7740_probe_dt(client, ov7740);
+ if (ret)
+ return ret;
+
+ ov7740->regmap = devm_regmap_init_i2c(client, &ov7740_regmap_config);
+ if (IS_ERR(ov7740->regmap)) {
+ ret = PTR_ERR(ov7740->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ sd = &ov7740->subdev;
+ client->flags |= I2C_CLIENT_SCCB;
+ v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops);
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ sd->internal_ops = &ov7740_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov7740->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad);
+ if (ret)
+ return ret;
+#endif
+
+ ret = ov7740_set_power(ov7740, 1);
+ if (ret)
+ return ret;
+
+ ret = ov7740_detect(ov7740);
+ if (ret)
+ goto error_detect;
+
+ mutex_init(&ov7740->mutex);
+
+ ret = ov7740_init_controls(ov7740);
+ if (ret)
+ goto error_init_controls;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ ov7740_init_default_format(ov7740);
+
+ ov7740_get_default_format(sd, &ov7740->format);
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto error_async_register;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+error_async_register:
+ v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+error_init_controls:
+ ov7740_free_controls(ov7740);
+error_detect:
+ ov7740_set_power(ov7740, 0);
+ media_entity_cleanup(&ov7740->subdev.entity);
+
+ return ret;
+}
+
+static int ov7740_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+ mutex_destroy(&ov7740->mutex);
+ v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&ov7740->subdev.entity);
+#endif
+ v4l2_async_unregister_subdev(sd);
+ ov7740_free_controls(ov7740);
+
+ pm_runtime_get_sync(&client->dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ ov7740_set_power(ov7740, 0);
+ return 0;
+}
+
+static int __maybe_unused ov7740_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+ ov7740_set_power(ov7740, 0);
+
+ return 0;
+}
+
+static int __maybe_unused ov7740_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
+
+ return ov7740_set_power(ov7740, 1);
+}
+
+static const struct i2c_device_id ov7740_id[] = {
+ { "ov7740", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ov7740_id);
+
+static const struct dev_pm_ops ov7740_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov7740_runtime_suspend, ov7740_runtime_resume, NULL)
+};
+
+static const struct of_device_id ov7740_of_match[] = {
+ {.compatible = "ovti,ov7740", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov7740_of_match);
+
+static struct i2c_driver ov7740_i2c_driver = {
+ .driver = {
+ .name = "ov7740",
+ .pm = &ov7740_pm_ops,
+ .of_match_table = of_match_ptr(ov7740_of_match),
+ },
+ .probe = ov7740_probe,
+ .remove = ov7740_remove,
+ .id_table = ov7740_id,
+};
+module_i2c_driver(ov7740_i2c_driver);
+
+MODULE_DESCRIPTION("The V4L2 driver for Omnivision 7740 sensor");
+MODULE_AUTHOR("Songjun Wu <songjun.wu-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
2.15.0
--
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
* [PATCH v6 1/2] media: ov7740: Document device tree bindings
From: Wenyou Yang @ 2017-12-04 6:58 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Nicolas Ferre,
devicetree-u79uwXL29TY76Z2rM5mHXA, Sakari Ailus, Jonathan Corbet,
Hans Verkuil, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Linux Media Mailing List, Wenyou Yang
In-Reply-To: <20171204065858.3138-1-wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Add the device tree binding documentation for the ov7740 sensor driver.
Signed-off-by: Wenyou Yang <wenyou.yang-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
---
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- Explicitly document the "remote-endpoint" property.
Changes in v2: None
.../devicetree/bindings/media/i2c/ov7740.txt | 47 ++++++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov7740.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/ov7740.txt b/Documentation/devicetree/bindings/media/i2c/ov7740.txt
new file mode 100644
index 000000000000..af781c3a5f0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov7740.txt
@@ -0,0 +1,47 @@
+* Omnivision OV7740 CMOS image sensor
+
+The Omnivision OV7740 image sensor supports multiple output image
+size, such as VGA, and QVGA, CIF and any size smaller. It also
+supports the RAW RGB and YUV output formats.
+
+The common video interfaces bindings (see video-interfaces.txt) should
+be used to specify link to the image data receiver. The OV7740 device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Required Properties:
+- compatible: "ovti,ov7740".
+- reg: I2C slave address of the sensor.
+- clocks: Reference to the xvclk input clock.
+- clock-names: "xvclk".
+
+Optional Properties:
+- reset-gpios: Rreference to the GPIO connected to the reset_b pin,
+ if any. Active low with pull-ip resistor.
+- powerdown-gpios: Reference to the GPIO connected to the pwdn pin,
+ if any. Active high with pull-down resistor.
+
+Endpoint node mandatory properties:
+- remote-endpoint: A phandle to the bus receiver's endpoint node.
+
+Example:
+
+ i2c1: i2c@fc028000 {
+ ov7740: camera@21 {
+ compatible = "ovti,ov7740";
+ reg = <0x21>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sensor_power &pinctrl_sensor_reset>;
+ clocks = <&isc>;
+ clock-names = "xvclk";
+ assigned-clocks = <&isc>;
+ assigned-clock-rates = <24000000>;
+ reset-gpios = <&pioA 43 GPIO_ACTIVE_LOW>;
+ powerdown-gpios = <&pioA 44 GPIO_ACTIVE_HIGH>;
+
+ port {
+ ov7740_0: endpoint {
+ remote-endpoint = <&isc_0>;
+ };
+ };
+ };
+ };
--
2.15.0
--
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
* [PATCH v6 0/2] media: ov7740: Add a V4L2 sensor-level driver
From: Wenyou Yang @ 2017-12-04 6:58 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Mark Rutland
Cc: linux-kernel, Nicolas Ferre, devicetree, Sakari Ailus,
Jonathan Corbet, Hans Verkuil, linux-arm-kernel,
Linux Media Mailing List, Wenyou Yang
Add a Video4Linux2 sensor-level driver for the OmniVision OV7740
VGA camera image sensor.
Changes in v6:
- Remove unnecessary #include <linux/init>.
- Remove unnecessary comments and extra newline.
- Add const for some structures.
- Add the check of the return value from regmap_write().
- Simplify the calling of __v4l2_ctrl_handler_setup().
- Add the default format initialization function.
- Integrate the set_power() and enable/disable the clock into
one function.
Changes in v5:
- Squash the driver and MAINTAINERS entry patches to one.
- Precede the driver patch with the bindings patch.
Changes in v4:
- Assign 'val' a initial value to avoid warning: 'val' may be
used uninitialized.
- Rename REG_REG15 to avoid warning: "REG_REG15" redefined.
Changes in v3:
- Explicitly document the "remote-endpoint" property.
- Put the MAINTAINERS change to a separate patch.
Changes in v2:
- Split off the bindings into a separate patch.
- Add a new entry to the MAINTAINERS file.
Wenyou Yang (2):
media: ov7740: Document device tree bindings
media: i2c: Add the ov7740 image sensor driver
.../devicetree/bindings/media/i2c/ov7740.txt | 47 +
MAINTAINERS | 8 +
drivers/media/i2c/Kconfig | 8 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov7740.c | 1226 ++++++++++++++++++++
5 files changed, 1290 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov7740.txt
create mode 100644 drivers/media/i2c/ov7740.c
--
2.15.0
^ permalink raw reply
* Re: [PATCH v5 2/2] media: i2c: Add the ov7740 image sensor driver
From: Yang, Wenyou @ 2017-12-04 6:58 UTC (permalink / raw)
To: Sakari Ailus
Cc: Mauro Carvalho Chehab, Rob Herring, Mark Rutland, linux-kernel,
Nicolas Ferre, devicetree, Jonathan Corbet, Hans Verkuil,
linux-arm-kernel, Linux Media Mailing List, Songjun Wu
In-Reply-To: <20171128120617.hmqbodu2usrtmkao@valkosipuli.retiisi.org.uk>
Hi Sakari,
Sorry for late answer.
Thank you for your review.
On 2017/11/28 20:06, Sakari Ailus wrote:
> Hi Wenyou,
>
> Thanks for the patch. Some comments below.
>
> On Tue, Nov 28, 2017 at 01:22:59PM +0800, Wenyou Yang wrote:
>> The ov7740 (color) image sensor is a high performance VGA CMOS
>> image snesor, which supports for output formats: RAW RGB and YUV
>> and image sizes: VGA, and QVGA, CIF and any size smaller.
>>
>> Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
>> Signed-off-by: Wenyou Yang <wenyou.yang@microchip.com>
>> ---
>>
>> Changes in v5:
>> - Squash the driver and MAINTAINERS entry patches to one.
>> - Precede the driver patch with the bindings patch.
>>
>> Changes in v4:
>> - Assign 'val' a initial value to avoid warning: 'val' may be
>> used uninitialized.
>> - Rename REG_REG15 to avoid warning: "REG_REG15" redefined.
>>
>> Changes in v3:
>> - Put the MAINTAINERS change to a separate patch.
>>
>> Changes in v2:
>> - Split off the bindings into a separate patch.
>> - Add a new entry to the MAINTAINERS file.
>>
>> MAINTAINERS | 8 +
>> drivers/media/i2c/Kconfig | 8 +
>> drivers/media/i2c/Makefile | 1 +
>> drivers/media/i2c/ov7740.c | 1220 ++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 1237 insertions(+)
>> create mode 100644 drivers/media/i2c/ov7740.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index aa71ab52fd76..19086a073ae9 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10053,6 +10053,14 @@ S: Maintained
>> F: drivers/media/i2c/ov7670.c
>> F: Documentation/devicetree/bindings/media/i2c/ov7670.txt
>>
>> +OMNIVISION OV7740 SENSOR DRIVER
>> +M: Wenyou Yang <wenyou.yang@microchip.com>
>> +L: linux-media@vger.kernel.org
>> +T: git git://linuxtv.org/media_tree.git
>> +S: Maintained
>> +F: drivers/media/i2c/ov7740.c
>> +F: Documentation/devicetree/bindings/media/i2c/ov7740.txt
>> +
>> ONENAND FLASH DRIVER
>> M: Kyungmin Park <kyungmin.park@samsung.com>
>> L: linux-mtd@lists.infradead.org
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 3c6d6428f525..ac484bb82fae 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -665,6 +665,14 @@ config VIDEO_OV7670
>> OV7670 VGA camera. It currently only works with the M88ALP01
>> controller.
>>
>> +config VIDEO_OV7740
>> + tristate "OmniVision OV7740 sensor support"
>> + depends on I2C && VIDEO_V4L2
>> + depends on MEDIA_CAMERA_SUPPORT
>> + ---help---
>> + This is a Video4Linux2 sensor-level driver for the OmniVision
>> + OV7740 VGA camera sensor.
>> +
>> config VIDEO_OV9650
>> tristate "OmniVision OV9650/OV9652 sensor support"
>> depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 548a9efce966..9b19ec7fcaf4 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -68,6 +68,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
>> obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
>> obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
>> obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
>> +obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
>> obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
>> obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
>> obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
>> diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
>> new file mode 100644
>> index 000000000000..b2ec015bf3f6
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov7740.c
>> @@ -0,0 +1,1220 @@
>> +/*
>> + * Copyright (c) 2017 Microchip Corporation.
>> + *
>> + * 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/delay.h>
>> +#include <linux/gpio.h>
>> +#include <linux/i2c.h>
>> +#include <linux/init.h>
> Do you need init.h?
No, will remove it.
>
>> +#include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-event.h>
>> +#include <media/v4l2-image-sizes.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define REG_OUTSIZE_LSB 0x34
>> +
>> +/* OV7740 register tables */
>> +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
>> +#define REG_BGAIN 0x01 /* blue gain */
>> +#define REG_RGAIN 0x02 /* red gain */
>> +#define REG_GGAIN 0x03 /* green gain */
>> +#define REG_REG04 0x04 /* analog setting, dont change*/
>> +#define REG_BAVG 0x05 /* b channel average */
>> +#define REG_GAVG 0x06 /* g channel average */
>> +#define REG_RAVG 0x07 /* r channel average */
>> +
>> +#define REG_REG0C 0x0C /* filp enable */
>> +#define REG0C_IMG_FLIP 0x80
>> +#define REG0C_IMG_MIRROR 0x40
>> +
>> +#define REG_REG0E 0x0E /* blc line */
>> +#define REG_HAEC 0x0F /* auto exposure cntrl */
>> +#define REG_AEC 0x10 /* auto exposure cntrl */
>> +
>> +#define REG_CLK 0x11 /* Clock control */
>> +#define REG_REG55 0x55 /* Clock PLL DIV/PreDiv */
>> +
>> +#define REG_REG12 0x12
>> +
>> +#define REG_REG13 0x13 /* auto/manual AGC, AEC, Write Balance*/
>> +#define REG13_AEC_EN 0x01
>> +#define REG13_AGC_EN 0x04
>> +
>> +#define REG_REG14 0x14
>> +#define REG_CTRL15 0x15
>> +#define REG15_GAIN_MSB 0x03
>> +
>> +#define REG_REG16 0x16
>> +
>> +#define REG_MIDH 0x1C /* manufacture id byte */
>> +#define REG_MIDL 0x1D /* manufacture id byre */
>> +#define REG_PIDH 0x0A /* Product ID MSB */
>> +#define REG_PIDL 0x0B /* Product ID LSB */
>> +
>> +#define REG_84 0x84 /* lots of stuff */
>> +#define REG_REG38 0x38 /* sub-addr */
>> +
>> +#define REG_AHSTART 0x17 /* Horiz start high bits */
>> +#define REG_AHSIZE 0x18
>> +#define REG_AVSTART 0x19 /* Vert start high bits */
>> +#define REG_AVSIZE 0x1A
>> +#define REG_PSHFT 0x1b /* Pixel delay after HREF */
>> +
>> +#define REG_HOUTSIZE 0x31
>> +#define REG_VOUTSIZE 0x32
>> +#define REG_HVSIZEOFF 0x33
>> +#define REG_REG34 0x34 /* DSP output size H/V LSB*/
>> +
>> +#define REG_ISP_CTRL00 0x80
>> +#define ISPCTRL00_AWB_EN 0x10
>> +#define ISPCTRL00_AWB_GAIN_EN 0x04
>> +
>> +#define REG_YGAIN 0xE2 /* ygain for contrast control */
>> +
>> +#define REG_YBRIGHT 0xE3
>> +#define REG_SGNSET 0xE4
>> +#define SGNSET_YBRIGHT_MASK 0x08
>> +
>> +#define REG_USAT 0xDD
>> +#define REG_VSAT 0xDE
>> +
>> +
>> +struct ov7740 {
>> + struct v4l2_subdev subdev;
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> + struct media_pad pad;
>> +#endif
>> + struct v4l2_mbus_framefmt format;
>> + struct ov7740_pixfmt *fmt; /* Current format */
>> + struct ov7740_framesize *frmsize;
>> + struct regmap *regmap;
>> + struct clk *xvclk;
>> + struct v4l2_ctrl_handler ctrl_handler;
>> + struct {
>> + /* gain cluster */
>> + struct v4l2_ctrl *auto_gain;
>> + struct v4l2_ctrl *gain;
>> + };
>> + struct {
>> + struct v4l2_ctrl *auto_wb;
>> + struct v4l2_ctrl *blue_balance;
>> + struct v4l2_ctrl *red_balance;
>> + };
>> + struct {
>> + struct v4l2_ctrl *hflip;
>> + struct v4l2_ctrl *vflip;
>> + };
>> + struct {
>> + /* exposure cluster */
>> + struct v4l2_ctrl *auto_exposure;
>> + struct v4l2_ctrl *exposure;
>> + };
>> + struct {
>> + /* saturation/hue cluster */
>> + struct v4l2_ctrl *saturation;
>> + struct v4l2_ctrl *hue;
>> + };
>> + struct v4l2_ctrl *brightness;
>> + struct v4l2_ctrl *contrast;
>> +
>> + struct mutex mutex; /* To serialize asynchronus callbacks */
>> + bool streaming; /* Streaming on/off */
>> +
>> + struct gpio_desc *resetb_gpio;
>> + struct gpio_desc *pwdn_gpio;
>> +};
>> +
>> +struct ov7740_pixfmt {
>> + u32 mbus_code;
>> + enum v4l2_colorspace colorspace;
>> + const struct reg_sequence *regs;
>> + u32 reg_num;
>> +};
>> +
>> +struct ov7740_framesize {
>> + u16 width;
>> + u16 height;
>> + const struct reg_sequence *regs;
>> + u32 reg_num;
>> +};
>> +
> Are the register lists below for a particular external clock frequency?
>
>> +static const struct reg_sequence ov7740_vga[] = {
>> + {0x55, 0x40},
>> + {0x11, 0x02},
>> +
>> + {0xd5, 0x10},
>> + {0x0c, 0x12},
>> + {0x0d, 0x34},
>> + {0x17, 0x25},
>> + {0x18, 0xa0},
>> + {0x19, 0x03},
>> + {0x1a, 0xf0},
>> + {0x1b, 0x89},
>> + {0x22, 0x03},
>> + {0x29, 0x18},
>> + {0x2b, 0xf8},
>> + {0x2c, 0x01},
>> + {REG_HOUTSIZE, 0xa0},
>> + {REG_VOUTSIZE, 0xf0},
>> + {0x33, 0xc4},
>> + {REG_OUTSIZE_LSB, 0x0},
>> + {0x35, 0x05},
>> + //{0x36, 0x3f},
> Why?
Sorry, forgot to remove.
>
>> + {0x04, 0x60},
>> + {0x27, 0x80},
>> + {0x3d, 0x0f},
>> + {0x3e, 0x80},
>> + {0x3f, 0x40},
>> + {0x40, 0x7f},
>> + {0x41, 0x6a},
>> + {0x42, 0x29},
>> + {0x44, 0x22},
>> + {0x45, 0x41},
>> + {0x47, 0x02},
>> + {0x49, 0x64},
>> + {0x4a, 0xa1},
>> + {0x4b, 0x40},
>> + {0x4c, 0x1a},
>> + {0x4d, 0x50},
>> + {0x4e, 0x13},
>> + {0x64, 0x00},
>> + {0x67, 0x88},
>> + {0x68, 0x1a},
>> +
>> + {0x14, 0x28},
>> + {0x24, 0x3c},
>> + {0x25, 0x30},
>> + {0x26, 0x72},
>> + {0x50, 0x97},
>> + {0x51, 0x1f},
>> + {0x52, 0x00},
>> + {0x53, 0x00},
>> + {0x20, 0x00},
>> + {0x21, 0xcf},
>> + {0x50, 0x4b},
>> + {0x38, 0x14},
>> + {0xe9, 0x00},
>> + {0x56, 0x55},
>> + {0x57, 0xff},
>> + {0x58, 0xff},
>> + {0x59, 0xff},
>> + {0x5f, 0x04},
>> + {0xec, 0x00},
>> + {0x13, 0xff},
>> +
>> + //{0x80,0x7f},
>> + {0x81, 0x3f},
>> + {0x82, 0x32},
>> + //{0x83, 0x01},
> Yes, but why?
>
>> + {0x38, 0x11},
>> + {0x84, 0x70},
>> + {0x85, 0x00},
>> + {0x86, 0x03},
>> + {0x87, 0x01},
>> + {0x88, 0x05},
>> + {0x89, 0x30},
>> + {0x8d, 0x30},
>> + {0x8f, 0x85},
>> + {0x93, 0x30},
>> + {0x95, 0x85},
>> + {0x99, 0x30},
>> + {0x9b, 0x85},
>> +
>> + {0x9c, 0x08},
>> + {0x9d, 0x12},
>> + {0x9e, 0x23},
>> + {0x9f, 0x45},
>> + {0xa0, 0x55},
>> + {0xa1, 0x64},
>> + {0xa2, 0x72},
>> + {0xa3, 0x7f},
>> + {0xa4, 0x8b},
>> + {0xa5, 0x95},
>> + {0xa6, 0xa7},
>> + {0xa7, 0xb5},
>> + {0xa8, 0xcb},
>> + {0xa9, 0xdd},
>> + {0xaa, 0xec},
>> + {0xab, 0x1a},
>> +
>> + {0xce, 0x78},
>> + {0xcf, 0x6e},
>> + {0xd0, 0x0a},
>> + {0xd1, 0x0c},
>> + {0xd2, 0x84},
>> + {0xd3, 0x90},
>> + {0xd4, 0x1e},
>> +
>> + {0x5a, 0x24},
>> + {0x5b, 0x1f},
>> + {0x5c, 0x88},
>> + {0x5d, 0x60},
>> +
>> + {0xac, 0x6e},
>> + {0xbe, 0xff},
>> + {0xbf, 0x00},
>> +
>> + {0x0f, 0x1d},
>> + {0x0f, 0x1f},
>> +};
>> +
>> +static struct ov7740_framesize ov7740_framesizes[] = {
> const?
>
> Same for a number of structs below.
Yes, will add const.
>
>> + {
>> + .width = VGA_WIDTH,
>> + .height = VGA_HEIGHT,
>> + .regs = ov7740_vga,
>> + .reg_num = ARRAY_SIZE(ov7740_vga),
>> + },
>> +};
>> +
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> +static int ov7740_get_register(struct v4l2_subdev *sd,
>> + struct v4l2_dbg_register *reg)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct regmap *regmap = ov7740->regmap;
>> + unsigned int val = 0;
>> + int ret;
>> +
>> + ret = regmap_read(regmap, reg->reg & 0xff, &val);
>> + reg->val = val;
>> + reg->size = 1;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_register(struct v4l2_subdev *sd,
>> + const struct v4l2_dbg_register *reg)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct regmap *regmap = ov7740->regmap;
>> +
>> + regmap_write(regmap, reg->reg & 0xff, reg->val & 0xff);
>> +
>> + return 0;
>> +}
>> +#endif
>> +
>> +static int ov7740_set_power(struct v4l2_subdev *sd, int on)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +
>> + if (ov7740->pwdn_gpio)
>> + gpiod_direction_output(ov7740->pwdn_gpio, !on);
>> + if (on && ov7740->resetb_gpio) {
>> + gpiod_set_value(ov7740->resetb_gpio, 1);
>> + usleep_range(500, 1000);
>> + gpiod_set_value(ov7740->resetb_gpio, 0);
>> + usleep_range(3000, 5000);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = {
>> + .log_status = v4l2_ctrl_subdev_log_status,
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> + .g_register = ov7740_get_register,
>> + .s_register = ov7740_set_register,
>> +#endif
>> + .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
>> + .unsubscribe_event = v4l2_event_subdev_unsubscribe,
>> +};
>> +
>> +static int ov7740_set_white_balance(struct ov7740 *ov7740, int awb)
>> +{
>> + struct regmap *regmap = ov7740->regmap;
>> + unsigned int value;
>> + int ret;
>> +
>> + ret = regmap_read(regmap, REG_ISP_CTRL00, &value);
>> + if (!ret) {
>> + if (awb)
>> + value |= (ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
>> + else
>> + value &= ~(ISPCTRL00_AWB_EN | ISPCTRL00_AWB_GAIN_EN);
>> + ret = regmap_write(regmap, REG_ISP_CTRL00, value);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (!awb) {
>> + ret = regmap_write(regmap, REG_BGAIN,
>> + ov7740->blue_balance->val);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_write(regmap, REG_RGAIN, ov7740->red_balance->val);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_saturation(struct regmap *regmap, int value)
>> +{
>> + int ret;
>> +
>> + ret = regmap_write(regmap, REG_USAT, (unsigned char)value);
> ret is not checked.
Will add the check.
>
>> + ret = regmap_write(regmap, REG_VSAT, (unsigned char)value);
>> +
>> + return ret;
>> +}
>> +
>> +static int ov7740_set_gain(struct regmap *regmap, int value)
>> +{
>> + int ret;
>> +
>> + ret = regmap_write(regmap, REG_GAIN, value & 0xff);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_update_bits(regmap, REG_CTRL15,
>> + REG15_GAIN_MSB, (value >> 8) & 0x3);
>> + if (!ret)
>> + ret = regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
>> +
>> + return ret;
>> +}
>> +
>> +static int ov7740_set_autogain(struct regmap *regmap, int value)
>> +{
>> + unsigned int reg;
>> + int ret;
>> +
>> + ret = regmap_read(regmap, REG_REG13, ®);
>> + if (ret)
>> + return ret;
>> + if (value)
>> + reg |= REG13_AGC_EN;
>> + else
>> + reg &= ~REG13_AGC_EN;
>> + return regmap_write(regmap, REG_REG13, reg);
>> +}
>> +
>> +static int ov7740_set_brightness(struct regmap *regmap, int value)
>> +{
>> + /* Turn off AEC/AGC */
>> + regmap_update_bits(regmap, REG_REG13, REG13_AEC_EN, 0);
>> + regmap_update_bits(regmap, REG_REG13, REG13_AGC_EN, 0);
>> +
>> + if (value >= 0) {
>> + regmap_write(regmap, REG_YBRIGHT, (unsigned char)value);
>> + regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 0);
>> + } else{
>> + regmap_write(regmap, REG_YBRIGHT, (unsigned char)(-value));
>> + regmap_update_bits(regmap, REG_SGNSET, SGNSET_YBRIGHT_MASK, 1);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_contrast(struct regmap *regmap, int value)
>> +{
>> + return regmap_write(regmap, REG_YGAIN, (unsigned char)value);
>> +}
>> +
>> +static int ov7740_get_gain(struct ov7740 *ov7740, struct v4l2_ctrl *ctrl)
>> +{
>> + struct regmap *regmap = ov7740->regmap;
>> + unsigned int value0, value1;
>> + int ret;
>> +
>> + if (!ctrl->val)
>> + return 0;
>> +
>> + ret = regmap_read(regmap, REG_GAIN, &value0);
>> + if (ret)
>> + return ret;
>> + ret = regmap_read(regmap, REG_CTRL15, &value1);
>> + if (ret)
>> + return ret;
>> +
>> + ov7740->gain->val = (value1 << 8) | (value0 & 0xff);
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_exp(struct regmap *regmap, int value)
>> +{
>> + int ret;
>> +
>> + /* Turn off AEC/AGC */
>> + ret = regmap_update_bits(regmap, REG_REG13,
>> + REG13_AEC_EN | REG13_AGC_EN, 0);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_write(regmap, REG_AEC, (unsigned char)value);
>> + if (ret)
>> + return ret;
>> +
>> + return regmap_write(regmap, REG_HAEC, (unsigned char)(value >> 8));
>> +}
>> +
>> +static int ov7740_set_autoexp(struct regmap *regmap,
>> + enum v4l2_exposure_auto_type value)
>> +{
>> + unsigned int reg;
>> + int ret;
>> +
>> + ret = regmap_read(regmap, REG_REG13, ®);
>> + if (!ret) {
>> + if (value == V4L2_EXPOSURE_AUTO)
>> + reg |= (REG13_AEC_EN | REG13_AGC_EN);
>> + else
>> + reg &= ~(REG13_AEC_EN | REG13_AGC_EN);
>> + ret = regmap_write(regmap, REG_REG13, reg);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +
>> +static int ov7740_get_volatile_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> + struct ov7740 *ov7740 = container_of(ctrl->handler,
>> + struct ov7740, ctrl_handler);
>> + int ret;
>> +
>> + switch (ctrl->id) {
>> + case V4L2_CID_AUTOGAIN:
>> + ret = ov7740_get_gain(ov7740, ctrl);
>> + break;
>> + default:
>> + ret = -EINVAL;
>> + break;
>> + }
>> + return ret;
>> +}
>> +
>> +static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> + struct ov7740 *ov7740 = container_of(ctrl->handler,
>> + struct ov7740, ctrl_handler);
>> + struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
>> + struct regmap *regmap = ov7740->regmap;
>> + int ret;
>> + u8 val = 0;
>> +
>> + if (pm_runtime_get_if_in_use(&client->dev) <= 0)
>> + return 0;
>> +
>> + switch (ctrl->id) {
>> + case V4L2_CID_AUTO_WHITE_BALANCE:
>> + ret = ov7740_set_white_balance(ov7740, ctrl->val);
>> + break;
>> + case V4L2_CID_SATURATION:
>> + ret = ov7740_set_saturation(regmap, ctrl->val);
>> + break;
>> + case V4L2_CID_BRIGHTNESS:
>> + ret = ov7740_set_brightness(regmap, ctrl->val);
>> + break;
>> + case V4L2_CID_CONTRAST:
>> + ret = ov7740_set_contrast(regmap, ctrl->val);
>> + break;
>> + case V4L2_CID_VFLIP:
>> + ret = regmap_update_bits(regmap, REG_REG0C,
>> + REG0C_IMG_FLIP, val);
>> + break;
>> + case V4L2_CID_HFLIP:
>> + val = ctrl->val ? REG0C_IMG_MIRROR : 0x00;
>> + ret = regmap_update_bits(regmap, REG_REG0C,
>> + REG0C_IMG_MIRROR, val);
>> + break;
>> + case V4L2_CID_AUTOGAIN:
>> + if (!ctrl->val)
>> + return ov7740_set_gain(regmap, ov7740->gain->val);
>> +
>> + ret = ov7740_set_autogain(regmap, ctrl->val);
>> + break;
>> +
>> + case V4L2_CID_EXPOSURE_AUTO:
>> + if (ctrl->val == V4L2_EXPOSURE_MANUAL)
>> + return ov7740_set_exp(regmap, ov7740->exposure->val);
>> +
>> + ret = ov7740_set_autoexp(regmap, ctrl->val);
>> + break;
>> + default:
>> + ret = -EINVAL;
>> + break;
>> + }
>> +
>> + pm_runtime_put(&client->dev);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops ov7740_ctrl_ops = {
>> + .g_volatile_ctrl = ov7740_get_volatile_ctrl,
>> + .s_ctrl = ov7740_set_ctrl,
>> +};
>> +
>> +static int ov7740_start_streaming(struct ov7740 *ov7740)
>> +{
>> + int ret;
>> +
>> + /* Apply customized values from user */
>> + ret = __v4l2_ctrl_handler_setup(ov7740->subdev.ctrl_handler);
>> + if (ret)
>> + return ret;
> return __v4l2_ctrl_handler_setup(...);
Right.
>
> You could as well move this to where it's being called, there's a single
> instance.
>
> That would make it easier to see that the sensor is probably starting the
> stream based on its power on or mode register list. Shouldn't you set the
> controls before starting streaming?
>
>> +
>> + return 0;
>> +}
>> +static int ov7740_set_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct i2c_client *client = v4l2_get_subdevdata(sd);
>> + int ret = 0;
>> +
>> + mutex_lock(&ov7740->mutex);
>> + if (ov7740->streaming == enable) {
>> + mutex_unlock(&ov7740->mutex);
>> + return 0;
>> + }
>> +
>> + if (enable) {
>> + ret = pm_runtime_get_sync(&client->dev);
>> + if (ret < 0) {
>> + pm_runtime_put_noidle(&client->dev);
>> + goto err_unlock;
>> + }
>> +
>> + ret = ov7740_start_streaming(ov7740);
>> + if (ret)
>> + goto err_rpm_put;
>> + } else {
>> + pm_runtime_put(&client->dev);
>> + }
>> +
>> + ov7740->streaming = enable;
>> +
>> + mutex_unlock(&ov7740->mutex);
>> + return ret;
>> +
>> +err_rpm_put:
>> + pm_runtime_put(&client->dev);
>> +err_unlock:
>> + mutex_unlock(&ov7740->mutex);
>> + return ret;
>> +}
>> +
>> +static int ov7740_get_parm(struct v4l2_subdev *sd,
>> + struct v4l2_streamparm *parms)
>> +{
>> + struct v4l2_captureparm *cp = &parms->parm.capture;
>> + struct v4l2_fract *tpf = &cp->timeperframe;
>> +
>> + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> + return -EINVAL;
>> +
>> + memset(cp, 0, sizeof(struct v4l2_captureparm));
>> + cp->capability = V4L2_CAP_TIMEPERFRAME;
>> +
>> + tpf->numerator = 1;
>> + tpf->denominator = 60;
> You implement also [gs]_frame_interval in pad ops. Do you have a need for
> this?
No, don't need.
>
> It'd be better if sensor drivers just implemented one of these APIs to
> select the frame interval ([gs]_frame_interval), not both.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_parm(struct v4l2_subdev *sd,
>> + struct v4l2_streamparm *parms)
>> +{
>> + struct v4l2_captureparm *cp = &parms->parm.capture;
>> + struct v4l2_fract *tpf = &cp->timeperframe;
>> +
>> + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> + return -EINVAL;
>> + if (cp->extendedmode != 0)
>> + return -EINVAL;
>> +
>> + cp->capability = V4L2_CAP_TIMEPERFRAME;
>> +
>> + tpf->numerator = 1;
>> + tpf->denominator = 60;
>> +
>> + return 0;
>> +}
>> +
>> +static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = {
> const?
>
>> + .s_stream = ov7740_set_stream,
>> + .s_parm = ov7740_set_parm,
>> + .g_parm = ov7740_get_parm,
>> +};
>> +
>> +static const struct reg_sequence ov7740_format_yuyv[] = {
>> + {0x12, 0x00},
>> + {0x36, 0x3f},
>> + {0x80, 0x7f},
>> + {0x83, 0x01},
>> +};
>> +
>> +static const struct reg_sequence ov7740_format_bggr8[] = {
>> + {0x36, 0x2f},
>> + {0x80, 0x01},
>> + {0x83, 0x04},
>> +};
>> +
>> +static struct ov7740_pixfmt ov7740_formats[] = {
>> + {
>> + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
>> + .colorspace = V4L2_COLORSPACE_SRGB,
>> + .regs = ov7740_format_yuyv,
>> + .reg_num = ARRAY_SIZE(ov7740_format_yuyv),
>> + },
>> + {
>> + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
>> + .colorspace = V4L2_COLORSPACE_SRGB,
>> + .regs = ov7740_format_bggr8,
>> + .reg_num = ARRAY_SIZE(ov7740_format_bggr8),
>> + }
>> +};
>> +#define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats)
>> +
>> +static int ov7740_enum_mbus_code(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> + if (code->pad || code->index >= N_OV7740_FMTS)
>> + return -EINVAL;
>> +
>> + code->code = ov7740_formats[code->index].mbus_code;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_enum_frame_interval(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_frame_interval_enum *fie)
>> +{
>> + if (fie->pad)
>> + return -EINVAL;
>> +
>> + if (fie->index >= 1)
>> + return -EINVAL;
>> +
>> + if ((fie->width != VGA_WIDTH) || (fie->height != VGA_HEIGHT))
>> + return -EINVAL;
>> +
>> + fie->interval.numerator = 1;
>> + fie->interval.denominator = 60;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_enum_frame_size(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> + if (fse->pad)
>> + return -EINVAL;
>> +
>> + if (fse->index > 0)
>> + return -EINVAL;
>> +
>> + fse->min_width = fse->max_width = VGA_WIDTH;
>> + fse->min_height = fse->max_height = VGA_HEIGHT;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_try_fmt_internal(struct v4l2_subdev *sd,
>> + struct v4l2_mbus_framefmt *fmt,
>> + struct ov7740_pixfmt **ret_fmt,
>> + struct ov7740_framesize **ret_frmsize)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct ov7740_framesize *fsize = &ov7740_framesizes[0];
>> + int index, i;
>> +
>> + for (index = 0; index < N_OV7740_FMTS; index++) {
>> + if (ov7740_formats[index].mbus_code == fmt->code)
>> + break;
>> + }
>> + if (index >= N_OV7740_FMTS) {
>> + /* default to first format */
>> + index = 0;
>> + fmt->code = ov7740_formats[0].mbus_code;
>> + }
>> + if (ret_fmt != NULL)
>> + *ret_fmt = ov7740_formats + index;
>> +
>> + for (i = 0; i < ARRAY_SIZE(ov7740_framesizes); i++) {
>> + if ((fsize->width >= fmt->width) &&
>> + (fsize->height >= fmt->height)) {
>> + fmt->width = fsize->width;
>> + fmt->height = fsize->height;
>> + break;
>> + }
>> +
>> + fsize++;
>> + }
>> +
>> + if (ret_frmsize != NULL)
>> + *ret_frmsize = fsize;
>> +
>> + fmt->field = V4L2_FIELD_NONE;
>> + fmt->colorspace = ov7740_formats[index].colorspace;
>> +
>> + ov7740->format = *fmt;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_set_fmt(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_format *format)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct ov7740_pixfmt *ovfmt;
>> + struct ov7740_framesize *fsize;
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> + struct v4l2_mbus_framefmt *mbus_fmt;
>> +#endif
>> + int ret;
>> +
>> + mutex_lock(&ov7740->mutex);
>> + if (format->pad) {
>> + ret = -EINVAL;
>> + goto error;
>> + }
>> +
>> + if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
>> + ret = ov7740_try_fmt_internal(sd, &format->format, NULL, NULL);
>> + if (ret)
>> + goto error;
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> + mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
>> + *mbus_fmt = format->format;
>> +
>> + mutex_unlock(&ov7740->mutex);
>> + return 0;
>> +#else
>> + ret = -ENOTTY;
>> + goto error;
>> +#endif
>> + }
>> +
>> + ret = ov7740_try_fmt_internal(sd, &format->format, &ovfmt, &fsize);
>> + if (ret)
>> + goto error;
>> +
>> + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> + regmap_multi_reg_write(ov7740->regmap,
>> + ovfmt->regs, ovfmt->reg_num);
> What if streaming is started without calling set_fmt after the device is
> powered on?
>
> These should probably be written when streaming is started.
Right, add the default format initialization function.
>
>> +
>> + regmap_multi_reg_write(ov7740->regmap,
>> + fsize->regs, fsize->reg_num);
>> + }
>> +
>> + ov7740->fmt = ovfmt;
>> +
>> + mutex_unlock(&ov7740->mutex);
>> + return 0;
>> +
>> +error:
>> + mutex_unlock(&ov7740->mutex);
>> + return ret;
>> +}
>> +
>> +static int ov7740_get_fmt(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_format *format)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> + struct v4l2_mbus_framefmt *mbus_fmt;
>> +#endif
>> + int ret = 0;
>> +
>> + mutex_lock(&ov7740->mutex);
>> + if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> + mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
>> + format->format = *mbus_fmt;
>> + ret = 0;
>> +#else
>> + ret = -ENOTTY;
>> +#endif
>> + } else {
>> + format->format = ov7740->format;
>> + }
>> + mutex_unlock(&ov7740->mutex);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct v4l2_subdev_pad_ops ov7740_subdev_pad_ops = {
>> + .enum_frame_interval = ov7740_enum_frame_interval,
>> + .enum_frame_size = ov7740_enum_frame_size,
>> + .enum_mbus_code = ov7740_enum_mbus_code,
>> + .get_fmt = ov7740_get_fmt,
>> + .set_fmt = ov7740_set_fmt,
>> +};
>> +
>> +static struct v4l2_subdev_ops ov7740_subdev_ops = {
>> + .core = &ov7740_subdev_core_ops,
>> + .video = &ov7740_subdev_video_ops,
>> + .pad = &ov7740_subdev_pad_ops,
>> +};
>> +
>> +static void ov7740_get_default_format(struct v4l2_subdev *sd,
>> + struct v4l2_mbus_framefmt *format)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +
>> + format->width = ov7740->frmsize->width;
>> + format->height = ov7740->frmsize->height;
>> + format->colorspace = ov7740->fmt->colorspace;
>> + format->code = ov7740->fmt->mbus_code;
>> + format->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static int ov7740_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> + struct v4l2_mbus_framefmt *format =
>> + v4l2_subdev_get_try_format(sd, fh->pad, 0);
>> +
>> + mutex_lock(&ov7740->mutex);
>> + ov7740_get_default_format(sd, format);
>> + mutex_unlock(&ov7740->mutex);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct v4l2_subdev_internal_ops ov7740_subdev_internal_ops = {
>> + .open = ov7740_open,
>> +};
>> +#endif
>> +
>> +static int ov7740_probe_dt(struct i2c_client *client,
>> + struct ov7740 *ov7740)
>> +{
>> + ov7740->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
>> + GPIOD_OUT_LOW);
>> + if (IS_ERR(ov7740->resetb_gpio)) {
>> + dev_info(&client->dev, "can't get %s GPIO\n", "reset");
>> + return PTR_ERR(ov7740->resetb_gpio);
>> + }
>> +
>> + ov7740->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
>> + GPIOD_OUT_HIGH);
>> + if (IS_ERR(ov7740->pwdn_gpio)) {
>> + dev_info(&client->dev, "can't get %s GPIO\n", "powerdown");
>> + return PTR_ERR(ov7740->pwdn_gpio);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_detect(struct ov7740 *ov7740)
>> +{
>> + struct regmap *regmap = ov7740->regmap;
>> + unsigned int midh, midl, pidh, pidl;
>> + int ret;
>> +
>> + ret = regmap_read(regmap, REG_MIDH, &midh);
>> + if (ret)
>> + return ret;
>> + if (midh != 0x7f)
>> + return -ENODEV;
>> +
>> + ret = regmap_read(regmap, REG_MIDL, &midl);
>> + if (ret)
>> + return ret;
>> + if (midl != 0xa2)
>> + return -ENODEV;
>> +
>> + ret = regmap_read(regmap, REG_PIDH, &pidh);
>> + if (ret)
>> + return ret;
>> + if (pidh != 0x77)
>> + return -ENODEV;
>> +
>> + ret = regmap_read(regmap, REG_PIDL, &pidl);
>> + if (ret)
>> + return ret;
>> + if ((pidl != 0x40) && (pidl != 0x41) && (pidl != 0x42))
>> + return -ENODEV;
>> +
>> + return 0;
>> +}
>> +
>> +static int ov7740_init_controls(struct ov7740 *ov7740)
>> +{
>> + struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev);
>> + struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler;
>> + int ret;
>> +
>> + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ctrl_hdlr->lock = &ov7740->mutex;
>> + ov7740->auto_wb = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_AUTO_WHITE_BALANCE,
>> + 0, 1, 1, 1);
>> + ov7740->blue_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_BLUE_BALANCE,
>> + 0, 0xff, 1, 0x80);
>> + ov7740->red_balance = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_RED_BALANCE,
>> + 0, 0xff, 1, 0x80);
>> +
>> + ov7740->brightness = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_BRIGHTNESS,
>> + -255, 255, 1, 0);
>> + ov7740->contrast = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_CONTRAST,
>> + 0, 127, 1, 0x20);
>> + ov7740->saturation = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_SATURATION, 0, 256, 1, 0x80);
>> + ov7740->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_HFLIP, 0, 1, 1, 0);
>> + ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_VFLIP, 0, 1, 1, 0);
>> + ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_GAIN, 0, 1023, 1, 500);
>> + ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
>> + ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
>> + V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
>> + ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr,
>> + &ov7740_ctrl_ops,
>> + V4L2_CID_EXPOSURE_AUTO,
>> + V4L2_EXPOSURE_MANUAL, 0,
>> + V4L2_EXPOSURE_AUTO);
>> +
>> + ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> + ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +
>> + v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false);
>> + v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true);
>> + v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure,
>> + V4L2_EXPOSURE_MANUAL, false);
>> + v4l2_ctrl_cluster(2, &ov7740->hflip);
>> +
>> + ret = v4l2_ctrl_handler_setup(ctrl_hdlr);
>> + if (ret) {
>> + dev_err(&client->dev, "%s control init failed (%d)\n",
>> + __func__, ret);
>> + goto error;
>> + }
>> +
>> + ov7740->subdev.ctrl_handler = ctrl_hdlr;
>> + return 0;
>> +
>> +error:
>> + v4l2_ctrl_handler_free(ctrl_hdlr);
>> + mutex_destroy(&ov7740->mutex);
>> + return ret;
>> +}
>> +
>> +static void ov7740_free_controls(struct ov7740 *ov7740)
>> +{
>> + v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
>> + mutex_destroy(&ov7740->mutex);
>> +}
>> +
>> +#define OV7740_MAX_REGISTER 0xff
>> +static const struct regmap_config ov7740_regmap_config = {
>> + .reg_bits = 8,
>> + .val_bits = 8,
>> + .max_register = OV7740_MAX_REGISTER,
>> +};
>> +
>> +static int ov7740_probe(struct i2c_client *client,
>> + const struct i2c_device_id *id)
>> +{
>> + struct ov7740 *ov7740;
>> + struct v4l2_subdev *sd;
>> + int ret;
>> +
>> + if (!i2c_check_functionality(client->adapter,
>> + I2C_FUNC_SMBUS_BYTE_DATA)) {
>> + dev_err(&client->dev,
>> + "OV7740: I2C-Adapter doesn't support SMBUS\n");
>> + return -EIO;
>> + }
>> +
>> + ov7740 = devm_kzalloc(&client->dev, sizeof(*ov7740), GFP_KERNEL);
>> + if (!ov7740)
>> + return -ENOMEM;
>> +
>> + ov7740->xvclk = devm_clk_get(&client->dev, "xvclk");
>> + if (IS_ERR(ov7740->xvclk)) {
>> + ret = PTR_ERR(ov7740->xvclk);
>> + dev_err(&client->dev,
>> + "OV7740: fail to get xvclk: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = ov7740_probe_dt(client, ov7740);
>> + if (ret)
>> + return ret;
>> +
>> + ret = clk_prepare_enable(ov7740->xvclk);
>> + if (ret)
>> + return ret;
>> +
>> + ov7740->regmap = devm_regmap_init_i2c(client, &ov7740_regmap_config);
>> + if (IS_ERR(ov7740->regmap)) {
>> + ret = PTR_ERR(ov7740->regmap);
>> + dev_err(&client->dev, "Failed to allocate register map: %d\n",
>> + ret);
>> + goto error_regmap_init;
>> + }
>> +
>> + sd = &ov7740->subdev;
>> + client->flags |= I2C_CLIENT_SCCB;
>> + v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops);
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> + sd->internal_ops = &ov7740_subdev_internal_ops;
>> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +#endif
>> +
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> + ov7740->pad.flags = MEDIA_PAD_FL_SOURCE;
>> + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> + ret = media_entity_pads_init(&sd->entity, 1, &ov7740->pad);
>> + if (ret)
>> + goto error_regmap_init;
>> +#endif
>> +
>> + ov7740_set_power(sd, 1);
>> +
>> + ret = ov7740_detect(ov7740);
>> + if (ret)
>> + goto error_detect;
>> +
>> + mutex_init(&ov7740->mutex);
>> +
>> + ret = ov7740_init_controls(ov7740);
>> + if (ret)
>> + goto error_init_controls;
>> +
>> + v4l_info(client, "chip found @ 0x%02x (%s)\n",
>> + client->addr << 1, client->adapter->name);
>> +
>> + ov7740->fmt = &ov7740_formats[0];
>> + ov7740->frmsize = &ov7740_framesizes[0];
>> +
>> + ov7740_get_default_format(sd, &ov7740->format);
>> +
>> + ret = v4l2_async_register_subdev(sd);
>> + if (ret)
>> + goto error_async_register;
>> +
>> + pm_runtime_get_noresume(&client->dev);
>> + pm_runtime_set_active(&client->dev);
>> + pm_runtime_enable(&client->dev);
>> + pm_runtime_put(&client->dev);
> You can use pm_runtime_idle() to replace get_noresume + put.
>
>> +
>> + return 0;
>> +
>> +error_async_register:
>> + v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
>> +error_init_controls:
>> + ov7740_free_controls(ov7740);
>> + ov7740_set_power(sd, 0);
>> +error_detect:
> ov7740_set_power(sd, 0) here?
Right.
>
>> + media_entity_cleanup(&ov7740->subdev.entity);
>> +error_regmap_init:
>> + clk_disable_unprepare(ov7740->xvclk);
>> +
>> + return ret;
>> +}
>> +
>> +static int ov7740_remove(struct i2c_client *client)
>> +{
>> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +
>> + clk_disable_unprepare(ov7740->xvclk);
>> + mutex_destroy(&ov7740->mutex);
>> + v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> + media_entity_cleanup(&ov7740->subdev.entity);
>> +#endif
>> + v4l2_async_unregister_subdev(sd);
>> + ov7740_free_controls(ov7740);
>> +
>> + pm_runtime_get_sync(&client->dev);
>> + pm_runtime_disable(&client->dev);
>> + pm_runtime_set_suspended(&client->dev);
>> + pm_runtime_put_noidle(&client->dev);
> ov7740_set_power(sd, 0) if the device was suspended.
>
> I'd think the smiapp driver does this generally right. Think about how this
> works if runtime PM is disabled.
Yes. I will improve them in the future.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused ov7740_runtime_suspend(struct device *dev)
>> +{
>> + struct i2c_client *client = to_i2c_client(dev);
>> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +
>> + clk_disable_unprepare(ov7740->xvclk);
>> + ov7740_set_power(sd, 0);
> How about moving clock control to ov7740_set_power()? Clock and the GPIOs
> appear to be always controller together, aren't they?
Yes, it is better.
Will get them together.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused ov7740_runtime_resume(struct device *dev)
>> +{
>> + struct i2c_client *client = to_i2c_client(dev);
>> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> + struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
>> +
>> + ov7740_set_power(sd, 1);
>> + return clk_prepare_enable(ov7740->xvclk);
>> +}
>> +
>> +static const struct i2c_device_id ov7740_id[] = {
>> + { "ov7740", 0 },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ov7740_id);
>> +
>> +static const struct dev_pm_ops ov7740_pm_ops = {
>> + SET_RUNTIME_PM_OPS(ov7740_runtime_suspend, ov7740_runtime_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id ov7740_of_match[] = {
>> + {.compatible = "ovti,ov7740", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ov7740_of_match);
>> +
> Extra newline.
>
>> +
>> +static struct i2c_driver ov7740_i2c_driver = {
>> + .driver = {
>> + .name = "ov7740",
>> + .pm = &ov7740_pm_ops,
>> + .of_match_table = of_match_ptr(ov7740_of_match),
>> + },
>> + .probe = ov7740_probe,
>> + .remove = ov7740_remove,
>> + .id_table = ov7740_id,
>> +};
>> +module_i2c_driver(ov7740_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("The V4L2 driver for Omnivision 7740 sensor");
>> +MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
>> +MODULE_LICENSE("GPL v2");
Best Regards,
Wenyou Yang
^ permalink raw reply
* Re: [PATCH 1/3] dt-bindings: Add optional nvmem MAC address bindings to ti,wlink-st
From: Marcel Holtmann @ 2017-12-04 6:50 UTC (permalink / raw)
To: David Lechner
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
Gustavo F. Padovan, Johan Hedberg, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1512357682-8911-2-git-send-email-david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
Hi David,
> This adds optional nvmem consumer properties to the ti,wlink-st device tree
> bindings to allow specifying the Bluetooth MAC address.
>
> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
> ---
> Documentation/devicetree/bindings/net/ti,wilink-st.txt | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/ti,wilink-st.txt b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
> index 1649c1f..24eb897 100644
> --- a/Documentation/devicetree/bindings/net/ti,wilink-st.txt
> +++ b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
> @@ -32,6 +32,8 @@ Optional properties:
> See ../clocks/clock-bindings.txt for details.
> - clock-names : Must include the following entry:
> "ext_clock" (External clock provided to the TI combo chip).
> + - nvmem-cells: phandle to nvmem data cell that contains a MAC address
> + - nvmem-cell-names: "mac-address" (required when nvmem-cells is specified)
lets call these “bt-address” since they are not really MAC addresses. They have special formatting etc. Also I prefer that the actual format on how they are stored is described. See Documentation/devicetree/bindings/net/bluetooth.txt for an example.
> Example:
>
> @@ -43,5 +45,7 @@ Example:
> enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
> clocks = <&clk32k_wl18xx>;
> clock-names = "ext_clock";
> + nvmem-cells: <&mac_address>
> + nvmem-cell-names "mac-address"
> };
> };
Regards
Marcel
^ permalink raw reply
* Re: [PATCH 3/3] bluetooth: hci_ll: Add optional nvmem MAC address source
From: Marcel Holtmann @ 2017-12-04 6:48 UTC (permalink / raw)
To: David Lechner
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
Gustavo F. Padovan, Johan Hedberg, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1512357682-8911-4-git-send-email-david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
Hi David,
> This adds an optional nvmem consumer to get a MAC address from an external
> source. The MAC address is then set in the Bluetooth chip after the
> firmware has been loaded.
>
> This has been tested working with a TI CC2560A chip (in a LEGO MINDSTORMS
> EV3).
>
> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
> ---
> drivers/bluetooth/hci_ll.c | 33 +++++++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
> index 974a788..dd3b5c2 100644
> --- a/drivers/bluetooth/hci_ll.c
> +++ b/drivers/bluetooth/hci_ll.c
> @@ -53,10 +53,12 @@
> #include <net/bluetooth/bluetooth.h>
> #include <net/bluetooth/hci_core.h>
> #include <linux/gpio/consumer.h>
> +#include <linux/nvmem-consumer.h>
>
> #include "hci_uart.h"
>
> /* Vendor-specific HCI commands */
> +#define HCI_VS_WRITE_BD_ADDR 0xfc06
> #define HCI_VS_UPDATE_UART_HCI_BAUDRATE 0xff36
the first patch should to add support for hdev->set_bdaddr. If the hardware supports changing the BD_ADDR, then it should also be exposed via Set Public Address mgmt command. For this it is required that the writing of the BD_ADDR is non-persistent over power cycles.
>
> /* HCILL commands */
> @@ -89,6 +91,7 @@ struct ll_device {
> struct serdev_device *serdev;
> struct gpio_desc *enable_gpio;
> struct clk *ext_clk;
> + u8 *bdaddr;
I would prefer if we just store it as bdaddr_t. That will work just fine when you created the set_bdaddr helper function that you need for hdev->set_bdaddr anyway.
> };
>
> struct ll_struct {
> @@ -698,6 +701,19 @@ static int ll_setup(struct hci_uart *hu)
> if (err)
> return err;
>
> + /* Set MAC address, if any */
> + if (lldev->bdaddr) {
And here I would really prefer to use bacmp. And example here would be btqcomsmd.c on how it is done.
> + struct sk_buff *skb;
> +
> + skb = __hci_cmd_sync(hu->hdev, HCI_VS_WRITE_BD_ADDR, 6,
> + lldev->bdaddr, HCI_INIT_TIMEOUT);
> + if (IS_ERR(skb))
> + bt_dev_err(hu->hdev, "Failed to set MAC address (%ld)",
> + PTR_ERR(skb));
> + else
> + kfree_skb(skb);
> + }
> +
> /* Operational speed if any */
> if (hu->oper_speed)
> speed = hu->oper_speed;
> @@ -726,6 +742,7 @@ static int hci_ti_probe(struct serdev_device *serdev)
> {
> struct hci_uart *hu;
> struct ll_device *lldev;
> + struct nvmem_cell *bdaddr_cell;
> u32 max_speed = 3000000;
While this is unrelated to this patch, when I seed the u32 speed and the direct use of it in a __hci_cmd_sync, then that is pretty obviously not endian safe. You might really want to fix that as well.
> lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
> @@ -747,6 +764,22 @@ static int hci_ti_probe(struct serdev_device *serdev)
> of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
> hci_uart_set_speeds(hu, 115200, max_speed);
>
> + /* optional MAC address from nvram */
> + bdaddr_cell = nvmem_cell_get(&serdev->dev, "mac-address”);
I prefer really to use the term “bd-address” like we discussed for the btqcomsmd.c driver as well.
> + if (IS_ERR(bdaddr_cell)) {
> + int err = PTR_ERR(bdaddr_cell);
> +
> + if (err != -ENOENT) {
> + if (err != -EPROBE_DEFER)
err != -ENOENT && err != -EPROBE_DEFER
> + dev_err(&serdev->dev,
> + "Failed to get \"mac-address\" nvmem cell\n”);
Also this is at most a warning. And more important, I would actually set the quirk BDADDR_INVALID here so that the device gets flagged as not having a valid address. The Set Public Address mgmt command can then be used from userspace to handle that situation.
> + return err;
> + }
> + } else {
> + lldev->bdaddr = nvmem_cell_read(bdaddr_cell, NULL);
> + nvmem_cell_put(bdaddr_cell);
> + }
> +
> return hci_uart_register_device(hu, &llp);
> }
Regards
Marcel
^ permalink raw reply
* [RFC v2 2/2] dt-bindings: mailbox: Add Xilinx IPI Mailbox
From: Wendy Liang @ 2017-12-04 6:25 UTC (permalink / raw)
To: jassisinghbrar, michal.simek, robh+dt, mark.rutland
Cc: linux-kernel, linux-arm-kernel, devicetree, Wendy Liang
In-Reply-To: <1512368735-27147-1-git-send-email-jliang@xilinx.com>
Xilinx ZynqMP IPI(Inter Processor Interrupt) is a hardware block
in ZynqMP SoC used for the communication between various processor
systems.
Signed-off-by: Wendy Liang <jliang@xilinx.com>
---
.../bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt | 104 +++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt
diff --git a/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt
new file mode 100644
index 0000000..5e270a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt
@@ -0,0 +1,104 @@
+Xilinx IPI Mailbox Controller
+========================================
+
+The Xilinx IPI(Inter Processor Interrupt) mailbox controller is to manage
+messaging between two Xilinx Zynq UltraScale+ MPSoC IPI agents. Each IPI
+agent owns registers used for notification and buffers for message.
+
+ +-------------------------------------+
+ | Xilinx ZynqMP IPI Controller |
+ +-------------------------------------+
+ +--------------------------------------------------+
+ATF | |
+ | |
+ | |
+ +--------------------------+ |
+ | |
+ | |
+ +--------------------------------------------------+
+ +------------------------------------------+
+ | +----------------+ +----------------+ |
+Hardware | | IPI Agent | | IPI Buffers | |
+ | | Registers | | | |
+ | | | | | |
+ | +----------------+ +----------------+ |
+ | |
+ | Xilinx IPI Agent Block |
+ +------------------------------------------+
+
+
+Controller Device Node:
+===========================
+Required properties:
+--------------------
+- compatible: Shall be: "xlnx,zynqmp-ipi-mailbox"
+- reg: IPI buffers address ranges
+- reg-names: Names of the reg resources. It should have:
+ * local_request_region
+ - IPI request msg buffer written by local and read
+ by remote
+ * local_response_region
+ - IPI response msg buffer written by local and read
+ by remote
+ * remote_request_region
+ - IPI request msg buffer written by remote and read
+ by local
+ * remote_response_region
+ - IPI response msg buffer written by remote and read
+ by local
+- #mbox-cells: Shall be 1. It contains:
+ * tx(0) or rx(1) channel
+- xlnx,ipi-ids: Xilinx IPI agent IDs of the two peers of the
+ Xilinx IPI communication channel.
+- interrupt-parent: Phandle for the interrupt controller
+- interrupts: Interrupt information corresponding to the
+ interrupt-names property.
+
+Optional properties:
+--------------------
+- method: The method of accessing the IPI agent registers.
+ Permitted values are: "smc" and "hvc". Default is
+ "smc".
+
+Example:
+===========================
+ /* APU<->RPU0 IPI mailbox controller */
+ ipi_mailbox_rpu0: mailbox@ff90400 {
+ compatible = "xlnx,zynqmp-ipi-mailbox";
+ reg = <0x0 0xff990400 0x0 0x20>,
+ <0x0 0xff990420 0x0 0x20>,
+ <0x0 0xff990080 0x0 0x20>,
+ <0x0 0xff9900a0 0x0 0x20>;
+ reg-names = "local_request_region", "local_response_region",
+ "remote_request_region", "remote_response_region";
+ #mbox-cells = <1>;
+ xlnx-ipi-ids = <0 1>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 29 4>;
+ };
+ /* APU<->RPU1 IPI mailbox controller */
+ ipi_mailbox_rpu1: mailbox@ff990440 {
+ compatible = "xlnx,zynqmp-ipi-mailbox";
+ reg = <0x0 0xff990440 0x0 0x20>,
+ <0x0 0xff990460 0x0 0x20>,
+ <0x0 0xff990280 0x0 0x20>,
+ <0x0 0xff9902a0 0x0 0x20>;
+ reg-names = "local_request_region", "local_response_region",
+ "remote_request_region", "remote_response_region";
+ #mbox-cells = <1>;
+ xlnx-ipi-ids = <0 2>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 29 4>;
+ };
+ rpu0 {
+ ...
+ mboxes = <&ipi_mailbox_rpu0 0>,
+ <&ipi_mailbox_rpu0 1>;
+ mbox-names = "tx", "rx";
+ };
+ rpu1 {
+ ...
+ mboxes = <&ipi_mailbox_rpu1 0>,
+ <&ipi_mailbox_rpu1 1>;
+ mbox-names = "tx", "rx";
+ };
--
2.7.4
^ permalink raw reply related
* [RFC v2 1/2] mailbox: ZynqMP IPI mailbox controller
From: Wendy Liang @ 2017-12-04 6:25 UTC (permalink / raw)
To: jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w,
michal.simek-gjFFaj9aHVfQT0dZR+AlfA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Wendy Liang
In-Reply-To: <1512368735-27147-1-git-send-email-jliang-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
This patch is to introduce ZynqMP IPI mailbox controller driver
to use the ZynqMP IPI block as mailboxes.
Signed-off-by: Wendy Liang <jliang-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
---
drivers/mailbox/Kconfig | 8 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/zynqmp-ipi-mailbox.c | 633 +++++++++++++++++++++++++++++
include/linux/mailbox/zynqmp-ipi-message.h | 24 ++
4 files changed, 667 insertions(+)
create mode 100644 drivers/mailbox/zynqmp-ipi-mailbox.c
create mode 100644 include/linux/mailbox/zynqmp-ipi-message.h
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ba2f152..876614a 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -171,4 +171,12 @@ config BCM_FLEXRM_MBOX
Mailbox implementation of the Broadcom FlexRM ring manager,
which provides access to various offload engines on Broadcom
SoCs. Say Y here if you want to use the Broadcom FlexRM.
+
+config ZYNQMP_IPI_MBOX
+ tristate "Xilinx ZynqMP IPI Mailbox"
+ depends on ARCH_ZYNQMP && OF
+ help
+ Mailbox implementation for Xilinx ZynqMP IPI. It is used to send
+ notification or short message between processors with Xilinx
+ ZynqMP IPI.
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 4896f8d..155f72f 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -36,3 +36,5 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX) += bcm-flexrm-mailbox.o
obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o
obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
+
+obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c
new file mode 100644
index 0000000..334f5e1
--- /dev/null
+++ b/drivers/mailbox/zynqmp-ipi-mailbox.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Inter Processor Interrupt(IPI) Mailbox Driver
+ *
+ * Copyright (C) 2017 Xilinx Inc.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/mailbox/zynqmp-ipi-message.h>
+
+/* IPI agent ID any */
+#define IPI_ID_ANY 0xFFUL
+
+/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */
+#define USE_SMC 0
+#define USE_HVC 1
+
+/* Default IPI SMC function IDs */
+#define SMC_IPI_MAILBOX_OPEN 0x82001000U
+#define SMC_IPI_MAILBOX_RELEASE 0x82001001U
+#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U
+#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U
+#define SMC_IPI_MAILBOX_ACK 0x82001004U
+#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U
+#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U
+
+/* IPI SMC Macros */
+#define IPI_SMC_OPEN_IRQ_MASK 0x00000001UL /* IRQ enable bit in IPI
+ * open SMC call
+ */
+#define IPI_SMC_NOTIFY_BLOCK_MASK 0x00000001UL /* Flag to indicate if
+ * IPI notification needs
+ * to be blocking.
+ */
+#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if
+ * notification interrupt
+ * to be disabled.
+ */
+#define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if
+ * notification interrupt
+ * to be enabled.
+ */
+
+/* IPI mailbox status */
+#define IPI_MB_STATUS_IDLE 0
+#define IPI_MB_STATUS_SEND_PENDING 1
+#define IPI_MB_STATUS_RECV_PENDING 2
+
+#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */
+#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */
+
+/**
+ * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
+ * @is_opened: indicate if the IPI channel is opened
+ * @req_buf: local to remote request buffer start address
+ * @resp_buf: local to remote response buffer start address
+ * @req_buf_size: request buffer size
+ * @resp_buf_size: response buffer size
+ * @chan_type: channel type
+ */
+struct zynqmp_ipi_mchan {
+ int is_opened;
+ void __iomem *req_buf;
+ void __iomem *resp_buf;
+ size_t req_buf_size;
+ size_t resp_buf_size;
+ unsigned int chan_type;
+};
+
+/**
+ * struct zynqmp_ipi_mbox_pdata - Description of a ZynqMP IPI mailbox
+ * platform data.
+ * @dev: device pointer corresponding to the Xilinx ZynqMP
+ * IPI mailbox
+ * @local_id: local IPI agent ID
+ * @remote_id: remote IPI agent ID
+ * @method: IPI SMC or HVC is going to be used
+ * @mbox: mailbox Controller
+ * @mchans: array for channels, tx channel and rx channel.
+ * @irq: IPI agent interrupt ID
+ * @lock: IPI mailbox platform data lock
+ */
+struct zynqmp_ipi_mbox_pdata {
+ struct device *dev;
+ u32 local_id;
+ u32 remote_id;
+ unsigned int method;
+ struct mbox_controller mbox;
+ struct zynqmp_ipi_mchan mchans[2];
+ int irq;
+ spinlock_t lock; /* spin lock for local data */
+};
+
+static void zynqmp_ipi_fw_call(struct zynqmp_ipi_mbox_pdata *pdata,
+ unsigned long a0, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ unsigned long a1, a2;
+
+ a1 = pdata->local_id;
+ a2 = pdata->remote_id;
+ if (pdata->method == USE_SMC)
+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+ else
+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+/**
+ * zynqmp_ipi_interrupt - Interrupt handler for IPI notification
+ *
+ * @irq: Interrupt number
+ * @data: ZynqMP IPI mailbox platform data.
+ *
+ * Return: -EINVAL if there is no instance
+ * IRQ_NONE if the interrupt is not ours.
+ * IRQ_HANDLED if the rx interrupt was successfully handled.
+ */
+static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data)
+{
+ struct zynqmp_ipi_mbox_pdata *pdata = data;
+ struct mbox_chan *chan;
+ struct zynqmp_ipi_mchan *mchan;
+ struct zynqmp_ipi_message msg;
+ u64 arg0, arg3;
+ struct arm_smccc_res res;
+ int ret;
+
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ arg3 = IPI_SMC_ENQUIRY_DIRQ_MASK;
+ zynqmp_ipi_fw_call(pdata, arg0, arg3, 0, 0, 0, 0, &res);
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+ if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) {
+ chan = &pdata->mbox.chans[IPI_MB_CHNL_RX];
+ mchan = chan->con_priv;
+ if (mchan->is_opened) {
+ msg.len = mchan->req_buf_size;
+ msg.data = mchan->req_buf;
+ /* Client will direclty copy data from
+ * IPI buffer to client data memory
+ */
+ mbox_chan_received_data(chan, (void *)&msg);
+ return IRQ_HANDLED;
+ }
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * zynqmp_ipi_peek_data - Peek to see if there are any rx messages.
+ *
+ * @chan: Channel Pointer
+ *
+ * Return: 'true' if there is pending rx data, 'false' if there is none.
+ */
+static bool zynqmp_ipi_peek_data(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox_pdata *pdata = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ int ret;
+ u64 arg0;
+ struct arm_smccc_res res;
+
+ if (WARN_ON(!pdata)) {
+ dev_err(dev, "no platform drv data??\n");
+ return false;
+ }
+
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* TX channel, check if the message has been acked
+ * by the remote, if yes, response is available.
+ */
+ if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING)
+ return false;
+ else
+ return true;
+ } else if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) {
+ /* RX channel, check if there is message arrived. */
+ return true;
+ }
+ return false;
+}
+
+/**
+ * zynqmp_ipi_last_tx_done - See if the last tx message is sent
+ *
+ * @chan: Channel pointer
+ *
+ * Return: 'true' is no pending tx data, 'false' if there are any.
+ */
+static bool zynqmp_ipi_last_tx_done(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox_pdata *pdata = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ int ret;
+ u64 arg0;
+ struct arm_smccc_res res;
+ struct zynqmp_ipi_message msg;
+
+ if (WARN_ON(!pdata)) {
+ dev_err(dev, "no platform drv data??\n");
+ return false;
+ }
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* We only need to check if the message been taken
+ * by the remote in the TX channel
+ */
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ /* Check the SMC call status, a0 of the result */
+ ret = (int)(res.a0 & 0xFFFFFFFF);
+ if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING)
+ return false;
+
+ msg.len = mchan->resp_buf_size;
+ msg.data = mchan->resp_buf;
+ /* Client will direclty copy data from
+ * IPI buffer to client data memory
+ */
+ mbox_chan_received_data(chan, (void *)&msg);
+ return true;
+ }
+ /* Always true for the response message in RX channel */
+ return true;
+}
+
+/**
+ * zynqmp_ipi_send_data - Send data
+ *
+ * @chan: Channel Pointer
+ * @data: Message Pointer
+ *
+ * Return: 0 if all goes good, else appropriate error messages.
+ */
+static int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox_pdata *pdata = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ struct zynqmp_ipi_message *msg = data;
+ u64 arg0;
+ struct arm_smccc_res res;
+ u32 timeout;
+ int ret;
+
+ if (WARN_ON(!pdata)) {
+ dev_err(dev, "no platform drv data??\n");
+ return -EINVAL;
+ }
+
+ if (mchan->chan_type == IPI_MB_CHNL_TX) {
+ /* Send request message */
+ if (msg && msg->len > mchan->resp_buf_size) {
+ dev_err(dev, "channel %d message length %u > max %lu\n",
+ mchan->chan_type, (unsigned int)msg->len,
+ mchan->resp_buf_size);
+ return -EINVAL;
+ }
+ /* Enquire if the mailbox is free to send message */
+ arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
+ timeout = 10;
+ do {
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ ret = res.a0 & 0xFFFFFFFF;
+ if (ret >= 0 && !(ret & IPI_MB_STATUS_SEND_PENDING))
+ break;
+ usleep_range(1, 2);
+ timeout--;
+ } while (timeout);
+ if (!timeout) {
+ dev_warn(dev, "channel %d sending msg timesout.\n",
+ pdata->remote_id);
+ return -ETIME;
+ }
+ /* Copy message to the request buffer */
+ if (msg && msg->len)
+ memcpy_toio(mchan->req_buf, msg->data, msg->len);
+ /* Kick IPI mailbox to send message */
+ arg0 = SMC_IPI_MAILBOX_NOTIFY;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ } else {
+ /* Send response message */
+ if (msg && msg->len > mchan->resp_buf_size) {
+ dev_err(dev, "channel %d message length %u > max %lu\n",
+ mchan->chan_type, (unsigned int)msg->len,
+ mchan->resp_buf_size);
+ return -EINVAL;
+ }
+ if (msg && msg->len)
+ memcpy(mchan->resp_buf, msg->data, msg->len);
+ arg0 = SMC_IPI_MAILBOX_NOTIFY;
+ arg0 = SMC_IPI_MAILBOX_ACK;
+ zynqmp_ipi_fw_call(pdata, arg0, IPI_SMC_ACK_EIRQ_MASK,
+ 0, 0, 0, 0, &res);
+ }
+ return 0;
+}
+
+/**
+ * zynqmp_ipi_startup - Startup the IPI channel
+ *
+ * @chan: Channel pointer
+ *
+ * Return: 0 if all goes good, else return corresponding error message
+ */
+static int zynqmp_ipi_startup(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox_pdata *pdata = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ u64 arg0;
+ struct arm_smccc_res res;
+ int ret = 0;
+ unsigned long flags;
+ unsigned int nchan_type;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ if (mchan->is_opened) {
+ spin_unlock_irqrestore(&pdata->lock, flags);
+ return 0;
+ }
+
+ /* If no channel has been opened, open the IPI mailbox */
+ nchan_type = (mchan->chan_type + 1) % 2;
+ if (!pdata->mchans[nchan_type].is_opened) {
+ arg0 = SMC_IPI_MAILBOX_OPEN;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ /* Check the SMC call status, a0 of the result */
+ ret = (int)(res.a0 | 0xFFFFFFFF);
+ if (res.a0 < 0) {
+ dev_err(dev, "SMC to open the IPI channel failed.\n");
+ ret = res.a0;
+ spin_unlock_irqrestore(&pdata->lock, flags);
+ return ret;
+ }
+ ret = 0;
+ }
+
+ /* If it is RX channel, enable the IPI notification interrupt */
+ if (mchan->chan_type == IPI_MB_CHNL_RX) {
+ arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ }
+ mchan->is_opened = 1;
+ spin_unlock_irqrestore(&pdata->lock, flags);
+
+ return ret;
+}
+
+/**
+ * zynqmp_ipi_shutdown - Shutdown the IPI channel
+ *
+ * @chan: Channel pointer
+ */
+static void zynqmp_ipi_shutdown(struct mbox_chan *chan)
+{
+ struct device *dev = chan->mbox->dev;
+ struct zynqmp_ipi_mbox_pdata *pdata = dev_get_drvdata(dev);
+ struct zynqmp_ipi_mchan *mchan = chan->con_priv;
+ u64 arg0;
+ struct arm_smccc_res res;
+ unsigned long flags;
+ unsigned int chan_type;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ if (!mchan->is_opened) {
+ spin_unlock_irqrestore(&pdata->lock, flags);
+ return;
+ }
+
+ /* If it is RX channel, disable notification interrupt */
+ chan_type = mchan->chan_type;
+ if (chan_type == IPI_MB_CHNL_RX) {
+ arg0 = SMC_IPI_MAILBOX_DISABLE_IRQ;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ }
+ /* Release IPI mailbox if no other channel is opened */
+ chan_type = (chan_type + 1) % 2;
+ if (!pdata->mchans[chan_type].is_opened) {
+ arg0 = SMC_IPI_MAILBOX_RELEASE;
+ zynqmp_ipi_fw_call(pdata, arg0, 0, 0, 0, 0, 0, &res);
+ }
+
+ mchan->is_opened = 0;
+ spin_unlock_irqrestore(&pdata->lock, flags);
+}
+
+/* ZynqMP IPI mailbox operations */
+static const struct mbox_chan_ops zynqmp_ipi_chan_ops = {
+ .startup = zynqmp_ipi_startup,
+ .shutdown = zynqmp_ipi_shutdown,
+ .peek_data = zynqmp_ipi_peek_data,
+ .last_tx_done = zynqmp_ipi_last_tx_done,
+ .send_data = zynqmp_ipi_send_data,
+};
+
+/**
+ * zynqmp_ipi_of_xlate - Translate of phandle to IPI mailbox channel
+ *
+ * @mbox: mailbox controller pointer
+ * @p: phandle pointer
+ *
+ * Return: Mailbox channel, else return error pointer.
+ */
+static struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *p)
+{
+ struct zynqmp_ipi_mbox_pdata *pdata;
+ struct mbox_chan *chan;
+ struct device *dev = mbox->dev;
+ unsigned int chan_type;
+
+ pdata = container_of(mbox, struct zynqmp_ipi_mbox_pdata, mbox);
+
+ /* Only supports TX and RX channels */
+ chan_type = p->args[0];
+ if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) {
+ dev_err(dev, "req chnl failure: invalid chnl type %u.\n",
+ chan_type);
+ return ERR_PTR(-EINVAL);
+ }
+ chan = &mbox->chans[chan_type];
+ return chan;
+}
+
+static const struct of_device_id zynqmp_ipi_of_match[] = {
+ {.compatible = "xlnx,zynqmp-ipi-mailbox"},
+};
+MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match);
+
+static int zynqmp_ipi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct zynqmp_ipi_mbox_pdata *pdata;
+ struct zynqmp_ipi_mchan *mchan;
+ struct mbox_chan *chans;
+ struct mbox_controller *mbox;
+ const unsigned char *prop;
+ struct resource *res;
+ int ret = -EINVAL;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->dev = dev;
+
+ mchan = &pdata->mchans[IPI_MB_CHNL_TX];
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "local_request_region");
+ if (res) {
+ mchan->req_buf_size = resource_size(res);
+ mchan->req_buf = devm_ioremap(&pdev->dev, res->start,
+ mchan->req_buf_size);
+ if (IS_ERR(mchan->req_buf)) {
+ dev_err(dev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->req_buf);
+ return ret;
+ }
+ }
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "remote_response_region");
+ if (res) {
+ mchan->resp_buf_size = resource_size(res);
+ mchan->resp_buf = devm_ioremap(&pdev->dev, res->start,
+ mchan->resp_buf_size);
+ if (IS_ERR(mchan->resp_buf)) {
+ dev_err(dev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->resp_buf);
+ return ret;
+ }
+ }
+
+ mchan = &pdata->mchans[IPI_MB_CHNL_RX];
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "remote_request_region");
+ if (res) {
+ mchan->req_buf_size = resource_size(res);
+ mchan->req_buf = devm_ioremap(&pdev->dev, res->start,
+ mchan->req_buf_size);
+ if (IS_ERR(mchan->req_buf)) {
+ dev_err(dev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->req_buf);
+ return ret;
+ }
+ }
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "local_response_region");
+ if (res) {
+ mchan->resp_buf_size = resource_size(res);
+ mchan->resp_buf = devm_ioremap(&pdev->dev, res->start,
+ mchan->resp_buf_size);
+ if (IS_ERR(mchan->resp_buf)) {
+ dev_err(dev, "Unable to map IPI buffer I/O memory\n");
+ ret = PTR_ERR(mchan->resp_buf);
+ return ret;
+ }
+ }
+
+ /* Get the IPI local and remote agents IDs */
+ ret = of_property_read_u32_index(np, "xlnx,ipi-ids", 0,
+ &pdata->local_id);
+ if (ret < 0) {
+ dev_err(dev, "No IPI local ID is specified.\n");
+ return ret;
+ }
+ ret = of_property_read_u32_index(np, "xlnx,ipi-ids", 1,
+ &pdata->remote_id);
+ if (ret < 0) {
+ dev_err(dev, "No IPI remote ID is specified.\n");
+ return ret;
+ }
+
+ /* Get how to access IPI agent method */
+ prop = of_get_property(np, "method", NULL);
+ if (!prop) {
+ pdata->method = USE_SMC;
+ } else if (!strcmp(prop, "smc")) {
+ pdata->method = USE_SMC;
+ } else if (!strcmp(prop, "hvc")) {
+ pdata->method = USE_HVC;
+ } else {
+ dev_err(dev, "Invalid \"method\" %s.\n", prop);
+ return ret;
+ }
+
+ /* IPI IRQ */
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "unable to find IPI IRQ.\n");
+ return ret;
+ }
+ pdata->irq = ret;
+ ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
+ IRQF_SHARED, dev_name(dev), pdata);
+ if (ret) {
+ dev_err(dev, "IRQ %d is not requested successfully.\n",
+ pdata->irq);
+ return ret;
+ }
+
+ mbox = &pdata->mbox;
+ mbox->dev = dev;
+ mbox->ops = &zynqmp_ipi_chan_ops;
+ mbox->num_chans = 2;
+ mbox->txdone_irq = false;
+ mbox->txdone_poll = true;
+ mbox->txpoll_period = 5;
+ mbox->of_xlate = zynqmp_ipi_of_xlate;
+ chans = devm_kzalloc(dev, 2 * sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return -ENOMEM;
+ mbox->chans = chans;
+ mbox->chans[IPI_MB_CHNL_TX].con_priv = &pdata->mchans[IPI_MB_CHNL_TX];
+ mbox->chans[IPI_MB_CHNL_RX].con_priv = &pdata->mchans[IPI_MB_CHNL_RX];
+ pdata->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX;
+ pdata->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX;
+ spin_lock_init(&pdata->lock);
+ platform_set_drvdata(pdev, pdata);
+ ret = mbox_controller_register(mbox);
+ if (ret)
+ dev_err(dev, "Failed to register mbox_controller(%d)\n", ret);
+ else
+ dev_info(dev, "Probed ZynqMP IPI Mailbox driver.\n");
+ return ret;
+}
+
+static int zynqmp_ipi_remove(struct platform_device *pdev)
+{
+ struct zynqmp_ipi_mbox_pdata *pdata;
+
+ pdata = platform_get_drvdata(pdev);
+ mbox_controller_unregister(&pdata->mbox);
+
+ return 0;
+}
+
+static struct platform_driver zynqmp_ipi_driver = {
+ .probe = zynqmp_ipi_probe,
+ .remove = zynqmp_ipi_remove,
+ .driver = {
+ .name = "zynqmp-ipi",
+ .of_match_table = of_match_ptr(zynqmp_ipi_of_match),
+ },
+};
+
+static struct class zynqmp_ipi_class = { .name = "zynqmp_ipi_mbox", };
+
+static int __init zynqmp_ipi_init(void)
+{
+ int err;
+
+ err = class_register(&zynqmp_ipi_class);
+ if (err)
+ return err;
+
+ return platform_driver_register(&zynqmp_ipi_driver);
+}
+subsys_initcall(zynqmp_ipi_init);
+
+static void __exit zynqmp_ipi_exit(void)
+{
+ platform_driver_unregister(&zynqmp_ipi_driver);
+ class_unregister(&zynqmp_ipi_class);
+}
+module_exit(zynqmp_ipi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Xilinx ZynqMP IPI Mailbox driver");
+MODULE_AUTHOR("Xilinx Inc.");
diff --git a/include/linux/mailbox/zynqmp-ipi-message.h b/include/linux/mailbox/zynqmp-ipi-message.h
new file mode 100644
index 0000000..173c41d
--- /dev/null
+++ b/include/linux/mailbox/zynqmp-ipi-message.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017 Xilinx Inc.
+ *
+ */
+
+#ifndef _LINUX_ZYNQMP_IPI_MESSAGE_H_
+#define _LINUX_ZYNQMP_IPI_MESSAGE_H_
+
+/**
+ * struct zynqmp_ipi_message - ZynqMP IPI message structure
+ * @len: Length of the request message
+ * @data: Request message pointer
+ *
+ * This is the structure for data used in mbox_send_message
+ * the maximum length of data buffer is fixed to 12 bytes.
+ * Client is supposed to be aware of this.
+ */
+struct zynqmp_ipi_message {
+ size_t len;
+ u8 *data;
+};
+
+#endif /* _LINUX_ZYNQMP_IPI_MESSAGE_H_ */
--
2.7.4
--
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
* [RFC v2 0/2] Xilinx ZynqMP IPI Mailbox Controller Driver
From: Wendy Liang @ 2017-12-04 6:25 UTC (permalink / raw)
To: jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w,
michal.simek-gjFFaj9aHVfQT0dZR+AlfA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Wendy Liang
Introduce mailbox controller driver for ZynqMP IPI(Inter-processor
interrupt) IP core.
There is previous discussion on the DT bindings:
https://patchwork.kernel.org/patch/10012755/
v2:
- change SPDX-License-Identifier license text style in .c file
- replace xlnx-ipi-ids with xlnx,ipi-ids
Wendy Liang (2):
mailbox: ZynqMP IPI mailbox controller
dt-bindings: mailbox: Add Xilinx IPI Mailbox
.../bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt | 104 ++++
drivers/mailbox/Kconfig | 8 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/zynqmp-ipi-mailbox.c | 633 +++++++++++++++++++++
include/linux/mailbox/zynqmp-ipi-message.h | 24 +
5 files changed, 771 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt
create mode 100644 drivers/mailbox/zynqmp-ipi-mailbox.c
create mode 100644 include/linux/mailbox/zynqmp-ipi-message.h
--
2.7.4
--
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 2/3] bluetooth: hci_ll: add constant for vendor-specific command
From: Marcel Holtmann @ 2017-12-04 6:23 UTC (permalink / raw)
To: David Lechner
Cc: devicetree, open list:BLUETOOTH DRIVERS, Rob Herring,
Mark Rutland, Gustavo F. Padovan, Johan Hedberg,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1512357682-8911-3-git-send-email-david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
Hi David,
> This adds a #define for the vendor-specific HCI command to set the
> baudrate instead of using the bare 0xff36 multiple times.
>
> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
> ---
> drivers/bluetooth/hci_ll.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
patch has been applied to bluetooth-next tree.
Regards
Marcel
--
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 3/3] ARM64: dts: meson-axg: add PWM DT info for Meson-Axg SoC
From: Yixun Lan @ 2017-12-04 6:00 UTC (permalink / raw)
To: Thierry Reding, Kevin Hilman, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Neil Armstrong,
Jerome Brunet, Mark Rutland, Carlo Caione, Jian Hu, Yixun Lan,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171204060018.8856-1-yixun.lan-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
From: Jian Hu <jian.hu-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
Add PWM DT info for the Amlogic's Meson-Axg SoC.
Signed-off-by: Jian Hu <jian.hu-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Yixun Lan <yixun.lan-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
---
arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 120 +++++++++++++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
index 92f65eec3e18..f7f228701df1 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
@@ -177,6 +177,24 @@
clock-names = "clk_i2c";
};
+ pwm_ab: pwm@1b000 {
+ compatible = "amlogic,meson-axg-ee-pwm";
+ reg = <0x0 0x1b000 0x0 0x20>;
+ #pwm-cells = <3>;
+ clocks = <&xtal>, <&xtal>;
+ clock-names = "clkin0", "clkin1";
+ status = "disabled";
+ };
+
+ pwm_cd: pwm@1a000 {
+ compatible = "amlogic,meson-axg-ee-pwm";
+ reg = <0x0 0x1a000 0x0 0x20>;
+ #pwm-cells = <3>;
+ clocks = <&xtal>, <&xtal>;
+ clock-names = "clkin0", "clkin1";
+ status = "disabled";
+ };
+
uart_A: serial@24000 {
compatible = "amlogic,meson-gx-uart", "amlogic,meson-uart";
reg = <0x0 0x24000 0x0 0x14>;
@@ -368,6 +386,90 @@
function = "i2c3";
};
};
+
+ pwm_a_a_pins: pwm_a_a {
+ mux {
+ groups = "pwm_a_a";
+ function = "pwm_a";
+ };
+ };
+
+ pwm_a_x18_pins: pwm_a_x18 {
+ mux {
+ groups = "pwm_a_x18";
+ function = "pwm_a";
+ };
+ };
+
+ pwm_a_x20_pins: pwm_a_x20 {
+ mux {
+ groups = "pwm_a_x20";
+ function = "pwm_a";
+ };
+ };
+
+ pwm_a_z_pins: pwm_a_z {
+ mux {
+ groups = "pwm_a_z";
+ function = "pwm_a";
+ };
+ };
+
+ pwm_b_a_pins: pwm_b_a {
+ mux {
+ groups = "pwm_b_a";
+ function = "pwm_b";
+ };
+ };
+
+ pwm_b_x_pins: pwm_b_x {
+ mux {
+ groups = "pwm_b_x";
+ function = "pwm_b";
+ };
+ };
+
+ pwm_b_z_pins: pwm_b_z {
+ mux {
+ groups = "pwm_b_z";
+ function = "pwm_b";
+ };
+ };
+
+ pwm_c_a_pins: pwm_c_a {
+ mux {
+ groups = "pwm_c_a";
+ function = "pwm_c";
+ };
+ };
+
+ pwm_c_x10_pins: pwm_c_x10 {
+ mux {
+ groups = "pwm_c_x10";
+ function = "pwm_c";
+ };
+ };
+
+ pwm_c_x17_pins: pwm_c_x17 {
+ mux {
+ groups = "pwm_c_x17";
+ function = "pwm_c";
+ };
+ };
+
+ pwm_d_x11_pins: pwm_d_x11 {
+ mux {
+ groups = "pwm_d_x11";
+ function = "pwm_d";
+ };
+ };
+
+ pwm_d_x16_pins: pwm_d_x16 {
+ mux {
+ groups = "pwm_d_x16";
+ function = "pwm_d";
+ };
+ };
};
};
@@ -435,6 +537,24 @@
clock-names = "clk_i2c";
};
+ pwm_AO_ab: pwm@7000 {
+ compatible = "amlogic,meson-axg-ao-pwm";
+ reg = <0x0 0x07000 0x0 0x20>;
+ #pwm-cells = <3>;
+ clocks = <&xtal>, <&xtal>;
+ clock-names = "clkin0", "clkin1";
+ status = "disabled";
+ };
+
+ pwm_AO_cd: pwm@2000 {
+ compatible = "amlogic,axg-ao-pwm";
+ reg = <0x0 0x02000 0x0 0x20>;
+ #pwm-cells = <3>;
+ clocks = <&xtal>, <&xtal>;
+ clock-names = "clkin0", "clkin1";
+ status = "disabled";
+ };
+
uart_AO: serial@3000 {
compatible = "amlogic,meson-gx-uart", "amlogic,meson-ao-uart";
reg = <0x0 0x3000 0x0 0x18>;
--
2.15.0
--
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
* [PATCH 2/3] pwm: meson: add clock source configuratin for Meson-AXG
From: Yixun Lan @ 2017-12-04 6:00 UTC (permalink / raw)
To: Thierry Reding, Kevin Hilman, linux-pwm, linux-amlogic
Cc: Rob Herring, devicetree, Neil Armstrong, Jerome Brunet,
Mark Rutland, Carlo Caione, Jian Hu, Yixun Lan, linux-arm-kernel,
linux-kernel
In-Reply-To: <20171204060018.8856-1-yixun.lan@amlogic.com>
From: Jian Hu <jian.hu@amlogic.com>
For PWM controller in the Meson-AXG SoC, the EE domain and
AO domain have different clock source. This patch try to describe
them in the DT compatible data.
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
drivers/pwm/pwm-meson.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
index 1f44b288af57..dcacc5c6ac1e 100644
--- a/drivers/pwm/pwm-meson.c
+++ b/drivers/pwm/pwm-meson.c
@@ -421,6 +421,24 @@ static const struct meson_pwm_data pwm_gxbb_ao_data = {
.num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
};
+static const char * const pwm_axg_ee_parent_names[] = {
+ "xtal", "fclk_div5", "fclk_div4", "fclk_div3"
+};
+
+static const struct meson_pwm_data pwm_axg_ee_data = {
+ .parent_names = pwm_axg_ee_parent_names,
+ .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_names),
+};
+
+static const char * const pwm_axg_ao_parent_names[] = {
+ "aoclk81", "xtal", "fclk_div4", "fclk_div5"
+};
+
+static const struct meson_pwm_data pwm_axg_ao_data = {
+ .parent_names = pwm_axg_ao_parent_names,
+ .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names),
+};
+
static const struct of_device_id meson_pwm_matches[] = {
{
.compatible = "amlogic,meson8b-pwm",
@@ -434,6 +452,14 @@ static const struct of_device_id meson_pwm_matches[] = {
.compatible = "amlogic,meson-gxbb-ao-pwm",
.data = &pwm_gxbb_ao_data
},
+ {
+ .compatible = "amlogic,meson-axg-ee-pwm",
+ .data = &pwm_axg_ee_data
+ },
+ {
+ .compatible = "amlogic,meson-axg-ao-pwm",
+ .data = &pwm_axg_ao_data
+ },
{},
};
MODULE_DEVICE_TABLE(of, meson_pwm_matches);
--
2.15.0
^ permalink raw reply related
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