From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755108AbbHNOeP (ORCPT ); Fri, 14 Aug 2015 10:34:15 -0400 Received: from mx2.suse.de ([195.135.220.15]:34264 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751962AbbHNOeO (ORCPT ); Fri, 14 Aug 2015 10:34:14 -0400 Date: Fri, 14 Aug 2015 16:34:07 +0200 Message-ID: From: Takashi Iwai To: Russell King Cc: Fabio Estevam , alsa-devel@alsa-project.org, David Airlie , Sascha Hauer , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, Mark Brown , Philipp Zabel , Yakir Yang , Andy Yan , Jon Nettleton , linux-arm-kernel@lists.infradead.org Subject: Re: [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver In-Reply-To: References: User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL/10.8 Emacs/24.5 (x86_64-suse-linux-gnu) MULE/6.0 (HANACHIRUSATO) MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, 14 Aug 2015 16:04:25 +0200, Russell King wrote: > > Add ALSA based HDMI AHB audio driver for dw_hdmi. The only buffer > format supported by the hardware is its own special IEC958 based format, > which is not compatible with any ALSA format. To avoid doing too much > data manipulation within the driver, we support only ALSAs IEC958 LE and > 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware > format. > > A more desirable solution would be to have this conversion in userspace, > but ALSA does not appear to allow such transformations outside of > libasound itself. > > Signed-off-by: Russell King > --- > v2: updated with Takashi Iwai's comments... and with a fixed Cc: line. Reviewed-by: Takashi Iwai thanks, Takashi > drivers/gpu/drm/bridge/Kconfig | 10 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++ > drivers/gpu/drm/bridge/dw_hdmi-audio.h | 13 + > drivers/gpu/drm/bridge/dw_hdmi.c | 24 ++ > drivers/gpu/drm/bridge/dw_hdmi.h | 3 + > 6 files changed, 630 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index acef3223772c..56ed35fe0734 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -3,6 +3,16 @@ config DRM_DW_HDMI > depends on DRM > select DRM_KMS_HELPER > > +config DRM_DW_HDMI_AHB_AUDIO > + tristate "Synopsis Designware AHB Audio interface" > + depends on DRM_DW_HDMI && SND > + select SND_PCM > + select SND_PCM_IEC958 > + help > + Support the AHB Audio interface which is part of the Synopsis > + Designware HDMI block. This is used in conjunction with > + the i.MX6 HDMI driver. > + > config DRM_PTN3460 > tristate "PTN3460 DP/LVDS bridge" > depends on DRM > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index 8dfebd984370..eb80dbbb8365 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm > obj-$(CONFIG_DRM_PS8622) += ps8622.o > obj-$(CONFIG_DRM_PTN3460) += ptn3460.o > obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o > +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o > diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > new file mode 100644 > index 000000000000..bf379310008a > --- /dev/null > +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > @@ -0,0 +1,579 @@ > +/* > + * DesignWare HDMI audio driver > + * > + * 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. > + * > + * Written and tested against the Designware HDMI Tx found in iMX6. > + */ > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include "dw_hdmi-audio.h" > + > +#define DRIVER_NAME "dw-hdmi-ahb-audio" > + > +/* Provide some bits rather than bit offsets */ > +enum { > + HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7), > + HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3), > + HDMI_AHB_DMA_START_START = BIT(0), > + HDMI_AHB_DMA_STOP_STOP = BIT(0), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL = > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY, > + HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5), > + HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4), > + HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3), > + HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2), > + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), > + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), > + HDMI_IH_AHBDMAAUD_STAT0_ALL = > + HDMI_IH_AHBDMAAUD_STAT0_ERROR | > + HDMI_IH_AHBDMAAUD_STAT0_LOST | > + HDMI_IH_AHBDMAAUD_STAT0_RETRY | > + HDMI_IH_AHBDMAAUD_STAT0_DONE | > + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL | > + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY, > + HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1, > + HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1, > + HDMI_AHB_DMA_CONF0_INCR4 = 0, > + HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0), > + HDMI_AHB_DMA_MASK_DONE = BIT(7), > + HDMI_REVISION_ID = 0x0001, > + HDMI_IH_AHBDMAAUD_STAT0 = 0x0109, > + HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189, > + HDMI_AHB_DMA_CONF0 = 0x3600, > + HDMI_AHB_DMA_START = 0x3601, > + HDMI_AHB_DMA_STOP = 0x3602, > + HDMI_AHB_DMA_THRSLD = 0x3603, > + HDMI_AHB_DMA_STRADDR0 = 0x3604, > + HDMI_AHB_DMA_STPADDR0 = 0x3608, > + HDMI_AHB_DMA_MASK = 0x3614, > + HDMI_AHB_DMA_POL = 0x3615, > + HDMI_AHB_DMA_CONF1 = 0x3616, > + HDMI_AHB_DMA_BUFFPOL = 0x361a, > +}; > + > +struct snd_dw_hdmi { > + struct snd_card *card; > + struct snd_pcm *pcm; > + spinlock_t lock; > + struct dw_hdmi_audio_data data; > + struct snd_pcm_substream *substream; > + void (*reformat)(struct snd_dw_hdmi *, size_t, size_t); > + void *buf_src; > + void *buf_dst; > + dma_addr_t buf_addr; > + unsigned buf_offset; > + unsigned buf_period; > + unsigned buf_size; > + unsigned channels; > + u8 revision; > + u8 iec_offset; > + u8 cs[192][8]; > +}; > + > +static void dw_hdmi_writel(u32 val, void __iomem *ptr) > +{ > + writeb_relaxed(val, ptr); > + writeb_relaxed(val >> 8, ptr + 1); > + writeb_relaxed(val >> 16, ptr + 2); > + writeb_relaxed(val >> 24, ptr + 3); > +} > + > +/* > + * Convert to hardware format: The userspace buffer contains IEC958 samples, > + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4. We > + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio > + * samples in 23..0. > + * > + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd > + * > + * Ideally, we could do with having the data properly formatted in userspace. > + */ > +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw, > + size_t offset, size_t bytes) > +{ > + u32 *src = dw->buf_src + offset; > + u32 *dst = dw->buf_dst + offset; > + u32 *end = dw->buf_src + offset + bytes; > + > + do { > + u32 b, sample = *src++; > + > + b = (sample & 8) << (28 - 3); > + > + sample >>= 4; > + > + *dst++ = sample | b; > + } while (src < end); > +} > + > +static u32 parity(u32 sample) > +{ > + sample ^= sample >> 16; > + sample ^= sample >> 8; > + sample ^= sample >> 4; > + sample ^= sample >> 2; > + sample ^= sample >> 1; > + return (sample & 1) << 27; > +} > + > +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw, > + size_t offset, size_t bytes) > +{ > + u32 *src = dw->buf_src + offset; > + u32 *dst = dw->buf_dst + offset; > + u32 *end = dw->buf_src + offset + bytes; > + > + do { > + unsigned i; > + u8 *cs; > + > + cs = dw->cs[dw->iec_offset++]; > + if (dw->iec_offset >= 192) > + dw->iec_offset = 0; > + > + i = dw->channels; > + do { > + u32 sample = *src++; > + > + sample &= ~0xff000000; > + sample |= *cs++ << 24; > + sample |= parity(sample & ~0xf8000000); > + > + *dst++ = sample; > + } while (--i); > + } while (src < end); > +} > + > +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw, > + struct snd_pcm_runtime *runtime) > +{ > + u8 cs[4]; > + unsigned ch, i, j; > + > + snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs)); > + > + memset(dw->cs, 0, sizeof(dw->cs)); > + > + for (ch = 0; ch < 8; ch++) { > + cs[2] &= ~IEC958_AES2_CON_CHANNEL; > + cs[2] |= (ch + 1) << 4; > + > + for (i = 0; i < ARRAY_SIZE(cs); i++) { > + unsigned c = cs[i]; > + > + for (j = 0; j < 8; j++, c >>= 1) > + dw->cs[i * 8 + j][ch] = (c & 1) << 2; > + } > + } > + dw->cs[0][0] |= BIT(4); > +} > + > +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw) > +{ > + void __iomem *base = dw->data.base; > + unsigned offset = dw->buf_offset; > + unsigned period = dw->buf_period; > + u32 start, stop; > + > + dw->reformat(dw, offset, period); > + > + /* Clear all irqs before enabling irqs and starting DMA */ > + writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL, > + base + HDMI_IH_AHBDMAAUD_STAT0); > + > + start = dw->buf_addr + offset; > + stop = start + period - 1; > + > + /* Setup the hardware start/stop addresses */ > + dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0); > + dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0); > + > + writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK); > + writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START); > + > + offset += period; > + if (offset >= dw->buf_size) > + offset = 0; > + dw->buf_offset = offset; > +} > + > +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw) > +{ > + /* Disable interrupts before disabling DMA */ > + writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK); > + writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP); > +} > + > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data) > +{ > + struct snd_dw_hdmi *dw = data; > + struct snd_pcm_substream *substream; > + unsigned stat; > + > + stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); > + if (!stat) > + return IRQ_NONE; > + > + writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); > + > + substream = dw->substream; > + if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) { > + snd_pcm_period_elapsed(substream); > + > + spin_lock(&dw->lock); > + if (dw->substream) > + dw_hdmi_start_dma(dw); > + spin_unlock(&dw->lock); > + } > + > + return IRQ_HANDLED; > +} > + > +static struct snd_pcm_hardware dw_hdmi_hw = { > + .info = SNDRV_PCM_INFO_INTERLEAVED | > + SNDRV_PCM_INFO_BLOCK_TRANSFER | > + SNDRV_PCM_INFO_MMAP | > + SNDRV_PCM_INFO_MMAP_VALID, > + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | > + SNDRV_PCM_FMTBIT_S24_LE, > + .rates = SNDRV_PCM_RATE_32000 | > + SNDRV_PCM_RATE_44100 | > + SNDRV_PCM_RATE_48000 | > + SNDRV_PCM_RATE_88200 | > + SNDRV_PCM_RATE_96000 | > + SNDRV_PCM_RATE_176400 | > + SNDRV_PCM_RATE_192000, > + .channels_min = 2, > + .channels_max = 8, > + .buffer_bytes_max = 64 * 1024, > + .period_bytes_min = 256, > + .period_bytes_max = 8192, /* ERR004323: must limit to 8k */ > + .periods_min = 2, > + .periods_max = 16, > + .fifo_size = 0, > +}; > + > +static int dw_hdmi_open(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + void __iomem *base = dw->data.base; > + int ret; > + > + runtime->hw = dw_hdmi_hw; > + > + ret = snd_pcm_limit_hw_rates(runtime); > + if (ret < 0) > + return ret; > + > + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); > + if (ret < 0) > + return ret; > + > + /* Clear FIFO */ > + writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST, > + base + HDMI_AHB_DMA_CONF0); > + > + /* Configure interrupt polarities */ > + writeb_relaxed(~0, base + HDMI_AHB_DMA_POL); > + writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL); > + > + /* Keep interrupts masked, and clear any pending */ > + writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK); > + writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0); > + > + ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED, > + "dw-hdmi-audio", dw); > + if (ret) > + return ret; > + > + /* Un-mute done interrupt */ > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL & > + ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE, > + base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + > + return 0; > +} > + > +static int dw_hdmi_close(struct snd_pcm_substream *substream) > +{ > + struct snd_dw_hdmi *dw = substream->private_data; > + > + /* Mute all interrupts */ > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, > + dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + > + free_irq(dw->data.irq, dw); > + > + return 0; > +} > + > +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream) > +{ > + return snd_pcm_lib_free_vmalloc_buffer(substream); > +} > + > +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params) > +{ > + return snd_pcm_lib_alloc_vmalloc_buffer(substream, > + params_buffer_bytes(params)); > +} > + > +static int dw_hdmi_prepare(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + u8 threshold, conf0, conf1; > + > + /* Setup as per 3.0.5 FSL 4.1.0 BSP */ > + switch (dw->revision) { > + case 0x0a: > + conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | > + HDMI_AHB_DMA_CONF0_INCR4; > + if (runtime->channels == 2) > + threshold = 126; > + else > + threshold = 124; > + break; > + case 0x1a: > + conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | > + HDMI_AHB_DMA_CONF0_INCR8; > + threshold = 128; > + break; > + default: > + /* NOTREACHED */ > + return -EINVAL; > + } > + > + dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate); > + > + /* Minimum number of bytes in the fifo. */ > + runtime->hw.fifo_size = threshold * 32; > + > + conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK; > + conf1 = (1 << runtime->channels) - 1; > + > + writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD); > + writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0); > + writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1); > + > + switch (runtime->format) { > + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: > + dw->reformat = dw_hdmi_reformat_iec958; > + break; > + case SNDRV_PCM_FORMAT_S24_LE: > + dw_hdmi_create_cs(dw, runtime); > + dw->reformat = dw_hdmi_reformat_s24; > + break; > + } > + dw->iec_offset = 0; > + dw->channels = runtime->channels; > + dw->buf_src = runtime->dma_area; > + dw->buf_dst = substream->dma_buffer.area; > + dw->buf_addr = substream->dma_buffer.addr; > + dw->buf_period = snd_pcm_lib_period_bytes(substream); > + dw->buf_size = snd_pcm_lib_buffer_bytes(substream); > + > + return 0; > +} > + > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd) > +{ > + struct snd_dw_hdmi *dw = substream->private_data; > + unsigned long flags; > + int ret = 0; > + > + switch (cmd) { > + case SNDRV_PCM_TRIGGER_START: > + spin_lock_irqsave(&dw->lock, flags); > + dw->buf_offset = 0; > + dw->substream = substream; > + dw_hdmi_start_dma(dw); > + dw_hdmi_audio_enable(dw->data.hdmi); > + spin_unlock_irqrestore(&dw->lock, flags); > + substream->runtime->delay = substream->runtime->period_size; > + break; > + > + case SNDRV_PCM_TRIGGER_STOP: > + spin_lock_irqsave(&dw->lock, flags); > + dw->substream = NULL; > + dw_hdmi_stop_dma(dw); > + dw_hdmi_audio_disable(dw->data.hdmi); > + spin_unlock_irqrestore(&dw->lock, flags); > + break; > + > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + > + /* > + * We are unable to report the exact hardware position as > + * reading the 32-bit DMA position using 8-bit reads is racy. > + */ > + return bytes_to_frames(runtime, dw->buf_offset); > +} > + > +static struct snd_pcm_ops snd_dw_hdmi_ops = { > + .open = dw_hdmi_open, > + .close = dw_hdmi_close, > + .ioctl = snd_pcm_lib_ioctl, > + .hw_params = dw_hdmi_hw_params, > + .hw_free = dw_hdmi_hw_free, > + .prepare = dw_hdmi_prepare, > + .trigger = dw_hdmi_trigger, > + .pointer = dw_hdmi_pointer, > + .page = snd_pcm_lib_get_vmalloc_page, > +}; > + > +static int snd_dw_hdmi_probe(struct platform_device *pdev) > +{ > + const struct dw_hdmi_audio_data *data = pdev->dev.platform_data; > + struct device *dev = pdev->dev.parent; > + struct snd_dw_hdmi *dw; > + struct snd_card *card; > + struct snd_pcm *pcm; > + unsigned revision; > + int ret; > + > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, > + data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + revision = readb_relaxed(data->base + HDMI_REVISION_ID); > + if (revision != 0x0a && revision != 0x1a) { > + dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n", > + revision); > + return -ENXIO; > + } > + > + ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, > + THIS_MODULE, sizeof(struct snd_dw_hdmi), &card); > + if (ret < 0) > + return ret; > + > + strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); > + strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname)); > + snprintf(card->longname, sizeof(card->longname), > + "%s rev 0x%02x, irq %d", card->shortname, revision, > + data->irq); > + > + dw = card->private_data; > + dw->card = card; > + dw->data = *data; > + dw->revision = revision; > + > + spin_lock_init(&dw->lock); > + > + ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm); > + if (ret < 0) > + goto err; > + > + dw->pcm = pcm; > + pcm->private_data = dw; > + strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops); > + > + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, > + dev, 64 * 1024, 64 * 1024); > + > + ret = snd_card_register(card); > + if (ret < 0) > + goto err; > + > + platform_set_drvdata(pdev, dw); > + > + return 0; > + > +err: > + snd_card_free(card); > + return ret; > +} > + > +static int snd_dw_hdmi_remove(struct platform_device *pdev) > +{ > + struct snd_dw_hdmi *dw = platform_get_drvdata(pdev); > + > + snd_card_free(dw->card); > + > + return 0; > +} > + > +#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN) > +/* > + * This code is fine, but requires implementation in the dw_hdmi_trigger() > + * method which is currently missing as I have no way to test this. > + */ > +static int snd_dw_hdmi_suspend(struct device *dev) > +{ > + struct snd_dw_hdmi *dw = dev_get_drvdata(dev); > + > + snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold); > + snd_pcm_suspend_all(dw->pcm); > + > + return 0; > +} > + > +static int snd_dw_hdmi_resume(struct device *dev) > +{ > + struct snd_dw_hdmi *dw = dev_get_drvdata(dev); > + > + snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend, > + snd_dw_hdmi_resume); > +#define PM_OPS &snd_dw_hdmi_pm > +#else > +#define PM_OPS NULL > +#endif > + > +static struct platform_driver snd_dw_hdmi_driver = { > + .probe = snd_dw_hdmi_probe, > + .remove = snd_dw_hdmi_remove, > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + .pm = PM_OPS, > + }, > +}; > + > +module_platform_driver(snd_dw_hdmi_driver); > + > +MODULE_AUTHOR("Russell King "); > +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:" DRIVER_NAME); > diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h > new file mode 100644 > index 000000000000..1e840118d90a > --- /dev/null > +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h > @@ -0,0 +1,13 @@ > +#ifndef DW_HDMI_AUDIO_H > +#define DW_HDMI_AUDIO_H > + > +struct dw_hdmi; > + > +struct dw_hdmi_audio_data { > + phys_addr_t phys; > + void __iomem *base; > + int irq; > + struct dw_hdmi *hdmi; > +}; > + > +#endif > diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c > index fba25607ef88..b65464789fbd 100644 > --- a/drivers/gpu/drm/bridge/dw_hdmi.c > +++ b/drivers/gpu/drm/bridge/dw_hdmi.c > @@ -28,6 +28,7 @@ > #include > > #include "dw_hdmi.h" > +#include "dw_hdmi-audio.h" > > #define HDMI_EDID_LEN 512 > > @@ -104,6 +105,7 @@ struct dw_hdmi { > struct drm_encoder *encoder; > struct drm_bridge *bridge; > > + struct platform_device *audio; > enum dw_hdmi_devtype dev_type; > struct device *dev; > struct clk *isfr_clk; > @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, > { > struct drm_device *drm = data; > struct device_node *np = dev->of_node; > + struct platform_device_info pdevinfo; > struct device_node *ddc_node; > + struct dw_hdmi_audio_data audio; > struct dw_hdmi *hdmi; > int ret; > u32 val = 1; > @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master, > hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), > HDMI_IH_MUTE_PHY_STAT0); > > + memset(&pdevinfo, 0, sizeof(pdevinfo)); > + pdevinfo.parent = dev; > + pdevinfo.id = PLATFORM_DEVID_AUTO; > + > + if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) { > + audio.phys = iores->start; > + audio.base = hdmi->regs; > + audio.irq = irq; > + audio.hdmi = hdmi; > + > + pdevinfo.name = "dw-hdmi-ahb-audio"; > + pdevinfo.data = &audio; > + pdevinfo.size_data = sizeof(audio); > + pdevinfo.dma_mask = DMA_BIT_MASK(32); > + hdmi->audio = platform_device_register_full(&pdevinfo); > + } > + > dev_set_drvdata(dev, hdmi); > > return 0; > @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) > { > struct dw_hdmi *hdmi = dev_get_drvdata(dev); > > + if (hdmi->audio && !IS_ERR(hdmi->audio)) > + platform_device_unregister(hdmi->audio); > + > /* Disable all interrupts */ > hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); > > diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h > index 175dbc89a824..78e54e813212 100644 > --- a/drivers/gpu/drm/bridge/dw_hdmi.h > +++ b/drivers/gpu/drm/bridge/dw_hdmi.h > @@ -545,6 +545,9 @@ > #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 > > enum { > +/* CONFIG1_ID field values */ > + HDMI_CONFIG1_AHB = 0x01, > + > /* IH_FC_INT2 field values */ > HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, > HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, > -- > 2.1.0 > > _______________________________________________ > Alsa-devel mailing list > Alsa-devel@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel > From mboxrd@z Thu Jan 1 00:00:00 1970 From: Takashi Iwai Subject: Re: [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver Date: Fri, 14 Aug 2015 16:34:07 +0200 Message-ID: References: Mime-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Russell King Cc: Fabio Estevam , alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, Mark Brown , Yakir Yang , Andy Yan , linux-arm-kernel@lists.infradead.org List-Id: alsa-devel@alsa-project.org T24gRnJpLCAxNCBBdWcgMjAxNSAxNjowNDoyNSArMDIwMCwKUnVzc2VsbCBLaW5nIHdyb3RlOgo+ IAo+IEFkZCBBTFNBIGJhc2VkIEhETUkgQUhCIGF1ZGlvIGRyaXZlciBmb3IgZHdfaGRtaS4gIFRo ZSBvbmx5IGJ1ZmZlcgo+IGZvcm1hdCBzdXBwb3J0ZWQgYnkgdGhlIGhhcmR3YXJlIGlzIGl0cyBv d24gc3BlY2lhbCBJRUM5NTggYmFzZWQgZm9ybWF0LAo+IHdoaWNoIGlzIG5vdCBjb21wYXRpYmxl IHdpdGggYW55IEFMU0EgZm9ybWF0LiAgVG8gYXZvaWQgZG9pbmcgdG9vIG11Y2gKPiBkYXRhIG1h bmlwdWxhdGlvbiB3aXRoaW4gdGhlIGRyaXZlciwgd2Ugc3VwcG9ydCBvbmx5IEFMU0FzIElFQzk1 OCBMRSBhbmQKPiAyNC1iaXQgUENNIGZvcm1hdHMgZm9yIDIgdG8gNiBjaGFubmVscywgd2hpY2gg d2UgY29udmVydCB0byBpdHMgaGFyZHdhcmUKPiBmb3JtYXQuCj4gCj4gQSBtb3JlIGRlc2lyYWJs ZSBzb2x1dGlvbiB3b3VsZCBiZSB0byBoYXZlIHRoaXMgY29udmVyc2lvbiBpbiB1c2Vyc3BhY2Us Cj4gYnV0IEFMU0EgZG9lcyBub3QgYXBwZWFyIHRvIGFsbG93IHN1Y2ggdHJhbnNmb3JtYXRpb25z IG91dHNpZGUgb2YKPiBsaWJhc291bmQgaXRzZWxmLgo+IAo+IFNpZ25lZC1vZmYtYnk6IFJ1c3Nl bGwgS2luZyA8cm1rK2tlcm5lbEBhcm0ubGludXgub3JnLnVrPgo+IC0tLQo+IHYyOiB1cGRhdGVk IHdpdGggVGFrYXNoaSBJd2FpJ3MgY29tbWVudHMuLi4gYW5kIHdpdGggYSBmaXhlZCBDYzogbGlu ZS4KClJldmlld2VkLWJ5OiBUYWthc2hpIEl3YWkgPHRpd2FpQHN1c2UuZGU+CgoKdGhhbmtzLAoK VGFrYXNoaQoKCj4gIGRyaXZlcnMvZ3B1L2RybS9icmlkZ2UvS2NvbmZpZyAgICAgICAgICAgICB8 ICAxMCArCj4gIGRyaXZlcnMvZ3B1L2RybS9icmlkZ2UvTWFrZWZpbGUgICAgICAgICAgICB8ICAg MSArCj4gIGRyaXZlcnMvZ3B1L2RybS9icmlkZ2UvZHdfaGRtaS1haGItYXVkaW8uYyB8IDU3OSAr KysrKysrKysrKysrKysrKysrKysrKysrKysrKwo+ICBkcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3 X2hkbWktYXVkaW8uaCAgICAgfCAgMTMgKwo+ICBkcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3X2hk bWkuYyAgICAgICAgICAgfCAgMjQgKysKPiAgZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1p LmggICAgICAgICAgIHwgICAzICsKPiAgNiBmaWxlcyBjaGFuZ2VkLCA2MzAgaW5zZXJ0aW9ucygr KQo+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1pLWFo Yi1hdWRpby5jCj4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3 X2hkbWktYXVkaW8uaAo+IAo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL0tj b25maWcgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL0tjb25maWcKPiBpbmRleCBhY2VmMzIyMzc3 MmMuLjU2ZWQzNWZlMDczNCAxMDA2NDQKPiAtLS0gYS9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL0tj b25maWcKPiArKysgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL0tjb25maWcKPiBAQCAtMyw2ICsz LDE2IEBAIGNvbmZpZyBEUk1fRFdfSERNSQo+ICAJZGVwZW5kcyBvbiBEUk0KPiAgCXNlbGVjdCBE Uk1fS01TX0hFTFBFUgo+ICAKPiArY29uZmlnIERSTV9EV19IRE1JX0FIQl9BVURJTwo+ICsJdHJp c3RhdGUgIlN5bm9wc2lzIERlc2lnbndhcmUgQUhCIEF1ZGlvIGludGVyZmFjZSIKPiArCWRlcGVu ZHMgb24gRFJNX0RXX0hETUkgJiYgU05ECj4gKwlzZWxlY3QgU05EX1BDTQo+ICsJc2VsZWN0IFNO RF9QQ01fSUVDOTU4Cj4gKwloZWxwCj4gKwkgIFN1cHBvcnQgdGhlIEFIQiBBdWRpbyBpbnRlcmZh Y2Ugd2hpY2ggaXMgcGFydCBvZiB0aGUgU3lub3BzaXMKPiArCSAgRGVzaWdud2FyZSBIRE1JIGJs b2NrLiAgVGhpcyBpcyB1c2VkIGluIGNvbmp1bmN0aW9uIHdpdGgKPiArCSAgdGhlIGkuTVg2IEhE TUkgZHJpdmVyLgo+ICsKPiAgY29uZmlnIERSTV9QVE4zNDYwCj4gIAl0cmlzdGF0ZSAiUFROMzQ2 MCBEUC9MVkRTIGJyaWRnZSIKPiAgCWRlcGVuZHMgb24gRFJNCj4gZGlmZiAtLWdpdCBhL2RyaXZl cnMvZ3B1L2RybS9icmlkZ2UvTWFrZWZpbGUgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL01ha2Vm aWxlCj4gaW5kZXggOGRmZWJkOTg0MzcwLi5lYjgwZGJiYjgzNjUgMTAwNjQ0Cj4gLS0tIGEvZHJp dmVycy9ncHUvZHJtL2JyaWRnZS9NYWtlZmlsZQo+ICsrKyBiL2RyaXZlcnMvZ3B1L2RybS9icmlk Z2UvTWFrZWZpbGUKPiBAQCAtMywzICszLDQgQEAgY2NmbGFncy15IDo9IC1JaW5jbHVkZS9kcm0K PiAgb2JqLSQoQ09ORklHX0RSTV9QUzg2MjIpICs9IHBzODYyMi5vCj4gIG9iai0kKENPTkZJR19E Uk1fUFROMzQ2MCkgKz0gcHRuMzQ2MC5vCj4gIG9iai0kKENPTkZJR19EUk1fRFdfSERNSSkgKz0g ZHdfaGRtaS5vCj4gK29iai0kKENPTkZJR19EUk1fRFdfSERNSV9BSEJfQVVESU8pICs9IGR3X2hk bWktYWhiLWF1ZGlvLm8KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19o ZG1pLWFoYi1hdWRpby5jIGIvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1pLWFoYi1hdWRp by5jCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQKPiBpbmRleCAwMDAwMDAwMDAwMDAuLmJmMzc5MzEw MDA4YQo+IC0tLSAvZGV2L251bGwKPiArKysgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3X2hk bWktYWhiLWF1ZGlvLmMKPiBAQCAtMCwwICsxLDU3OSBAQAo+ICsvKgo+ICsgKiBEZXNpZ25XYXJl IEhETUkgYXVkaW8gZHJpdmVyCj4gKyAqCj4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3 YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5Cj4gKyAqIGl0IHVuZGVy IHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVyc2lvbiAyIGFz Cj4gKyAqIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLgo+ICsgKgo+ ICsgKiBXcml0dGVuIGFuZCB0ZXN0ZWQgYWdhaW5zdCB0aGUgRGVzaWdud2FyZSBIRE1JIFR4IGZv dW5kIGluIGlNWDYuCj4gKyAqLwo+ICsjaW5jbHVkZSA8bGludXgvaW8uaD4KPiArI2luY2x1ZGUg PGxpbnV4L2ludGVycnVwdC5oPgo+ICsjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+Cj4gKyNpbmNs dWRlIDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4KPiArI2luY2x1ZGUgPGRybS9icmlkZ2UvZHdf aGRtaS5oPgo+ICsKPiArI2luY2x1ZGUgPHNvdW5kL2Fzb3VuZGVmLmg+Cj4gKyNpbmNsdWRlIDxz b3VuZC9jb3JlLmg+Cj4gKyNpbmNsdWRlIDxzb3VuZC9pbml0dmFsLmg+Cj4gKyNpbmNsdWRlIDxz b3VuZC9wY20uaD4KPiArI2luY2x1ZGUgPHNvdW5kL3BjbV9pZWM5NTguaD4KPiArCj4gKyNpbmNs dWRlICJkd19oZG1pLWF1ZGlvLmgiCj4gKwo+ICsjZGVmaW5lIERSSVZFUl9OQU1FICJkdy1oZG1p LWFoYi1hdWRpbyIKPiArCj4gKy8qIFByb3ZpZGUgc29tZSBiaXRzIHJhdGhlciB0aGFuIGJpdCBv ZmZzZXRzICovCj4gK2VudW0gewo+ICsJSERNSV9BSEJfRE1BX0NPTkYwX1NXX0ZJRk9fUlNUID0g QklUKDcpLAo+ICsJSERNSV9BSEJfRE1BX0NPTkYwX0VOX0hMT0NLID0gQklUKDMpLAo+ICsJSERN SV9BSEJfRE1BX1NUQVJUX1NUQVJUID0gQklUKDApLAo+ICsJSERNSV9BSEJfRE1BX1NUT1BfU1RP UCA9IEJJVCgwKSwKPiArCUhETUlfSUhfTVVURV9BSEJETUFBVURfU1RBVDBfRVJST1IgPSBCSVQo NSksCj4gKwlIRE1JX0lIX01VVEVfQUhCRE1BQVVEX1NUQVQwX0xPU1QgPSBCSVQoNCksCj4gKwlI RE1JX0lIX01VVEVfQUhCRE1BQVVEX1NUQVQwX1JFVFJZID0gQklUKDMpLAo+ICsJSERNSV9JSF9N VVRFX0FIQkRNQUFVRF9TVEFUMF9ET05FID0gQklUKDIpLAo+ICsJSERNSV9JSF9NVVRFX0FIQkRN QUFVRF9TVEFUMF9CVUZGRlVMTCA9IEJJVCgxKSwKPiArCUhETUlfSUhfTVVURV9BSEJETUFBVURf U1RBVDBfQlVGRkVNUFRZID0gQklUKDApLAo+ICsJSERNSV9JSF9NVVRFX0FIQkRNQUFVRF9TVEFU MF9BTEwgPQo+ICsJCUhETUlfSUhfTVVURV9BSEJETUFBVURfU1RBVDBfRVJST1IgfAo+ICsJCUhE TUlfSUhfTVVURV9BSEJETUFBVURfU1RBVDBfTE9TVCB8Cj4gKwkJSERNSV9JSF9NVVRFX0FIQkRN QUFVRF9TVEFUMF9SRVRSWSB8Cj4gKwkJSERNSV9JSF9NVVRFX0FIQkRNQUFVRF9TVEFUMF9ET05F IHwKPiArCQlIRE1JX0lIX01VVEVfQUhCRE1BQVVEX1NUQVQwX0JVRkZGVUxMIHwKPiArCQlIRE1J X0lIX01VVEVfQUhCRE1BQVVEX1NUQVQwX0JVRkZFTVBUWSwKPiArCUhETUlfSUhfQUhCRE1BQVVE X1NUQVQwX0VSUk9SID0gQklUKDUpLAo+ICsJSERNSV9JSF9BSEJETUFBVURfU1RBVDBfTE9TVCA9 IEJJVCg0KSwKPiArCUhETUlfSUhfQUhCRE1BQVVEX1NUQVQwX1JFVFJZID0gQklUKDMpLAo+ICsJ SERNSV9JSF9BSEJETUFBVURfU1RBVDBfRE9ORSA9IEJJVCgyKSwKPiArCUhETUlfSUhfQUhCRE1B QVVEX1NUQVQwX0JVRkZGVUxMID0gQklUKDEpLAo+ICsJSERNSV9JSF9BSEJETUFBVURfU1RBVDBf QlVGRkVNUFRZID0gQklUKDApLAo+ICsJSERNSV9JSF9BSEJETUFBVURfU1RBVDBfQUxMID0KPiAr CQlIRE1JX0lIX0FIQkRNQUFVRF9TVEFUMF9FUlJPUiB8Cj4gKwkJSERNSV9JSF9BSEJETUFBVURf U1RBVDBfTE9TVCB8Cj4gKwkJSERNSV9JSF9BSEJETUFBVURfU1RBVDBfUkVUUlkgfAo+ICsJCUhE TUlfSUhfQUhCRE1BQVVEX1NUQVQwX0RPTkUgfAo+ICsJCUhETUlfSUhfQUhCRE1BQVVEX1NUQVQw X0JVRkZGVUxMIHwKPiArCQlIRE1JX0lIX0FIQkRNQUFVRF9TVEFUMF9CVUZGRU1QVFksCj4gKwlI RE1JX0FIQl9ETUFfQ09ORjBfSU5DUjE2ID0gMiA8PCAxLAo+ICsJSERNSV9BSEJfRE1BX0NPTkYw X0lOQ1I4ID0gMSA8PCAxLAo+ICsJSERNSV9BSEJfRE1BX0NPTkYwX0lOQ1I0ID0gMCwKPiArCUhE TUlfQUhCX0RNQV9DT05GMF9CVVJTVF9NT0RFID0gQklUKDApLAo+ICsJSERNSV9BSEJfRE1BX01B U0tfRE9ORSA9IEJJVCg3KSwKPiArCUhETUlfUkVWSVNJT05fSUQgPSAweDAwMDEsCj4gKwlIRE1J X0lIX0FIQkRNQUFVRF9TVEFUMCA9IDB4MDEwOSwKPiArCUhETUlfSUhfTVVURV9BSEJETUFBVURf U1RBVDAgPSAweDAxODksCj4gKwlIRE1JX0FIQl9ETUFfQ09ORjAgPSAweDM2MDAsCj4gKwlIRE1J X0FIQl9ETUFfU1RBUlQgPSAweDM2MDEsCj4gKwlIRE1JX0FIQl9ETUFfU1RPUCA9IDB4MzYwMiwK PiArCUhETUlfQUhCX0RNQV9USFJTTEQgPSAweDM2MDMsCj4gKwlIRE1JX0FIQl9ETUFfU1RSQURE UjAgPSAweDM2MDQsCj4gKwlIRE1JX0FIQl9ETUFfU1RQQUREUjAgPSAweDM2MDgsCj4gKwlIRE1J X0FIQl9ETUFfTUFTSyA9IDB4MzYxNCwKPiArCUhETUlfQUhCX0RNQV9QT0wgPSAweDM2MTUsCj4g KwlIRE1JX0FIQl9ETUFfQ09ORjEgPSAweDM2MTYsCj4gKwlIRE1JX0FIQl9ETUFfQlVGRlBPTCA9 IDB4MzYxYSwKPiArfTsKPiArCj4gK3N0cnVjdCBzbmRfZHdfaGRtaSB7Cj4gKwlzdHJ1Y3Qgc25k X2NhcmQgKmNhcmQ7Cj4gKwlzdHJ1Y3Qgc25kX3BjbSAqcGNtOwo+ICsJc3BpbmxvY2tfdCBsb2Nr Owo+ICsJc3RydWN0IGR3X2hkbWlfYXVkaW9fZGF0YSBkYXRhOwo+ICsJc3RydWN0IHNuZF9wY21f c3Vic3RyZWFtICpzdWJzdHJlYW07Cj4gKwl2b2lkICgqcmVmb3JtYXQpKHN0cnVjdCBzbmRfZHdf aGRtaSAqLCBzaXplX3QsIHNpemVfdCk7Cj4gKwl2b2lkICpidWZfc3JjOwo+ICsJdm9pZCAqYnVm X2RzdDsKPiArCWRtYV9hZGRyX3QgYnVmX2FkZHI7Cj4gKwl1bnNpZ25lZCBidWZfb2Zmc2V0Owo+ ICsJdW5zaWduZWQgYnVmX3BlcmlvZDsKPiArCXVuc2lnbmVkIGJ1Zl9zaXplOwo+ICsJdW5zaWdu ZWQgY2hhbm5lbHM7Cj4gKwl1OCByZXZpc2lvbjsKPiArCXU4IGllY19vZmZzZXQ7Cj4gKwl1OCBj c1sxOTJdWzhdOwo+ICt9Owo+ICsKPiArc3RhdGljIHZvaWQgZHdfaGRtaV93cml0ZWwodTMyIHZh bCwgdm9pZCBfX2lvbWVtICpwdHIpCj4gK3sKPiArCXdyaXRlYl9yZWxheGVkKHZhbCwgcHRyKTsK PiArCXdyaXRlYl9yZWxheGVkKHZhbCA+PiA4LCBwdHIgKyAxKTsKPiArCXdyaXRlYl9yZWxheGVk KHZhbCA+PiAxNiwgcHRyICsgMik7Cj4gKwl3cml0ZWJfcmVsYXhlZCh2YWwgPj4gMjQsIHB0ciAr IDMpOwo+ICt9Cj4gKwo+ICsvKgo+ICsgKiBDb252ZXJ0IHRvIGhhcmR3YXJlIGZvcm1hdDogVGhl IHVzZXJzcGFjZSBidWZmZXIgY29udGFpbnMgSUVDOTU4IHNhbXBsZXMsCj4gKyAqIHdpdGggdGhl IFBDVVYgYml0cyBpbiBiaXRzIDMxLi4yOCBhbmQgYXVkaW8gc2FtcGxlcyBpbiBiaXRzIDI3Li40 LiAgV2UKPiArICogbmVlZCB0aGVzZSB0byBiZSBpbiBiaXRzIDI3Li4yNCwgd2l0aCB0aGUgSUVD IEIgYml0IGluIGJpdCAyOCwgYW5kIGF1ZGlvCj4gKyAqIHNhbXBsZXMgaW4gMjMuLjAuCj4gKyAq Cj4gKyAqIERlZmF1bHQgcHJlYW1ibGUgaW4gYml0cyAzLi4wOiA4ID0gYmxvY2sgc3RhcnQsIDQg PSBldmVuIDIgPSBvZGQKPiArICoKPiArICogSWRlYWxseSwgd2UgY291bGQgZG8gd2l0aCBoYXZp bmcgdGhlIGRhdGEgcHJvcGVybHkgZm9ybWF0dGVkIGluIHVzZXJzcGFjZS4KPiArICovCj4gK3N0 YXRpYyB2b2lkIGR3X2hkbWlfcmVmb3JtYXRfaWVjOTU4KHN0cnVjdCBzbmRfZHdfaGRtaSAqZHcs Cj4gKwlzaXplX3Qgb2Zmc2V0LCBzaXplX3QgYnl0ZXMpCj4gK3sKPiArCXUzMiAqc3JjID0gZHct PmJ1Zl9zcmMgKyBvZmZzZXQ7Cj4gKwl1MzIgKmRzdCA9IGR3LT5idWZfZHN0ICsgb2Zmc2V0Owo+ ICsJdTMyICplbmQgPSBkdy0+YnVmX3NyYyArIG9mZnNldCArIGJ5dGVzOwo+ICsKPiArCWRvIHsK PiArCQl1MzIgYiwgc2FtcGxlID0gKnNyYysrOwo+ICsKPiArCQliID0gKHNhbXBsZSAmIDgpIDw8 ICgyOCAtIDMpOwo+ICsKPiArCQlzYW1wbGUgPj49IDQ7Cj4gKwo+ICsJCSpkc3QrKyA9IHNhbXBs ZSB8IGI7Cj4gKwl9IHdoaWxlIChzcmMgPCBlbmQpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdTMyIHBh cml0eSh1MzIgc2FtcGxlKQo+ICt7Cj4gKwlzYW1wbGUgXj0gc2FtcGxlID4+IDE2Owo+ICsJc2Ft cGxlIF49IHNhbXBsZSA+PiA4Owo+ICsJc2FtcGxlIF49IHNhbXBsZSA+PiA0Owo+ICsJc2FtcGxl IF49IHNhbXBsZSA+PiAyOwo+ICsJc2FtcGxlIF49IHNhbXBsZSA+PiAxOwo+ICsJcmV0dXJuIChz YW1wbGUgJiAxKSA8PCAyNzsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgZHdfaGRtaV9yZWZvcm1h dF9zMjQoc3RydWN0IHNuZF9kd19oZG1pICpkdywKPiArCXNpemVfdCBvZmZzZXQsIHNpemVfdCBi eXRlcykKPiArewo+ICsJdTMyICpzcmMgPSBkdy0+YnVmX3NyYyArIG9mZnNldDsKPiArCXUzMiAq ZHN0ID0gZHctPmJ1Zl9kc3QgKyBvZmZzZXQ7Cj4gKwl1MzIgKmVuZCA9IGR3LT5idWZfc3JjICsg b2Zmc2V0ICsgYnl0ZXM7Cj4gKwo+ICsJZG8gewo+ICsJCXVuc2lnbmVkIGk7Cj4gKwkJdTggKmNz Owo+ICsKPiArCQljcyA9IGR3LT5jc1tkdy0+aWVjX29mZnNldCsrXTsKPiArCQlpZiAoZHctPmll Y19vZmZzZXQgPj0gMTkyKQo+ICsJCQlkdy0+aWVjX29mZnNldCA9IDA7Cj4gKwo+ICsJCWkgPSBk dy0+Y2hhbm5lbHM7Cj4gKwkJZG8gewo+ICsJCQl1MzIgc2FtcGxlID0gKnNyYysrOwo+ICsKPiAr CQkJc2FtcGxlICY9IH4weGZmMDAwMDAwOwo+ICsJCQlzYW1wbGUgfD0gKmNzKysgPDwgMjQ7Cj4g KwkJCXNhbXBsZSB8PSBwYXJpdHkoc2FtcGxlICYgfjB4ZjgwMDAwMDApOwo+ICsKPiArCQkJKmRz dCsrID0gc2FtcGxlOwo+ICsJCX0gd2hpbGUgKC0taSk7Cj4gKwl9IHdoaWxlIChzcmMgPCBlbmQp Owo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBkd19oZG1pX2NyZWF0ZV9jcyhzdHJ1Y3Qgc25kX2R3 X2hkbWkgKmR3LAo+ICsJc3RydWN0IHNuZF9wY21fcnVudGltZSAqcnVudGltZSkKPiArewo+ICsJ dTggY3NbNF07Cj4gKwl1bnNpZ25lZCBjaCwgaSwgajsKPiArCj4gKwlzbmRfcGNtX2NyZWF0ZV9p ZWM5NThfY29uc3VtZXIocnVudGltZSwgY3MsIHNpemVvZihjcykpOwo+ICsKPiArCW1lbXNldChk dy0+Y3MsIDAsIHNpemVvZihkdy0+Y3MpKTsKPiArCj4gKwlmb3IgKGNoID0gMDsgY2ggPCA4OyBj aCsrKSB7Cj4gKwkJY3NbMl0gJj0gfklFQzk1OF9BRVMyX0NPTl9DSEFOTkVMOwo+ICsJCWNzWzJd IHw9IChjaCArIDEpIDw8IDQ7Cj4gKwo+ICsJCWZvciAoaSA9IDA7IGkgPCBBUlJBWV9TSVpFKGNz KTsgaSsrKSB7Cj4gKwkJCXVuc2lnbmVkIGMgPSBjc1tpXTsKPiArCj4gKwkJCWZvciAoaiA9IDA7 IGogPCA4OyBqKyssIGMgPj49IDEpCj4gKwkJCQlkdy0+Y3NbaSAqIDggKyBqXVtjaF0gPSAoYyAm IDEpIDw8IDI7Cj4gKwkJfQo+ICsJfQo+ICsJZHctPmNzWzBdWzBdIHw9IEJJVCg0KTsKPiArfQo+ ICsKPiArc3RhdGljIHZvaWQgZHdfaGRtaV9zdGFydF9kbWEoc3RydWN0IHNuZF9kd19oZG1pICpk dykKPiArewo+ICsJdm9pZCBfX2lvbWVtICpiYXNlID0gZHctPmRhdGEuYmFzZTsKPiArCXVuc2ln bmVkIG9mZnNldCA9IGR3LT5idWZfb2Zmc2V0Owo+ICsJdW5zaWduZWQgcGVyaW9kID0gZHctPmJ1 Zl9wZXJpb2Q7Cj4gKwl1MzIgc3RhcnQsIHN0b3A7Cj4gKwo+ICsJZHctPnJlZm9ybWF0KGR3LCBv ZmZzZXQsIHBlcmlvZCk7Cj4gKwo+ICsJLyogQ2xlYXIgYWxsIGlycXMgYmVmb3JlIGVuYWJsaW5n IGlycXMgYW5kIHN0YXJ0aW5nIERNQSAqLwo+ICsJd3JpdGViX3JlbGF4ZWQoSERNSV9JSF9BSEJE TUFBVURfU1RBVDBfQUxMLAo+ICsJCSAgICAgICBiYXNlICsgSERNSV9JSF9BSEJETUFBVURfU1RB VDApOwo+ICsKPiArCXN0YXJ0ID0gZHctPmJ1Zl9hZGRyICsgb2Zmc2V0Owo+ICsJc3RvcCA9IHN0 YXJ0ICsgcGVyaW9kIC0gMTsKPiArCj4gKwkvKiBTZXR1cCB0aGUgaGFyZHdhcmUgc3RhcnQvc3Rv cCBhZGRyZXNzZXMgKi8KPiArCWR3X2hkbWlfd3JpdGVsKHN0YXJ0LCBiYXNlICsgSERNSV9BSEJf RE1BX1NUUkFERFIwKTsKPiArCWR3X2hkbWlfd3JpdGVsKHN0b3AsIGJhc2UgKyBIRE1JX0FIQl9E TUFfU1RQQUREUjApOwo+ICsKPiArCXdyaXRlYl9yZWxheGVkKCh1OCl+SERNSV9BSEJfRE1BX01B U0tfRE9ORSwgYmFzZSArIEhETUlfQUhCX0RNQV9NQVNLKTsKPiArCXdyaXRlYihIRE1JX0FIQl9E TUFfU1RBUlRfU1RBUlQsIGJhc2UgKyBIRE1JX0FIQl9ETUFfU1RBUlQpOwo+ICsKPiArCW9mZnNl dCArPSBwZXJpb2Q7Cj4gKwlpZiAob2Zmc2V0ID49IGR3LT5idWZfc2l6ZSkKPiArCQlvZmZzZXQg PSAwOwo+ICsJZHctPmJ1Zl9vZmZzZXQgPSBvZmZzZXQ7Cj4gK30KPiArCj4gK3N0YXRpYyB2b2lk IGR3X2hkbWlfc3RvcF9kbWEoc3RydWN0IHNuZF9kd19oZG1pICpkdykKPiArewo+ICsJLyogRGlz YWJsZSBpbnRlcnJ1cHRzIGJlZm9yZSBkaXNhYmxpbmcgRE1BICovCj4gKwl3cml0ZWJfcmVsYXhl ZCh+MCwgZHctPmRhdGEuYmFzZSArIEhETUlfQUhCX0RNQV9NQVNLKTsKPiArCXdyaXRlYl9yZWxh eGVkKEhETUlfQUhCX0RNQV9TVE9QX1NUT1AsIGR3LT5kYXRhLmJhc2UgKyBIRE1JX0FIQl9ETUFf U1RPUCk7Cj4gK30KPiArCj4gK3N0YXRpYyBpcnFyZXR1cm5fdCBzbmRfZHdfaGRtaV9pcnEoaW50 IGlycSwgdm9pZCAqZGF0YSkKPiArewo+ICsJc3RydWN0IHNuZF9kd19oZG1pICpkdyA9IGRhdGE7 Cj4gKwlzdHJ1Y3Qgc25kX3BjbV9zdWJzdHJlYW0gKnN1YnN0cmVhbTsKPiArCXVuc2lnbmVkIHN0 YXQ7Cj4gKwo+ICsJc3RhdCA9IHJlYWRiX3JlbGF4ZWQoZHctPmRhdGEuYmFzZSArIEhETUlfSUhf QUhCRE1BQVVEX1NUQVQwKTsKPiArCWlmICghc3RhdCkKPiArCQlyZXR1cm4gSVJRX05PTkU7Cj4g Kwo+ICsJd3JpdGViX3JlbGF4ZWQoc3RhdCwgZHctPmRhdGEuYmFzZSArIEhETUlfSUhfQUhCRE1B QVVEX1NUQVQwKTsKPiArCj4gKwlzdWJzdHJlYW0gPSBkdy0+c3Vic3RyZWFtOwo+ICsJaWYgKHN0 YXQgJiBIRE1JX0lIX0FIQkRNQUFVRF9TVEFUMF9ET05FICYmIHN1YnN0cmVhbSkgewo+ICsJCXNu ZF9wY21fcGVyaW9kX2VsYXBzZWQoc3Vic3RyZWFtKTsKPiArCj4gKwkJc3Bpbl9sb2NrKCZkdy0+ bG9jayk7Cj4gKwkJaWYgKGR3LT5zdWJzdHJlYW0pCj4gKwkJCWR3X2hkbWlfc3RhcnRfZG1hKGR3 KTsKPiArCQlzcGluX3VubG9jaygmZHctPmxvY2spOwo+ICsJfQo+ICsKPiArCXJldHVybiBJUlFf SEFORExFRDsKPiArfQo+ICsKPiArc3RhdGljIHN0cnVjdCBzbmRfcGNtX2hhcmR3YXJlIGR3X2hk bWlfaHcgPSB7Cj4gKwkuaW5mbyA9IFNORFJWX1BDTV9JTkZPX0lOVEVSTEVBVkVEIHwKPiArCQlT TkRSVl9QQ01fSU5GT19CTE9DS19UUkFOU0ZFUiB8Cj4gKwkJU05EUlZfUENNX0lORk9fTU1BUCB8 Cj4gKwkJU05EUlZfUENNX0lORk9fTU1BUF9WQUxJRCwKPiArCS5mb3JtYXRzID0gU05EUlZfUENN X0ZNVEJJVF9JRUM5NThfU1VCRlJBTUVfTEUgfAo+ICsJCSAgIFNORFJWX1BDTV9GTVRCSVRfUzI0 X0xFLAo+ICsJLnJhdGVzID0gU05EUlZfUENNX1JBVEVfMzIwMDAgfAo+ICsJCSBTTkRSVl9QQ01f UkFURV80NDEwMCB8Cj4gKwkJIFNORFJWX1BDTV9SQVRFXzQ4MDAwIHwKPiArCQkgU05EUlZfUENN X1JBVEVfODgyMDAgfAo+ICsJCSBTTkRSVl9QQ01fUkFURV85NjAwMCB8Cj4gKwkJIFNORFJWX1BD TV9SQVRFXzE3NjQwMCB8Cj4gKwkJIFNORFJWX1BDTV9SQVRFXzE5MjAwMCwKPiArCS5jaGFubmVs c19taW4gPSAyLAo+ICsJLmNoYW5uZWxzX21heCA9IDgsCj4gKwkuYnVmZmVyX2J5dGVzX21heCA9 IDY0ICogMTAyNCwKPiArCS5wZXJpb2RfYnl0ZXNfbWluID0gMjU2LAo+ICsJLnBlcmlvZF9ieXRl c19tYXggPSA4MTkyLAkvKiBFUlIwMDQzMjM6IG11c3QgbGltaXQgdG8gOGsgKi8KPiArCS5wZXJp b2RzX21pbiA9IDIsCj4gKwkucGVyaW9kc19tYXggPSAxNiwKPiArCS5maWZvX3NpemUgPSAwLAo+ ICt9Owo+ICsKPiArc3RhdGljIGludCBkd19oZG1pX29wZW4oc3RydWN0IHNuZF9wY21fc3Vic3Ry ZWFtICpzdWJzdHJlYW0pCj4gK3sKPiArCXN0cnVjdCBzbmRfcGNtX3J1bnRpbWUgKnJ1bnRpbWUg PSBzdWJzdHJlYW0tPnJ1bnRpbWU7Cj4gKwlzdHJ1Y3Qgc25kX2R3X2hkbWkgKmR3ID0gc3Vic3Ry ZWFtLT5wcml2YXRlX2RhdGE7Cj4gKwl2b2lkIF9faW9tZW0gKmJhc2UgPSBkdy0+ZGF0YS5iYXNl Owo+ICsJaW50IHJldDsKPiArCj4gKwlydW50aW1lLT5odyA9IGR3X2hkbWlfaHc7Cj4gKwo+ICsJ cmV0ID0gc25kX3BjbV9saW1pdF9od19yYXRlcyhydW50aW1lKTsKPiArCWlmIChyZXQgPCAwKQo+ ICsJCXJldHVybiByZXQ7Cj4gKwo+ICsJcmV0ID0gc25kX3BjbV9od19jb25zdHJhaW50X2ludGVn ZXIocnVudGltZSwgU05EUlZfUENNX0hXX1BBUkFNX1BFUklPRFMpOwo+ICsJaWYgKHJldCA8IDAp Cj4gKwkJcmV0dXJuIHJldDsKPiArCj4gKwkvKiBDbGVhciBGSUZPICovCj4gKwl3cml0ZWJfcmVs YXhlZChIRE1JX0FIQl9ETUFfQ09ORjBfU1dfRklGT19SU1QsCj4gKwkJICAgICAgIGJhc2UgKyBI RE1JX0FIQl9ETUFfQ09ORjApOwo+ICsKPiArCS8qIENvbmZpZ3VyZSBpbnRlcnJ1cHQgcG9sYXJp dGllcyAqLwo+ICsJd3JpdGViX3JlbGF4ZWQofjAsIGJhc2UgKyBIRE1JX0FIQl9ETUFfUE9MKTsK PiArCXdyaXRlYl9yZWxheGVkKH4wLCBiYXNlICsgSERNSV9BSEJfRE1BX0JVRkZQT0wpOwo+ICsK PiArCS8qIEtlZXAgaW50ZXJydXB0cyBtYXNrZWQsIGFuZCBjbGVhciBhbnkgcGVuZGluZyAqLwo+ ICsJd3JpdGViX3JlbGF4ZWQofjAsIGJhc2UgKyBIRE1JX0FIQl9ETUFfTUFTSyk7Cj4gKwl3cml0 ZWJfcmVsYXhlZCh+MCwgYmFzZSArIEhETUlfSUhfQUhCRE1BQVVEX1NUQVQwKTsKPiArCj4gKwly ZXQgPSByZXF1ZXN0X2lycShkdy0+ZGF0YS5pcnEsIHNuZF9kd19oZG1pX2lycSwgSVJRRl9TSEFS RUQsCj4gKwkJCSAgImR3LWhkbWktYXVkaW8iLCBkdyk7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVy biByZXQ7Cj4gKwo+ICsJLyogVW4tbXV0ZSBkb25lIGludGVycnVwdCAqLwo+ICsJd3JpdGViX3Jl bGF4ZWQoSERNSV9JSF9NVVRFX0FIQkRNQUFVRF9TVEFUMF9BTEwgJgo+ICsJCSAgICAgICB+SERN SV9JSF9NVVRFX0FIQkRNQUFVRF9TVEFUMF9ET05FLAo+ICsJCSAgICAgICBiYXNlICsgSERNSV9J SF9NVVRFX0FIQkRNQUFVRF9TVEFUMCk7Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArCj4gK3N0 YXRpYyBpbnQgZHdfaGRtaV9jbG9zZShzdHJ1Y3Qgc25kX3BjbV9zdWJzdHJlYW0gKnN1YnN0cmVh bSkKPiArewo+ICsJc3RydWN0IHNuZF9kd19oZG1pICpkdyA9IHN1YnN0cmVhbS0+cHJpdmF0ZV9k YXRhOwo+ICsKPiArCS8qIE11dGUgYWxsIGludGVycnVwdHMgKi8KPiArCXdyaXRlYl9yZWxheGVk KEhETUlfSUhfTVVURV9BSEJETUFBVURfU1RBVDBfQUxMLAo+ICsJCSAgICAgICBkdy0+ZGF0YS5i YXNlICsgSERNSV9JSF9NVVRFX0FIQkRNQUFVRF9TVEFUMCk7Cj4gKwo+ICsJZnJlZV9pcnEoZHct PmRhdGEuaXJxLCBkdyk7Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQg ZHdfaGRtaV9od19mcmVlKHN0cnVjdCBzbmRfcGNtX3N1YnN0cmVhbSAqc3Vic3RyZWFtKQo+ICt7 Cj4gKwlyZXR1cm4gc25kX3BjbV9saWJfZnJlZV92bWFsbG9jX2J1ZmZlcihzdWJzdHJlYW0pOwo+ ICt9Cj4gKwo+ICtzdGF0aWMgaW50IGR3X2hkbWlfaHdfcGFyYW1zKHN0cnVjdCBzbmRfcGNtX3N1 YnN0cmVhbSAqc3Vic3RyZWFtLAo+ICsJc3RydWN0IHNuZF9wY21faHdfcGFyYW1zICpwYXJhbXMp Cj4gK3sKPiArCXJldHVybiBzbmRfcGNtX2xpYl9hbGxvY192bWFsbG9jX2J1ZmZlcihzdWJzdHJl YW0sCj4gKwkJCQkJCXBhcmFtc19idWZmZXJfYnl0ZXMocGFyYW1zKSk7Cj4gK30KPiArCj4gK3N0 YXRpYyBpbnQgZHdfaGRtaV9wcmVwYXJlKHN0cnVjdCBzbmRfcGNtX3N1YnN0cmVhbSAqc3Vic3Ry ZWFtKQo+ICt7Cj4gKwlzdHJ1Y3Qgc25kX3BjbV9ydW50aW1lICpydW50aW1lID0gc3Vic3RyZWFt LT5ydW50aW1lOwo+ICsJc3RydWN0IHNuZF9kd19oZG1pICpkdyA9IHN1YnN0cmVhbS0+cHJpdmF0 ZV9kYXRhOwo+ICsJdTggdGhyZXNob2xkLCBjb25mMCwgY29uZjE7Cj4gKwo+ICsJLyogU2V0dXAg YXMgcGVyIDMuMC41IEZTTCA0LjEuMCBCU1AgKi8KPiArCXN3aXRjaCAoZHctPnJldmlzaW9uKSB7 Cj4gKwljYXNlIDB4MGE6Cj4gKwkJY29uZjAgPSBIRE1JX0FIQl9ETUFfQ09ORjBfQlVSU1RfTU9E RSB8Cj4gKwkJCUhETUlfQUhCX0RNQV9DT05GMF9JTkNSNDsKPiArCQlpZiAocnVudGltZS0+Y2hh bm5lbHMgPT0gMikKPiArCQkJdGhyZXNob2xkID0gMTI2Owo+ICsJCWVsc2UKPiArCQkJdGhyZXNo b2xkID0gMTI0Owo+ICsJCWJyZWFrOwo+ICsJY2FzZSAweDFhOgo+ICsJCWNvbmYwID0gSERNSV9B SEJfRE1BX0NPTkYwX0JVUlNUX01PREUgfAo+ICsJCQlIRE1JX0FIQl9ETUFfQ09ORjBfSU5DUjg7 Cj4gKwkJdGhyZXNob2xkID0gMTI4Owo+ICsJCWJyZWFrOwo+ICsJZGVmYXVsdDoKPiArCQkvKiBO T1RSRUFDSEVEICovCj4gKwkJcmV0dXJuIC1FSU5WQUw7Cj4gKwl9Cj4gKwo+ICsJZHdfaGRtaV9z ZXRfc2FtcGxlX3JhdGUoZHctPmRhdGEuaGRtaSwgcnVudGltZS0+cmF0ZSk7Cj4gKwo+ICsJLyog TWluaW11bSBudW1iZXIgb2YgYnl0ZXMgaW4gdGhlIGZpZm8uICovCj4gKwlydW50aW1lLT5ody5m aWZvX3NpemUgPSB0aHJlc2hvbGQgKiAzMjsKPiArCj4gKwljb25mMCB8PSBIRE1JX0FIQl9ETUFf Q09ORjBfRU5fSExPQ0s7Cj4gKwljb25mMSA9ICgxIDw8IHJ1bnRpbWUtPmNoYW5uZWxzKSAtIDE7 Cj4gKwo+ICsJd3JpdGViX3JlbGF4ZWQodGhyZXNob2xkLCBkdy0+ZGF0YS5iYXNlICsgSERNSV9B SEJfRE1BX1RIUlNMRCk7Cj4gKwl3cml0ZWJfcmVsYXhlZChjb25mMCwgZHctPmRhdGEuYmFzZSAr IEhETUlfQUhCX0RNQV9DT05GMCk7Cj4gKwl3cml0ZWJfcmVsYXhlZChjb25mMSwgZHctPmRhdGEu YmFzZSArIEhETUlfQUhCX0RNQV9DT05GMSk7Cj4gKwo+ICsJc3dpdGNoIChydW50aW1lLT5mb3Jt YXQpIHsKPiArCWNhc2UgU05EUlZfUENNX0ZPUk1BVF9JRUM5NThfU1VCRlJBTUVfTEU6Cj4gKwkJ ZHctPnJlZm9ybWF0ID0gZHdfaGRtaV9yZWZvcm1hdF9pZWM5NTg7Cj4gKwkJYnJlYWs7Cj4gKwlj YXNlIFNORFJWX1BDTV9GT1JNQVRfUzI0X0xFOgo+ICsJCWR3X2hkbWlfY3JlYXRlX2NzKGR3LCBy dW50aW1lKTsKPiArCQlkdy0+cmVmb3JtYXQgPSBkd19oZG1pX3JlZm9ybWF0X3MyNDsKPiArCQli cmVhazsKPiArCX0KPiArCWR3LT5pZWNfb2Zmc2V0ID0gMDsKPiArCWR3LT5jaGFubmVscyA9IHJ1 bnRpbWUtPmNoYW5uZWxzOwo+ICsJZHctPmJ1Zl9zcmMgID0gcnVudGltZS0+ZG1hX2FyZWE7Cj4g Kwlkdy0+YnVmX2RzdCAgPSBzdWJzdHJlYW0tPmRtYV9idWZmZXIuYXJlYTsKPiArCWR3LT5idWZf YWRkciA9IHN1YnN0cmVhbS0+ZG1hX2J1ZmZlci5hZGRyOwo+ICsJZHctPmJ1Zl9wZXJpb2QgPSBz bmRfcGNtX2xpYl9wZXJpb2RfYnl0ZXMoc3Vic3RyZWFtKTsKPiArCWR3LT5idWZfc2l6ZSA9IHNu ZF9wY21fbGliX2J1ZmZlcl9ieXRlcyhzdWJzdHJlYW0pOwo+ICsKPiArCXJldHVybiAwOwo+ICt9 Cj4gKwo+ICtzdGF0aWMgaW50IGR3X2hkbWlfdHJpZ2dlcihzdHJ1Y3Qgc25kX3BjbV9zdWJzdHJl YW0gKnN1YnN0cmVhbSwgaW50IGNtZCkKPiArewo+ICsJc3RydWN0IHNuZF9kd19oZG1pICpkdyA9 IHN1YnN0cmVhbS0+cHJpdmF0ZV9kYXRhOwo+ICsJdW5zaWduZWQgbG9uZyBmbGFnczsKPiArCWlu dCByZXQgPSAwOwo+ICsKPiArCXN3aXRjaCAoY21kKSB7Cj4gKwljYXNlIFNORFJWX1BDTV9UUklH R0VSX1NUQVJUOgo+ICsJCXNwaW5fbG9ja19pcnFzYXZlKCZkdy0+bG9jaywgZmxhZ3MpOwo+ICsJ CWR3LT5idWZfb2Zmc2V0ID0gMDsKPiArCQlkdy0+c3Vic3RyZWFtID0gc3Vic3RyZWFtOwo+ICsJ CWR3X2hkbWlfc3RhcnRfZG1hKGR3KTsKPiArCQlkd19oZG1pX2F1ZGlvX2VuYWJsZShkdy0+ZGF0 YS5oZG1pKTsKPiArCQlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZkdy0+bG9jaywgZmxhZ3MpOwo+ ICsJCXN1YnN0cmVhbS0+cnVudGltZS0+ZGVsYXkgPSBzdWJzdHJlYW0tPnJ1bnRpbWUtPnBlcmlv ZF9zaXplOwo+ICsJCWJyZWFrOwo+ICsKPiArCWNhc2UgU05EUlZfUENNX1RSSUdHRVJfU1RPUDoK PiArCQlzcGluX2xvY2tfaXJxc2F2ZSgmZHctPmxvY2ssIGZsYWdzKTsKPiArCQlkdy0+c3Vic3Ry ZWFtID0gTlVMTDsKPiArCQlkd19oZG1pX3N0b3BfZG1hKGR3KTsKPiArCQlkd19oZG1pX2F1ZGlv X2Rpc2FibGUoZHctPmRhdGEuaGRtaSk7Cj4gKwkJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmZHct PmxvY2ssIGZsYWdzKTsKPiArCQlicmVhazsKPiArCj4gKwlkZWZhdWx0Ogo+ICsJCXJldCA9IC1F SU5WQUw7Cj4gKwkJYnJlYWs7Cj4gKwl9Cj4gKwo+ICsJcmV0dXJuIHJldDsKPiArfQo+ICsKPiAr c3RhdGljIHNuZF9wY21fdWZyYW1lc190IGR3X2hkbWlfcG9pbnRlcihzdHJ1Y3Qgc25kX3BjbV9z dWJzdHJlYW0gKnN1YnN0cmVhbSkKPiArewo+ICsJc3RydWN0IHNuZF9wY21fcnVudGltZSAqcnVu dGltZSA9IHN1YnN0cmVhbS0+cnVudGltZTsKPiArCXN0cnVjdCBzbmRfZHdfaGRtaSAqZHcgPSBz dWJzdHJlYW0tPnByaXZhdGVfZGF0YTsKPiArCj4gKwkvKgo+ICsJICogV2UgYXJlIHVuYWJsZSB0 byByZXBvcnQgdGhlIGV4YWN0IGhhcmR3YXJlIHBvc2l0aW9uIGFzCj4gKwkgKiByZWFkaW5nIHRo ZSAzMi1iaXQgRE1BIHBvc2l0aW9uIHVzaW5nIDgtYml0IHJlYWRzIGlzIHJhY3kuCj4gKwkgKi8K PiArCXJldHVybiBieXRlc190b19mcmFtZXMocnVudGltZSwgZHctPmJ1Zl9vZmZzZXQpOwo+ICt9 Cj4gKwo+ICtzdGF0aWMgc3RydWN0IHNuZF9wY21fb3BzIHNuZF9kd19oZG1pX29wcyA9IHsKPiAr CS5vcGVuID0gZHdfaGRtaV9vcGVuLAo+ICsJLmNsb3NlID0gZHdfaGRtaV9jbG9zZSwKPiArCS5p b2N0bCA9IHNuZF9wY21fbGliX2lvY3RsLAo+ICsJLmh3X3BhcmFtcyA9IGR3X2hkbWlfaHdfcGFy YW1zLAo+ICsJLmh3X2ZyZWUgPSBkd19oZG1pX2h3X2ZyZWUsCj4gKwkucHJlcGFyZSA9IGR3X2hk bWlfcHJlcGFyZSwKPiArCS50cmlnZ2VyID0gZHdfaGRtaV90cmlnZ2VyLAo+ICsJLnBvaW50ZXIg PSBkd19oZG1pX3BvaW50ZXIsCj4gKwkucGFnZSA9IHNuZF9wY21fbGliX2dldF92bWFsbG9jX3Bh Z2UsCj4gK307Cj4gKwo+ICtzdGF0aWMgaW50IHNuZF9kd19oZG1pX3Byb2JlKHN0cnVjdCBwbGF0 Zm9ybV9kZXZpY2UgKnBkZXYpCj4gK3sKPiArCWNvbnN0IHN0cnVjdCBkd19oZG1pX2F1ZGlvX2Rh dGEgKmRhdGEgPSBwZGV2LT5kZXYucGxhdGZvcm1fZGF0YTsKPiArCXN0cnVjdCBkZXZpY2UgKmRl diA9IHBkZXYtPmRldi5wYXJlbnQ7Cj4gKwlzdHJ1Y3Qgc25kX2R3X2hkbWkgKmR3Owo+ICsJc3Ry dWN0IHNuZF9jYXJkICpjYXJkOwo+ICsJc3RydWN0IHNuZF9wY20gKnBjbTsKPiArCXVuc2lnbmVk IHJldmlzaW9uOwo+ICsJaW50IHJldDsKPiArCj4gKwl3cml0ZWJfcmVsYXhlZChIRE1JX0lIX01V VEVfQUhCRE1BQVVEX1NUQVQwX0FMTCwKPiArCQkgICAgICAgZGF0YS0+YmFzZSArIEhETUlfSUhf TVVURV9BSEJETUFBVURfU1RBVDApOwo+ICsJcmV2aXNpb24gPSByZWFkYl9yZWxheGVkKGRhdGEt PmJhc2UgKyBIRE1JX1JFVklTSU9OX0lEKTsKPiArCWlmIChyZXZpc2lvbiAhPSAweDBhICYmIHJl dmlzaW9uICE9IDB4MWEpIHsKPiArCQlkZXZfZXJyKGRldiwgImR3LWhkbWktYXVkaW86IHVua25v d24gcmV2aXNpb24gMHglMDJ4XG4iLAo+ICsJCQlyZXZpc2lvbik7Cj4gKwkJcmV0dXJuIC1FTlhJ TzsKPiArCX0KPiArCj4gKwlyZXQgPSBzbmRfY2FyZF9uZXcoZGV2LCBTTkRSVl9ERUZBVUxUX0lE WDEsIFNORFJWX0RFRkFVTFRfU1RSMSwKPiArCQkJICAgICAgVEhJU19NT0RVTEUsIHNpemVvZihz dHJ1Y3Qgc25kX2R3X2hkbWkpLCAmY2FyZCk7Cj4gKwlpZiAocmV0IDwgMCkKPiArCQlyZXR1cm4g cmV0Owo+ICsKPiArCXN0cmxjcHkoY2FyZC0+ZHJpdmVyLCBEUklWRVJfTkFNRSwgc2l6ZW9mKGNh cmQtPmRyaXZlcikpOwo+ICsJc3RybGNweShjYXJkLT5zaG9ydG5hbWUsICJEVy1IRE1JIiwgc2l6 ZW9mKGNhcmQtPnNob3J0bmFtZSkpOwo+ICsJc25wcmludGYoY2FyZC0+bG9uZ25hbWUsIHNpemVv ZihjYXJkLT5sb25nbmFtZSksCj4gKwkJICIlcyByZXYgMHglMDJ4LCBpcnEgJWQiLCBjYXJkLT5z aG9ydG5hbWUsIHJldmlzaW9uLAo+ICsJCSBkYXRhLT5pcnEpOwo+ICsKPiArCWR3ID0gY2FyZC0+ cHJpdmF0ZV9kYXRhOwo+ICsJZHctPmNhcmQgPSBjYXJkOwo+ICsJZHctPmRhdGEgPSAqZGF0YTsK PiArCWR3LT5yZXZpc2lvbiA9IHJldmlzaW9uOwo+ICsKPiArCXNwaW5fbG9ja19pbml0KCZkdy0+ bG9jayk7Cj4gKwo+ICsJcmV0ID0gc25kX3BjbV9uZXcoY2FyZCwgIkRXIEhETUkiLCAwLCAxLCAw LCAmcGNtKTsKPiArCWlmIChyZXQgPCAwKQo+ICsJCWdvdG8gZXJyOwo+ICsKPiArCWR3LT5wY20g PSBwY207Cj4gKwlwY20tPnByaXZhdGVfZGF0YSA9IGR3Owo+ICsJc3RybGNweShwY20tPm5hbWUs IERSSVZFUl9OQU1FLCBzaXplb2YocGNtLT5uYW1lKSk7Cj4gKwlzbmRfcGNtX3NldF9vcHMocGNt LCBTTkRSVl9QQ01fU1RSRUFNX1BMQVlCQUNLLCAmc25kX2R3X2hkbWlfb3BzKTsKPiArCj4gKwlz bmRfcGNtX2xpYl9wcmVhbGxvY2F0ZV9wYWdlc19mb3JfYWxsKHBjbSwgU05EUlZfRE1BX1RZUEVf REVWLAo+ICsJCQlkZXYsIDY0ICogMTAyNCwgNjQgKiAxMDI0KTsKPiArCj4gKwlyZXQgPSBzbmRf Y2FyZF9yZWdpc3RlcihjYXJkKTsKPiArCWlmIChyZXQgPCAwKQo+ICsJCWdvdG8gZXJyOwo+ICsK PiArCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIGR3KTsKPiArCj4gKwlyZXR1cm4gMDsKPiAr Cj4gK2VycjoKPiArCXNuZF9jYXJkX2ZyZWUoY2FyZCk7Cj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4g Kwo+ICtzdGF0aWMgaW50IHNuZF9kd19oZG1pX3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNl ICpwZGV2KQo+ICt7Cj4gKwlzdHJ1Y3Qgc25kX2R3X2hkbWkgKmR3ID0gcGxhdGZvcm1fZ2V0X2Ry dmRhdGEocGRldik7Cj4gKwo+ICsJc25kX2NhcmRfZnJlZShkdy0+Y2FyZCk7Cj4gKwo+ICsJcmV0 dXJuIDA7Cj4gK30KPiArCj4gKyNpZiBkZWZpbmVkKENPTkZJR19QTV9TTEVFUCkgJiYgZGVmaW5l ZChJU19OT1RfQlJPS0VOKQo+ICsvKgo+ICsgKiBUaGlzIGNvZGUgaXMgZmluZSwgYnV0IHJlcXVp cmVzIGltcGxlbWVudGF0aW9uIGluIHRoZSBkd19oZG1pX3RyaWdnZXIoKQo+ICsgKiBtZXRob2Qg d2hpY2ggaXMgY3VycmVudGx5IG1pc3NpbmcgYXMgSSBoYXZlIG5vIHdheSB0byB0ZXN0IHRoaXMu Cj4gKyAqLwo+ICtzdGF0aWMgaW50IHNuZF9kd19oZG1pX3N1c3BlbmQoc3RydWN0IGRldmljZSAq ZGV2KQo+ICt7Cj4gKwlzdHJ1Y3Qgc25kX2R3X2hkbWkgKmR3ID0gZGV2X2dldF9kcnZkYXRhKGRl dik7Cj4gKwo+ICsJc25kX3Bvd2VyX2NoYW5nZV9zdGF0ZShkdy0+Y2FyZCwgU05EUlZfQ1RMX1BP V0VSX0QzY29sZCk7Cj4gKwlzbmRfcGNtX3N1c3BlbmRfYWxsKGR3LT5wY20pOwo+ICsKPiArCXJl dHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IHNuZF9kd19oZG1pX3Jlc3VtZShzdHJ1Y3Qg ZGV2aWNlICpkZXYpCj4gK3sKPiArCXN0cnVjdCBzbmRfZHdfaGRtaSAqZHcgPSBkZXZfZ2V0X2Ry dmRhdGEoZGV2KTsKPiArCj4gKwlzbmRfcG93ZXJfY2hhbmdlX3N0YXRlKGR3LT5jYXJkLCBTTkRS Vl9DVExfUE9XRVJfRDApOwo+ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMgU0lN UExFX0RFVl9QTV9PUFMoc25kX2R3X2hkbWlfcG0sIHNuZF9kd19oZG1pX3N1c3BlbmQsCj4gKwkJ CSBzbmRfZHdfaGRtaV9yZXN1bWUpOwo+ICsjZGVmaW5lIFBNX09QUyAmc25kX2R3X2hkbWlfcG0K PiArI2Vsc2UKPiArI2RlZmluZSBQTV9PUFMgTlVMTAo+ICsjZW5kaWYKPiArCj4gK3N0YXRpYyBz dHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIHNuZF9kd19oZG1pX2RyaXZlciA9IHsKPiArCS5wcm9iZQk9 IHNuZF9kd19oZG1pX3Byb2JlLAo+ICsJLnJlbW92ZQk9IHNuZF9kd19oZG1pX3JlbW92ZSwKPiAr CS5kcml2ZXIJPSB7Cj4gKwkJLm5hbWUgPSBEUklWRVJfTkFNRSwKPiArCQkub3duZXIgPSBUSElT X01PRFVMRSwKPiArCQkucG0gPSBQTV9PUFMsCj4gKwl9LAo+ICt9Owo+ICsKPiArbW9kdWxlX3Bs YXRmb3JtX2RyaXZlcihzbmRfZHdfaGRtaV9kcml2ZXIpOwo+ICsKPiArTU9EVUxFX0FVVEhPUigi UnVzc2VsbCBLaW5nIDxybWsra2VybmVsQGFybS5saW51eC5vcmcudWs+Iik7Cj4gK01PRFVMRV9E RVNDUklQVElPTigiU3lub3BzaXMgRGVzaWdud2FyZSBIRE1JIEFIQiBBTFNBIGludGVyZmFjZSIp Owo+ICtNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7Cj4gK01PRFVMRV9BTElBUygicGxhdGZvcm06 IiBEUklWRVJfTkFNRSk7Cj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9icmlkZ2UvZHdf aGRtaS1hdWRpby5oIGIvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1pLWF1ZGlvLmgKPiBu ZXcgZmlsZSBtb2RlIDEwMDY0NAo+IGluZGV4IDAwMDAwMDAwMDAwMC4uMWU4NDAxMThkOTBhCj4g LS0tIC9kZXYvbnVsbAo+ICsrKyBiL2RyaXZlcnMvZ3B1L2RybS9icmlkZ2UvZHdfaGRtaS1hdWRp by5oCj4gQEAgLTAsMCArMSwxMyBAQAo+ICsjaWZuZGVmIERXX0hETUlfQVVESU9fSAo+ICsjZGVm aW5lIERXX0hETUlfQVVESU9fSAo+ICsKPiArc3RydWN0IGR3X2hkbWk7Cj4gKwo+ICtzdHJ1Y3Qg ZHdfaGRtaV9hdWRpb19kYXRhIHsKPiArCXBoeXNfYWRkcl90IHBoeXM7Cj4gKwl2b2lkIF9faW9t ZW0gKmJhc2U7Cj4gKwlpbnQgaXJxOwo+ICsJc3RydWN0IGR3X2hkbWkgKmhkbWk7Cj4gK307Cj4g Kwo+ICsjZW5kaWYKPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1p LmMgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3X2hkbWkuYwo+IGluZGV4IGZiYTI1NjA3ZWY4 OC4uYjY1NDY0Nzg5ZmJkIDEwMDY0NAo+IC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9icmlkZ2UvZHdf aGRtaS5jCj4gKysrIGIvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1pLmMKPiBAQCAtMjgs NiArMjgsNyBAQAo+ICAjaW5jbHVkZSA8ZHJtL2JyaWRnZS9kd19oZG1pLmg+Cj4gIAo+ICAjaW5j bHVkZSAiZHdfaGRtaS5oIgo+ICsjaW5jbHVkZSAiZHdfaGRtaS1hdWRpby5oIgo+ICAKPiAgI2Rl ZmluZSBIRE1JX0VESURfTEVOCQk1MTIKPiAgCj4gQEAgLTEwNCw2ICsxMDUsNyBAQCBzdHJ1Y3Qg ZHdfaGRtaSB7Cj4gIAlzdHJ1Y3QgZHJtX2VuY29kZXIgKmVuY29kZXI7Cj4gIAlzdHJ1Y3QgZHJt X2JyaWRnZSAqYnJpZGdlOwo+ICAKPiArCXN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKmF1ZGlvOwo+ ICAJZW51bSBkd19oZG1pX2RldnR5cGUgZGV2X3R5cGU7Cj4gIAlzdHJ1Y3QgZGV2aWNlICpkZXY7 Cj4gIAlzdHJ1Y3QgY2xrICppc2ZyX2NsazsKPiBAQCAtMTczMiw3ICsxNzM0LDkgQEAgaW50IGR3 X2hkbWlfYmluZChzdHJ1Y3QgZGV2aWNlICpkZXYsIHN0cnVjdCBkZXZpY2UgKm1hc3RlciwKPiAg ewo+ICAJc3RydWN0IGRybV9kZXZpY2UgKmRybSA9IGRhdGE7Cj4gIAlzdHJ1Y3QgZGV2aWNlX25v ZGUgKm5wID0gZGV2LT5vZl9ub2RlOwo+ICsJc3RydWN0IHBsYXRmb3JtX2RldmljZV9pbmZvIHBk ZXZpbmZvOwo+ICAJc3RydWN0IGRldmljZV9ub2RlICpkZGNfbm9kZTsKPiArCXN0cnVjdCBkd19o ZG1pX2F1ZGlvX2RhdGEgYXVkaW87Cj4gIAlzdHJ1Y3QgZHdfaGRtaSAqaGRtaTsKPiAgCWludCBy ZXQ7Cj4gIAl1MzIgdmFsID0gMTsKPiBAQCAtMTg2MCw2ICsxODY0LDIzIEBAIGludCBkd19oZG1p X2JpbmQoc3RydWN0IGRldmljZSAqZGV2LCBzdHJ1Y3QgZGV2aWNlICptYXN0ZXIsCj4gIAloZG1p X3dyaXRlYihoZG1pLCB+KEhETUlfSUhfUEhZX1NUQVQwX0hQRCB8IEhETUlfSUhfUEhZX1NUQVQw X1JYX1NFTlNFKSwKPiAgCQkgICAgSERNSV9JSF9NVVRFX1BIWV9TVEFUMCk7Cj4gIAo+ICsJbWVt c2V0KCZwZGV2aW5mbywgMCwgc2l6ZW9mKHBkZXZpbmZvKSk7Cj4gKwlwZGV2aW5mby5wYXJlbnQg PSBkZXY7Cj4gKwlwZGV2aW5mby5pZCA9IFBMQVRGT1JNX0RFVklEX0FVVE87Cj4gKwo+ICsJaWYg KGhkbWlfcmVhZGIoaGRtaSwgSERNSV9DT05GSUcxX0lEKSAmIEhETUlfQ09ORklHMV9BSEIpIHsK PiArCQlhdWRpby5waHlzID0gaW9yZXMtPnN0YXJ0Owo+ICsJCWF1ZGlvLmJhc2UgPSBoZG1pLT5y ZWdzOwo+ICsJCWF1ZGlvLmlycSA9IGlycTsKPiArCQlhdWRpby5oZG1pID0gaGRtaTsKPiArCj4g KwkJcGRldmluZm8ubmFtZSA9ICJkdy1oZG1pLWFoYi1hdWRpbyI7Cj4gKwkJcGRldmluZm8uZGF0 YSA9ICZhdWRpbzsKPiArCQlwZGV2aW5mby5zaXplX2RhdGEgPSBzaXplb2YoYXVkaW8pOwo+ICsJ CXBkZXZpbmZvLmRtYV9tYXNrID0gRE1BX0JJVF9NQVNLKDMyKTsKPiArCQloZG1pLT5hdWRpbyA9 IHBsYXRmb3JtX2RldmljZV9yZWdpc3Rlcl9mdWxsKCZwZGV2aW5mbyk7Cj4gKwl9Cj4gKwo+ICAJ ZGV2X3NldF9kcnZkYXRhKGRldiwgaGRtaSk7Cj4gIAo+ICAJcmV0dXJuIDA7Cj4gQEAgLTE4Nzcs NiArMTg5OCw5IEBAIHZvaWQgZHdfaGRtaV91bmJpbmQoc3RydWN0IGRldmljZSAqZGV2LCBzdHJ1 Y3QgZGV2aWNlICptYXN0ZXIsIHZvaWQgKmRhdGEpCj4gIHsKPiAgCXN0cnVjdCBkd19oZG1pICpo ZG1pID0gZGV2X2dldF9kcnZkYXRhKGRldik7Cj4gIAo+ICsJaWYgKGhkbWktPmF1ZGlvICYmICFJ U19FUlIoaGRtaS0+YXVkaW8pKQo+ICsJCXBsYXRmb3JtX2RldmljZV91bnJlZ2lzdGVyKGhkbWkt PmF1ZGlvKTsKPiArCj4gIAkvKiBEaXNhYmxlIGFsbCBpbnRlcnJ1cHRzICovCj4gIAloZG1pX3dy aXRlYihoZG1pLCB+MCwgSERNSV9JSF9NVVRFX1BIWV9TVEFUMCk7Cj4gIAo+IGRpZmYgLS1naXQg YS9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL2R3X2hkbWkuaCBiL2RyaXZlcnMvZ3B1L2RybS9icmlk Z2UvZHdfaGRtaS5oCj4gaW5kZXggMTc1ZGJjODlhODI0Li43OGU1NGU4MTMyMTIgMTAwNjQ0Cj4g LS0tIGEvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9kd19oZG1pLmgKPiArKysgYi9kcml2ZXJzL2dw dS9kcm0vYnJpZGdlL2R3X2hkbWkuaAo+IEBAIC01NDUsNiArNTQ1LDkgQEAKPiAgI2RlZmluZSBI RE1JX0kyQ01fRlNfU0NMX0xDTlRfMF9BRERSICAgICAgICAgICAgMHg3RTEyCj4gIAo+ICBlbnVt IHsKPiArLyogQ09ORklHMV9JRCBmaWVsZCB2YWx1ZXMgKi8KPiArCUhETUlfQ09ORklHMV9BSEIg PSAweDAxLAo+ICsKPiAgLyogSUhfRkNfSU5UMiBmaWVsZCB2YWx1ZXMgKi8KPiAgCUhETUlfSUhf RkNfSU5UMl9PVkVSRkxPV19NQVNLID0gMHgwMywKPiAgCUhETUlfSUhfRkNfSU5UMl9MT1dfUFJJ T1JJVFlfT1ZFUkZMT1cgPSAweDAyLAo+IC0tIAo+IDIuMS4wCj4gCj4gX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KPiBBbHNhLWRldmVsIG1haWxpbmcgbGlz dAo+IEFsc2EtZGV2ZWxAYWxzYS1wcm9qZWN0Lm9yZwo+IGh0dHA6Ly9tYWlsbWFuLmFsc2EtcHJv amVjdC5vcmcvbWFpbG1hbi9saXN0aW5mby9hbHNhLWRldmVsCj4gCl9fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCmRyaS1kZXZlbCBtYWlsaW5nIGxpc3QKZHJp LWRldmVsQGxpc3RzLmZyZWVkZXNrdG9wLm9yZwpodHRwOi8vbGlzdHMuZnJlZWRlc2t0b3Aub3Jn L21haWxtYW4vbGlzdGluZm8vZHJpLWRldmVsCg== From mboxrd@z Thu Jan 1 00:00:00 1970 From: tiwai@suse.de (Takashi Iwai) Date: Fri, 14 Aug 2015 16:34:07 +0200 Subject: [alsa-devel] [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver In-Reply-To: References: Message-ID: To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org On Fri, 14 Aug 2015 16:04:25 +0200, Russell King wrote: > > Add ALSA based HDMI AHB audio driver for dw_hdmi. The only buffer > format supported by the hardware is its own special IEC958 based format, > which is not compatible with any ALSA format. To avoid doing too much > data manipulation within the driver, we support only ALSAs IEC958 LE and > 24-bit PCM formats for 2 to 6 channels, which we convert to its hardware > format. > > A more desirable solution would be to have this conversion in userspace, > but ALSA does not appear to allow such transformations outside of > libasound itself. > > Signed-off-by: Russell King > --- > v2: updated with Takashi Iwai's comments... and with a fixed Cc: line. Reviewed-by: Takashi Iwai thanks, Takashi > drivers/gpu/drm/bridge/Kconfig | 10 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c | 579 +++++++++++++++++++++++++++++ > drivers/gpu/drm/bridge/dw_hdmi-audio.h | 13 + > drivers/gpu/drm/bridge/dw_hdmi.c | 24 ++ > drivers/gpu/drm/bridge/dw_hdmi.h | 3 + > 6 files changed, 630 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > create mode 100644 drivers/gpu/drm/bridge/dw_hdmi-audio.h > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index acef3223772c..56ed35fe0734 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -3,6 +3,16 @@ config DRM_DW_HDMI > depends on DRM > select DRM_KMS_HELPER > > +config DRM_DW_HDMI_AHB_AUDIO > + tristate "Synopsis Designware AHB Audio interface" > + depends on DRM_DW_HDMI && SND > + select SND_PCM > + select SND_PCM_IEC958 > + help > + Support the AHB Audio interface which is part of the Synopsis > + Designware HDMI block. This is used in conjunction with > + the i.MX6 HDMI driver. > + > config DRM_PTN3460 > tristate "PTN3460 DP/LVDS bridge" > depends on DRM > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index 8dfebd984370..eb80dbbb8365 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm > obj-$(CONFIG_DRM_PS8622) += ps8622.o > obj-$(CONFIG_DRM_PTN3460) += ptn3460.o > obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o > +obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o > diff --git a/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > new file mode 100644 > index 000000000000..bf379310008a > --- /dev/null > +++ b/drivers/gpu/drm/bridge/dw_hdmi-ahb-audio.c > @@ -0,0 +1,579 @@ > +/* > + * DesignWare HDMI audio driver > + * > + * 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. > + * > + * Written and tested against the Designware HDMI Tx found in iMX6. > + */ > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include "dw_hdmi-audio.h" > + > +#define DRIVER_NAME "dw-hdmi-ahb-audio" > + > +/* Provide some bits rather than bit offsets */ > +enum { > + HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7), > + HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3), > + HDMI_AHB_DMA_START_START = BIT(0), > + HDMI_AHB_DMA_STOP_STOP = BIT(0), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL = > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL | > + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY, > + HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5), > + HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4), > + HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3), > + HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2), > + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), > + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), > + HDMI_IH_AHBDMAAUD_STAT0_ALL = > + HDMI_IH_AHBDMAAUD_STAT0_ERROR | > + HDMI_IH_AHBDMAAUD_STAT0_LOST | > + HDMI_IH_AHBDMAAUD_STAT0_RETRY | > + HDMI_IH_AHBDMAAUD_STAT0_DONE | > + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL | > + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY, > + HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1, > + HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1, > + HDMI_AHB_DMA_CONF0_INCR4 = 0, > + HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0), > + HDMI_AHB_DMA_MASK_DONE = BIT(7), > + HDMI_REVISION_ID = 0x0001, > + HDMI_IH_AHBDMAAUD_STAT0 = 0x0109, > + HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189, > + HDMI_AHB_DMA_CONF0 = 0x3600, > + HDMI_AHB_DMA_START = 0x3601, > + HDMI_AHB_DMA_STOP = 0x3602, > + HDMI_AHB_DMA_THRSLD = 0x3603, > + HDMI_AHB_DMA_STRADDR0 = 0x3604, > + HDMI_AHB_DMA_STPADDR0 = 0x3608, > + HDMI_AHB_DMA_MASK = 0x3614, > + HDMI_AHB_DMA_POL = 0x3615, > + HDMI_AHB_DMA_CONF1 = 0x3616, > + HDMI_AHB_DMA_BUFFPOL = 0x361a, > +}; > + > +struct snd_dw_hdmi { > + struct snd_card *card; > + struct snd_pcm *pcm; > + spinlock_t lock; > + struct dw_hdmi_audio_data data; > + struct snd_pcm_substream *substream; > + void (*reformat)(struct snd_dw_hdmi *, size_t, size_t); > + void *buf_src; > + void *buf_dst; > + dma_addr_t buf_addr; > + unsigned buf_offset; > + unsigned buf_period; > + unsigned buf_size; > + unsigned channels; > + u8 revision; > + u8 iec_offset; > + u8 cs[192][8]; > +}; > + > +static void dw_hdmi_writel(u32 val, void __iomem *ptr) > +{ > + writeb_relaxed(val, ptr); > + writeb_relaxed(val >> 8, ptr + 1); > + writeb_relaxed(val >> 16, ptr + 2); > + writeb_relaxed(val >> 24, ptr + 3); > +} > + > +/* > + * Convert to hardware format: The userspace buffer contains IEC958 samples, > + * with the PCUV bits in bits 31..28 and audio samples in bits 27..4. We > + * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio > + * samples in 23..0. > + * > + * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd > + * > + * Ideally, we could do with having the data properly formatted in userspace. > + */ > +static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw, > + size_t offset, size_t bytes) > +{ > + u32 *src = dw->buf_src + offset; > + u32 *dst = dw->buf_dst + offset; > + u32 *end = dw->buf_src + offset + bytes; > + > + do { > + u32 b, sample = *src++; > + > + b = (sample & 8) << (28 - 3); > + > + sample >>= 4; > + > + *dst++ = sample | b; > + } while (src < end); > +} > + > +static u32 parity(u32 sample) > +{ > + sample ^= sample >> 16; > + sample ^= sample >> 8; > + sample ^= sample >> 4; > + sample ^= sample >> 2; > + sample ^= sample >> 1; > + return (sample & 1) << 27; > +} > + > +static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw, > + size_t offset, size_t bytes) > +{ > + u32 *src = dw->buf_src + offset; > + u32 *dst = dw->buf_dst + offset; > + u32 *end = dw->buf_src + offset + bytes; > + > + do { > + unsigned i; > + u8 *cs; > + > + cs = dw->cs[dw->iec_offset++]; > + if (dw->iec_offset >= 192) > + dw->iec_offset = 0; > + > + i = dw->channels; > + do { > + u32 sample = *src++; > + > + sample &= ~0xff000000; > + sample |= *cs++ << 24; > + sample |= parity(sample & ~0xf8000000); > + > + *dst++ = sample; > + } while (--i); > + } while (src < end); > +} > + > +static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw, > + struct snd_pcm_runtime *runtime) > +{ > + u8 cs[4]; > + unsigned ch, i, j; > + > + snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs)); > + > + memset(dw->cs, 0, sizeof(dw->cs)); > + > + for (ch = 0; ch < 8; ch++) { > + cs[2] &= ~IEC958_AES2_CON_CHANNEL; > + cs[2] |= (ch + 1) << 4; > + > + for (i = 0; i < ARRAY_SIZE(cs); i++) { > + unsigned c = cs[i]; > + > + for (j = 0; j < 8; j++, c >>= 1) > + dw->cs[i * 8 + j][ch] = (c & 1) << 2; > + } > + } > + dw->cs[0][0] |= BIT(4); > +} > + > +static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw) > +{ > + void __iomem *base = dw->data.base; > + unsigned offset = dw->buf_offset; > + unsigned period = dw->buf_period; > + u32 start, stop; > + > + dw->reformat(dw, offset, period); > + > + /* Clear all irqs before enabling irqs and starting DMA */ > + writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL, > + base + HDMI_IH_AHBDMAAUD_STAT0); > + > + start = dw->buf_addr + offset; > + stop = start + period - 1; > + > + /* Setup the hardware start/stop addresses */ > + dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0); > + dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0); > + > + writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK); > + writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START); > + > + offset += period; > + if (offset >= dw->buf_size) > + offset = 0; > + dw->buf_offset = offset; > +} > + > +static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw) > +{ > + /* Disable interrupts before disabling DMA */ > + writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK); > + writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP); > +} > + > +static irqreturn_t snd_dw_hdmi_irq(int irq, void *data) > +{ > + struct snd_dw_hdmi *dw = data; > + struct snd_pcm_substream *substream; > + unsigned stat; > + > + stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); > + if (!stat) > + return IRQ_NONE; > + > + writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); > + > + substream = dw->substream; > + if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) { > + snd_pcm_period_elapsed(substream); > + > + spin_lock(&dw->lock); > + if (dw->substream) > + dw_hdmi_start_dma(dw); > + spin_unlock(&dw->lock); > + } > + > + return IRQ_HANDLED; > +} > + > +static struct snd_pcm_hardware dw_hdmi_hw = { > + .info = SNDRV_PCM_INFO_INTERLEAVED | > + SNDRV_PCM_INFO_BLOCK_TRANSFER | > + SNDRV_PCM_INFO_MMAP | > + SNDRV_PCM_INFO_MMAP_VALID, > + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | > + SNDRV_PCM_FMTBIT_S24_LE, > + .rates = SNDRV_PCM_RATE_32000 | > + SNDRV_PCM_RATE_44100 | > + SNDRV_PCM_RATE_48000 | > + SNDRV_PCM_RATE_88200 | > + SNDRV_PCM_RATE_96000 | > + SNDRV_PCM_RATE_176400 | > + SNDRV_PCM_RATE_192000, > + .channels_min = 2, > + .channels_max = 8, > + .buffer_bytes_max = 64 * 1024, > + .period_bytes_min = 256, > + .period_bytes_max = 8192, /* ERR004323: must limit to 8k */ > + .periods_min = 2, > + .periods_max = 16, > + .fifo_size = 0, > +}; > + > +static int dw_hdmi_open(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + void __iomem *base = dw->data.base; > + int ret; > + > + runtime->hw = dw_hdmi_hw; > + > + ret = snd_pcm_limit_hw_rates(runtime); > + if (ret < 0) > + return ret; > + > + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); > + if (ret < 0) > + return ret; > + > + /* Clear FIFO */ > + writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST, > + base + HDMI_AHB_DMA_CONF0); > + > + /* Configure interrupt polarities */ > + writeb_relaxed(~0, base + HDMI_AHB_DMA_POL); > + writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL); > + > + /* Keep interrupts masked, and clear any pending */ > + writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK); > + writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0); > + > + ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED, > + "dw-hdmi-audio", dw); > + if (ret) > + return ret; > + > + /* Un-mute done interrupt */ > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL & > + ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE, > + base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + > + return 0; > +} > + > +static int dw_hdmi_close(struct snd_pcm_substream *substream) > +{ > + struct snd_dw_hdmi *dw = substream->private_data; > + > + /* Mute all interrupts */ > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, > + dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + > + free_irq(dw->data.irq, dw); > + > + return 0; > +} > + > +static int dw_hdmi_hw_free(struct snd_pcm_substream *substream) > +{ > + return snd_pcm_lib_free_vmalloc_buffer(substream); > +} > + > +static int dw_hdmi_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params) > +{ > + return snd_pcm_lib_alloc_vmalloc_buffer(substream, > + params_buffer_bytes(params)); > +} > + > +static int dw_hdmi_prepare(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + u8 threshold, conf0, conf1; > + > + /* Setup as per 3.0.5 FSL 4.1.0 BSP */ > + switch (dw->revision) { > + case 0x0a: > + conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | > + HDMI_AHB_DMA_CONF0_INCR4; > + if (runtime->channels == 2) > + threshold = 126; > + else > + threshold = 124; > + break; > + case 0x1a: > + conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | > + HDMI_AHB_DMA_CONF0_INCR8; > + threshold = 128; > + break; > + default: > + /* NOTREACHED */ > + return -EINVAL; > + } > + > + dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate); > + > + /* Minimum number of bytes in the fifo. */ > + runtime->hw.fifo_size = threshold * 32; > + > + conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK; > + conf1 = (1 << runtime->channels) - 1; > + > + writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD); > + writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0); > + writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1); > + > + switch (runtime->format) { > + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: > + dw->reformat = dw_hdmi_reformat_iec958; > + break; > + case SNDRV_PCM_FORMAT_S24_LE: > + dw_hdmi_create_cs(dw, runtime); > + dw->reformat = dw_hdmi_reformat_s24; > + break; > + } > + dw->iec_offset = 0; > + dw->channels = runtime->channels; > + dw->buf_src = runtime->dma_area; > + dw->buf_dst = substream->dma_buffer.area; > + dw->buf_addr = substream->dma_buffer.addr; > + dw->buf_period = snd_pcm_lib_period_bytes(substream); > + dw->buf_size = snd_pcm_lib_buffer_bytes(substream); > + > + return 0; > +} > + > +static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd) > +{ > + struct snd_dw_hdmi *dw = substream->private_data; > + unsigned long flags; > + int ret = 0; > + > + switch (cmd) { > + case SNDRV_PCM_TRIGGER_START: > + spin_lock_irqsave(&dw->lock, flags); > + dw->buf_offset = 0; > + dw->substream = substream; > + dw_hdmi_start_dma(dw); > + dw_hdmi_audio_enable(dw->data.hdmi); > + spin_unlock_irqrestore(&dw->lock, flags); > + substream->runtime->delay = substream->runtime->period_size; > + break; > + > + case SNDRV_PCM_TRIGGER_STOP: > + spin_lock_irqsave(&dw->lock, flags); > + dw->substream = NULL; > + dw_hdmi_stop_dma(dw); > + dw_hdmi_audio_disable(dw->data.hdmi); > + spin_unlock_irqrestore(&dw->lock, flags); > + break; > + > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_dw_hdmi *dw = substream->private_data; > + > + /* > + * We are unable to report the exact hardware position as > + * reading the 32-bit DMA position using 8-bit reads is racy. > + */ > + return bytes_to_frames(runtime, dw->buf_offset); > +} > + > +static struct snd_pcm_ops snd_dw_hdmi_ops = { > + .open = dw_hdmi_open, > + .close = dw_hdmi_close, > + .ioctl = snd_pcm_lib_ioctl, > + .hw_params = dw_hdmi_hw_params, > + .hw_free = dw_hdmi_hw_free, > + .prepare = dw_hdmi_prepare, > + .trigger = dw_hdmi_trigger, > + .pointer = dw_hdmi_pointer, > + .page = snd_pcm_lib_get_vmalloc_page, > +}; > + > +static int snd_dw_hdmi_probe(struct platform_device *pdev) > +{ > + const struct dw_hdmi_audio_data *data = pdev->dev.platform_data; > + struct device *dev = pdev->dev.parent; > + struct snd_dw_hdmi *dw; > + struct snd_card *card; > + struct snd_pcm *pcm; > + unsigned revision; > + int ret; > + > + writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, > + data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); > + revision = readb_relaxed(data->base + HDMI_REVISION_ID); > + if (revision != 0x0a && revision != 0x1a) { > + dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n", > + revision); > + return -ENXIO; > + } > + > + ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, > + THIS_MODULE, sizeof(struct snd_dw_hdmi), &card); > + if (ret < 0) > + return ret; > + > + strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); > + strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname)); > + snprintf(card->longname, sizeof(card->longname), > + "%s rev 0x%02x, irq %d", card->shortname, revision, > + data->irq); > + > + dw = card->private_data; > + dw->card = card; > + dw->data = *data; > + dw->revision = revision; > + > + spin_lock_init(&dw->lock); > + > + ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm); > + if (ret < 0) > + goto err; > + > + dw->pcm = pcm; > + pcm->private_data = dw; > + strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops); > + > + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, > + dev, 64 * 1024, 64 * 1024); > + > + ret = snd_card_register(card); > + if (ret < 0) > + goto err; > + > + platform_set_drvdata(pdev, dw); > + > + return 0; > + > +err: > + snd_card_free(card); > + return ret; > +} > + > +static int snd_dw_hdmi_remove(struct platform_device *pdev) > +{ > + struct snd_dw_hdmi *dw = platform_get_drvdata(pdev); > + > + snd_card_free(dw->card); > + > + return 0; > +} > + > +#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN) > +/* > + * This code is fine, but requires implementation in the dw_hdmi_trigger() > + * method which is currently missing as I have no way to test this. > + */ > +static int snd_dw_hdmi_suspend(struct device *dev) > +{ > + struct snd_dw_hdmi *dw = dev_get_drvdata(dev); > + > + snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold); > + snd_pcm_suspend_all(dw->pcm); > + > + return 0; > +} > + > +static int snd_dw_hdmi_resume(struct device *dev) > +{ > + struct snd_dw_hdmi *dw = dev_get_drvdata(dev); > + > + snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend, > + snd_dw_hdmi_resume); > +#define PM_OPS &snd_dw_hdmi_pm > +#else > +#define PM_OPS NULL > +#endif > + > +static struct platform_driver snd_dw_hdmi_driver = { > + .probe = snd_dw_hdmi_probe, > + .remove = snd_dw_hdmi_remove, > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + .pm = PM_OPS, > + }, > +}; > + > +module_platform_driver(snd_dw_hdmi_driver); > + > +MODULE_AUTHOR("Russell King "); > +MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:" DRIVER_NAME); > diff --git a/drivers/gpu/drm/bridge/dw_hdmi-audio.h b/drivers/gpu/drm/bridge/dw_hdmi-audio.h > new file mode 100644 > index 000000000000..1e840118d90a > --- /dev/null > +++ b/drivers/gpu/drm/bridge/dw_hdmi-audio.h > @@ -0,0 +1,13 @@ > +#ifndef DW_HDMI_AUDIO_H > +#define DW_HDMI_AUDIO_H > + > +struct dw_hdmi; > + > +struct dw_hdmi_audio_data { > + phys_addr_t phys; > + void __iomem *base; > + int irq; > + struct dw_hdmi *hdmi; > +}; > + > +#endif > diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c > index fba25607ef88..b65464789fbd 100644 > --- a/drivers/gpu/drm/bridge/dw_hdmi.c > +++ b/drivers/gpu/drm/bridge/dw_hdmi.c > @@ -28,6 +28,7 @@ > #include > > #include "dw_hdmi.h" > +#include "dw_hdmi-audio.h" > > #define HDMI_EDID_LEN 512 > > @@ -104,6 +105,7 @@ struct dw_hdmi { > struct drm_encoder *encoder; > struct drm_bridge *bridge; > > + struct platform_device *audio; > enum dw_hdmi_devtype dev_type; > struct device *dev; > struct clk *isfr_clk; > @@ -1732,7 +1734,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, > { > struct drm_device *drm = data; > struct device_node *np = dev->of_node; > + struct platform_device_info pdevinfo; > struct device_node *ddc_node; > + struct dw_hdmi_audio_data audio; > struct dw_hdmi *hdmi; > int ret; > u32 val = 1; > @@ -1860,6 +1864,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master, > hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), > HDMI_IH_MUTE_PHY_STAT0); > > + memset(&pdevinfo, 0, sizeof(pdevinfo)); > + pdevinfo.parent = dev; > + pdevinfo.id = PLATFORM_DEVID_AUTO; > + > + if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_CONFIG1_AHB) { > + audio.phys = iores->start; > + audio.base = hdmi->regs; > + audio.irq = irq; > + audio.hdmi = hdmi; > + > + pdevinfo.name = "dw-hdmi-ahb-audio"; > + pdevinfo.data = &audio; > + pdevinfo.size_data = sizeof(audio); > + pdevinfo.dma_mask = DMA_BIT_MASK(32); > + hdmi->audio = platform_device_register_full(&pdevinfo); > + } > + > dev_set_drvdata(dev, hdmi); > > return 0; > @@ -1877,6 +1898,9 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) > { > struct dw_hdmi *hdmi = dev_get_drvdata(dev); > > + if (hdmi->audio && !IS_ERR(hdmi->audio)) > + platform_device_unregister(hdmi->audio); > + > /* Disable all interrupts */ > hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); > > diff --git a/drivers/gpu/drm/bridge/dw_hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h > index 175dbc89a824..78e54e813212 100644 > --- a/drivers/gpu/drm/bridge/dw_hdmi.h > +++ b/drivers/gpu/drm/bridge/dw_hdmi.h > @@ -545,6 +545,9 @@ > #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 > > enum { > +/* CONFIG1_ID field values */ > + HDMI_CONFIG1_AHB = 0x01, > + > /* IH_FC_INT2 field values */ > HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, > HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, > -- > 2.1.0 > > _______________________________________________ > Alsa-devel mailing list > Alsa-devel at alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel >