* [PATCH 1/2] pinctrl: stm32: remove dependency with interrupt controller
From: Linus Walleij @ 2016-10-23 23:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476970012-7838-2-git-send-email-alexandre.torgue@st.com>
On Thu, Oct 20, 2016 at 3:26 PM, Alexandre TORGUE
<alexandre.torgue@st.com> wrote:
> This patch allows to probe stm32 pinctrl driver even if no interrupt
> controller is defined to manage gpio irqs.
>
> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
Patch applied for fixes.
Yours,
Linus Walleij
^ permalink raw reply
* [GIT PULL 4/4] Broadcom defconfig-arm64 changes for 4.9 (part 2)
From: Olof Johansson @ 2016-10-23 23:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <62d5c9b0-9073-c8e3-6a9f-390f381d4d9d@gmail.com>
On Sun, Oct 23, 2016 at 2:55 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 09/30/2016 12:23 PM, Florian Fainelli wrote:
>> The following changes since commit 29b4817d4018df78086157ea3a55c1d9424a7cfc:
>>
>> Linux 4.8-rc1 (2016-08-07 18:18:00 -0700)
>>
>> are available in the git repository at:
>>
>> http://github.com/Broadcom/stblinux.git tags/arm-soc/for-4.9/defconfig-arm64
>>
>> for you to fetch changes up to 51e3fb1d3f514cd336faf185df73b25fca194773:
>>
>> Merge tag 'bcm2835-defconfig-64-next-2016-09-22' into defconfig-arm64/next (2016-09-30 12:02:29 -0700)
>>
>> ----------------------------------------------------------------
>> This pull request contains Broadcom ARM64-based SoCs defconfig changes for 4.9,
>> please pull the following changes:
>>
>> - Eric updates the ARMv8 defconfig to contain everything that is needed to run
>> a 64-bit kernel on the Raspberry Pi 3
>>
>> ----------------------------------------------------------------
>> Eric Anholt (1):
>> arm64: Add BCM2835 (Raspberry Pi 3) support to the defconfig
>>
>> Florian Fainelli (1):
>> Merge tag 'bcm2835-defconfig-64-next-2016-09-22' into defconfig-arm64/next
>>
>> arch/arm64/configs/defconfig | 16 ++++++++++++++++
>> 1 file changed, 16 insertions(+)
>
> Arnd, Kevin, Olof,
>
> Following Olof's response here:
>
> https://www.spinics.net/lists/arm-kernel/msg534687.html
>
> do you think we could merge these for 4.9-rcX? Let me know if I should
> send a fresh pull request for that.
Main reason to respin would be if you need to rebase due to other
changes that have gone in, or if you expect to have other material
that should be based on a 4.9-rc base.
-Olof
^ permalink raw reply
* [PATCH] pinctrl: st: don't specify default interrupt trigger
From: Linus Walleij @ 2016-10-23 23:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476774988-13484-1-git-send-email-patrice.chotard@st.com>
On Tue, Oct 18, 2016 at 9:16 AM, <patrice.chotard@st.com> wrote:
> From: Patrice Chotard <patrice.chotard@st.com>
>
> Thanks to 332e99d5ae4 which now alerts of default
> trigger usage when configuring interrupts.
>
> Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Patch applied with Peter's ACK.
Pls also look into the following: __gpio_irq_handler seems to be
doing some stuff per-IRQ that only pertains to edge-triggered IRQs.
Normally that should be handled by:
- Setting default handler to handle_bad_irq()
- Setting handler to handle_edge_irq() or handle_level_irq() in .set_type()
- Implement .irq_ack() on the irqchip and handle the edge-specific ACKing
there.
See for example drivers/gpio/gpio-pl061.c.
I am not *sure* this applies to pinctrl-st.c but please check if it provides
more elegant code. Notmally the .irq_ack() is for edge detection hardware
that allows ACKing the ege IRQ in a separate register and after that we
can raise another edge IRQ while the current IRQ is being handled.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH] Revert "gpio/mvebu: convert to use irq_domain_add_simple()"
From: Linus Walleij @ 2016-10-23 23:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019210341.GA3746@obsidianresearch.com>
On Wed, Oct 19, 2016 at 11:03 PM, Jason Gunthorpe
<jgunthorpe@obsidianresearch.com> wrote:
> From 7566f05ac445b652ba7607cc1899fed10fea1c76 Mon Sep 17 00:00:00 2001
> From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
> Date: Wed, 19 Oct 2016 14:57:45 -0600
> Subject: [PATCH] gpio/mvebu: Use irq_domain_add_linear
>
> This fixes the irq allocation in this driver to not print:
> irq: Cannot allocate irq_descs @ IRQ34, assuming pre-allocated
> irq: Cannot allocate irq_descs @ IRQ66, assuming pre-allocated
>
> Which happens because the driver already called irq_alloc_descs()
> and so the change to use irq_domain_add_simple resulted in calling
> irq_alloc_descs() twice.
>
> Modernize the irq allocation in this driver to use the
> irq_domain_add_linear flow directly and eliminate the use of
> irq_domain_add_simple/legacy
>
> Fixes: ce931f571b6d ("gpio/mvebu: convert to use irq_domain_add_simple()")
> Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
So can I just apply this?
Gregory?
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 5/8] pinctrl: aspeed: Enable capture of off-SCU pinmux state
From: Linus Walleij @ 2016-10-23 22:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACPK8Xc7y3GtcJCVYYs-JKTqBZvqVeZaz5MUk=UX151SX1xEFw@mail.gmail.com>
On Thu, Sep 29, 2016 at 8:45 AM, Joel Stanley <joel@jms.id.au> wrote:
> On Wed, Sep 28, 2016 at 12:20 AM, Andrew Jeffery <andrew@aj.id.au> wrote:
>> On the AST2400 and AST2500 a number of pins depend on state in one of
>> the SIO, LPC or GFX IP blocks, so add support to at least capture what
>> that state is. The pinctrl engine for the Aspeed SoCs doesn't try to
>> inspect or modify the state of the off-SCU IP blocks. Instead, it logs
>> the state requirement with the expectation that the platform
>> designer/maintainer arranges for the appropriate configuration to be
>> applied through the associated drivers.
(...)
>
> This is unfortunate.
>
> This patch kicks the can down the road, but doesn't solve the problem
> for a user who wants to configure some functionality that depends on
> the non-SCU bits. Because of this I'm not sure if we want to put it in
> the tree.
>
> However, I'm not sure what a proper solution would look like. Perhaps
> Linus can point out another SoC that has a similar problem?
If I could only understand it.
Don't you actually want to go and read a few registers on another
IO range?
What we generally do when pin control is spread out in a system is try
to consolidate it, meaning expose the necessary registers on the remote
end using syscon (drivers/mfd/syscon) so that we can easily get a handle
on these registers withe regmap MMIO.
Since regmap handles atomic access to the registers, that way we
protect from disasters and keep the state in the hardware.
I don't know if this is helpful. For a normal peripheral you may not want
to put a regmap over all its registers but you prefer to ioremap it, and then
we get the spaghetti of accessor functions to peek and poke into another
peripherals I/O space, and that is undesireable.
Maybe this is completely not understanding what you want to do, so
sorry, please elaborate. I am afraid the two of you are the only people on
the planet who actually understand what is going on here.
Also the hardware engineer who wrote the Aspeed pin controller, I would
like to read this persons design specification, I am pretty sure it is very
interesting.
>> - * @reg: The register offset from base in bytes
>> + * @reg: Split into three fields:
>> + * 31:24: IP selector
>> + * 23:12: Reserved
>> + * 11:0: Register offset
That seems like unnecessary shoehorning. Is it reflecting the register layout
of the hardware or are you trying to save bits? For the latter, let it
go and instead
have one member for offset and one member for selector.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 6/8] pinctrl: aspeed-g4: Capture SuperIO pinmux dependency
From: Linus Walleij @ 2016-10-23 22:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477010011.8917.20.camel@aj.id.au>
On Fri, Oct 21, 2016 at 2:33 AM, Andrew Jeffery <andrew@aj.id.au> wrote:
>> Patch applied for v4.10.
>> (Tell me if I'm applying patches in wrong order or something, and
>> I hope this doesn't clash with the fixes.)
>
> Both this patch and 8/8 functionally depend on 5/8. I fetched the
> pinctrl tree to poke around but this patch didn't appear in any of the
> updated branches, so I'm not sure whether we have the right ordering.
> Without it we should hit build failures from missing macro definitions.
>
> Have you had a chance to look over patch 5/8? Joel wasn't keen on its
> current form, so I would appreciate your input.
Oops backed this patch out.
Will look at 5/8.
Appreciate if you repost the remaining patches in the series based on
v4.9-rc2 once it's out, and I'll rebase the pinctrl tree onto that.
Yours,
Linus Walleij
^ permalink raw reply
* [GIT PULL 4/4] Broadcom defconfig-arm64 changes for 4.9 (part 2)
From: Florian Fainelli @ 2016-10-23 21:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1475263395-27653-4-git-send-email-f.fainelli@gmail.com>
On 09/30/2016 12:23 PM, Florian Fainelli wrote:
> The following changes since commit 29b4817d4018df78086157ea3a55c1d9424a7cfc:
>
> Linux 4.8-rc1 (2016-08-07 18:18:00 -0700)
>
> are available in the git repository at:
>
> http://github.com/Broadcom/stblinux.git tags/arm-soc/for-4.9/defconfig-arm64
>
> for you to fetch changes up to 51e3fb1d3f514cd336faf185df73b25fca194773:
>
> Merge tag 'bcm2835-defconfig-64-next-2016-09-22' into defconfig-arm64/next (2016-09-30 12:02:29 -0700)
>
> ----------------------------------------------------------------
> This pull request contains Broadcom ARM64-based SoCs defconfig changes for 4.9,
> please pull the following changes:
>
> - Eric updates the ARMv8 defconfig to contain everything that is needed to run
> a 64-bit kernel on the Raspberry Pi 3
>
> ----------------------------------------------------------------
> Eric Anholt (1):
> arm64: Add BCM2835 (Raspberry Pi 3) support to the defconfig
>
> Florian Fainelli (1):
> Merge tag 'bcm2835-defconfig-64-next-2016-09-22' into defconfig-arm64/next
>
> arch/arm64/configs/defconfig | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
Arnd, Kevin, Olof,
Following Olof's response here:
https://www.spinics.net/lists/arm-kernel/msg534687.html
do you think we could merge these for 4.9-rcX? Let me know if I should
send a fresh pull request for that.
Thank you!
--
Florian
^ permalink raw reply
* [PATCH] asm-generic: Drop getrlimit and setrlimit syscalls from default list
From: Vineet Gupta @ 2016-10-23 20:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477138444-14993-1-git-send-email-ynorov@caviumnetworks.com>
On 10/22/2016 05:14 AM, Yury Norov wrote:
> The newer prlimit64 syscall provides all the functionality provided by
> the getrlimit and setrlimit syscalls and adds the pid of target process,
> so future architectures won't need to include getrlimit and setrlimit.
>
> Therefore drop getrlimit and setrlimit syscalls from the generic syscall
> list unless __ARCH_WANT_SET_GET_RLIMIT is defined by the architecture's
> unistd.h prior to including asm-generic/unistd.h, and adjust all
> architectures using the generic syscall list to define it so that no
> in-tree architectures are affected.
>
> Cc: Vineet Gupta <vgupta@synopsys.com>
Acked-by: Vineet Gupta <vgupta@synopsys.com> #arch/arc bits
Thx,
-Vineet
^ permalink raw reply
* [PATCH] arm64: Neaten show_regs, remove KERN_CONT
From: Joe Perches @ 2016-10-23 20:40 UTC (permalink / raw)
To: linux-arm-kernel
commit db4b0710fae9 ("arm64: fix show_regs fallout from KERN_CONT changes")
corrected the KERN_CONT fallout from commit 4bcc595ccd80
("printk: reinstate KERN_CONT for printing continuation lines"), but
the code still has unnecessary KERN_CONT uses. Remove them.
Miscellanea:
o Remove unnecessary trailing blank from the output too.
Signed-off-by: Joe Perches <joe@perches.com>
---
arch/arm64/kernel/process.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 01753cd7d3f0..2278e7197a8e 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -190,18 +190,16 @@ void __show_regs(struct pt_regs *regs)
i = top_reg;
- while (i >= 0) {
- printk("x%-2d: %016llx ", i, regs->regs[i]);
+ if (i >= 0 && !(i % 2)) {
+ printk("x%-2d: %016llx\n", i, regs->regs[i]);
i--;
-
- if (i % 2 == 0) {
- pr_cont("x%-2d: %016llx ", i, regs->regs[i]);
- i--;
- }
-
- pr_cont("\n");
}
- printk("\n");
+ while (i > 0) {
+ printk("x%-2d: %016llx x%-2d: %016llx\n",
+ i, regs->regs[i],
+ i - 1, regs->regs[i - 1]);
+ i -= 2;
+ }
}
void show_regs(struct pt_regs * regs)
--
2.10.0.rc2.1.g053435c
^ permalink raw reply related
* [PATCH RFC 10/10] drm/i2c: tda998x: switch to boolean is_on
From: Russell King @ 2016-10-23 19:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Rather than storing the DPMS mode (which will always be on or off) use a
boolean to store this instead.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 547cf99ac32d..1f9a25fe17f3 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -42,7 +42,7 @@ struct tda998x_priv {
struct mutex mutex;
u16 rev;
u8 current_page;
- int dpms;
+ bool is_on;
bool is_hdmi_sink;
bool is_hdmi_config;
u8 vip_cntrl_0;
@@ -1150,16 +1150,15 @@ static int tda998x_connector_init(struct tda998x_priv *priv,
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
+ bool on;
/* we only care about on or off: */
- if (mode != DRM_MODE_DPMS_ON)
- mode = DRM_MODE_DPMS_OFF;
+ on = mode == DRM_MODE_DPMS_ON;
- if (mode == priv->dpms)
+ if (on == priv->is_on)
return;
- switch (mode) {
- case DRM_MODE_DPMS_ON:
+ if (on) {
/* enable video ports, audio will be enabled later */
reg_write(priv, REG_ENA_VP_0, 0xff);
reg_write(priv, REG_ENA_VP_1, 0xff);
@@ -1168,16 +1167,16 @@ static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
reg_write(priv, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
reg_write(priv, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
reg_write(priv, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
- break;
- case DRM_MODE_DPMS_OFF:
+
+ priv->is_on = true;
+ } else {
/* disable video ports */
reg_write(priv, REG_ENA_VP_0, 0x00);
reg_write(priv, REG_ENA_VP_1, 0x00);
reg_write(priv, REG_ENA_VP_2, 0x00);
- break;
- }
- priv->dpms = mode;
+ priv->is_on = false;
+ }
}
static void
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 09/10] drm/i2c: tda998x: remove complexity from tda998x_audio_get_eld()
From: Russell King @ 2016-10-23 19:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
tda998x_audio_get_eld() is needlessly complex - the connector associated
with the encoder is always our own priv->connector. Remove this
complexity, but ensure that there are no races when copying out the ELD.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 6a7095b66a69..547cf99ac32d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -911,21 +911,13 @@ static int tda998x_audio_get_eld(struct device *dev, void *data,
uint8_t *buf, size_t len)
{
struct tda998x_priv *priv = dev_get_drvdata(dev);
- struct drm_mode_config *config = &priv->encoder.dev->mode_config;
- struct drm_connector *connector;
- int ret = -ENODEV;
-
- mutex_lock(&config->mutex);
- list_for_each_entry(connector, &config->connector_list, head) {
- if (&priv->encoder == connector->encoder) {
- memcpy(buf, connector->eld,
- min(sizeof(connector->eld), len));
- ret = 0;
- }
- }
- mutex_unlock(&config->mutex);
- return ret;
+ mutex_lock(&priv->audio_mutex);
+ memcpy(buf, priv->connector.eld,
+ min(sizeof(priv->connector.eld), len));
+ mutex_unlock(&priv->audio_mutex);
+
+ return 0;
}
static const struct hdmi_codec_ops audio_codec_ops = {
@@ -1082,7 +1074,10 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector, edid);
n = drm_add_edid_modes(connector, edid);
priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+
+ mutex_lock(&priv->audio_mutex);
drm_edid_to_eld(connector, edid);
+ mutex_unlock(&priv->audio_mutex);
kfree(edid);
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 08/10] drm/i2c: tda998x: group audio functions together
From: Russell King @ 2016-10-23 19:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Group the TDA998x audio functions together rather than split between
two different locations in the file, keeping like code together.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 278 +++++++++++++++++++-------------------
1 file changed, 140 insertions(+), 138 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 4379c6aa1c48..6a7095b66a69 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -702,6 +702,8 @@ tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode)
tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, &frame);
}
+/* Audio support */
+
static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
{
if (on) {
@@ -820,6 +822,144 @@ tda998x_configure_audio(struct tda998x_priv *priv,
return tda998x_write_aif(priv, ¶ms->cea);
}
+static int tda998x_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct tda998x_priv *priv = dev_get_drvdata(dev);
+ int i, ret;
+ struct tda998x_audio_params audio = {
+ .sample_width = params->sample_width,
+ .sample_rate = params->sample_rate,
+ .cea = params->cea,
+ };
+
+ memcpy(audio.status, params->iec.status,
+ min(sizeof(audio.status), sizeof(params->iec.status)));
+
+ switch (daifmt->fmt) {
+ case HDMI_I2S:
+ if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
+ daifmt->bit_clk_master || daifmt->frame_clk_master) {
+ dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
+ daifmt->bit_clk_inv, daifmt->frame_clk_inv,
+ daifmt->bit_clk_master,
+ daifmt->frame_clk_master);
+ return -EINVAL;
+ }
+ for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+ if (priv->audio_port[i].format == AFMT_I2S)
+ audio.config = priv->audio_port[i].config;
+ audio.format = AFMT_I2S;
+ break;
+ case HDMI_SPDIF:
+ for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+ if (priv->audio_port[i].format == AFMT_SPDIF)
+ audio.config = priv->audio_port[i].config;
+ audio.format = AFMT_SPDIF;
+ break;
+ default:
+ dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+ return -EINVAL;
+ }
+
+ if (audio.config == 0) {
+ dev_err(dev, "%s: No audio configutation found\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&priv->audio_mutex);
+ /* We must not program the TDA998x for audio if the sink is DVI. */
+ if (priv->is_hdmi_config)
+ ret = tda998x_configure_audio(priv, &audio);
+ else
+ ret = 0;
+
+ if (ret == 0)
+ priv->audio_params = audio;
+ mutex_unlock(&priv->audio_mutex);
+
+ return ret;
+}
+
+static void tda998x_audio_shutdown(struct device *dev, void *data)
+{
+ struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->audio_mutex);
+
+ reg_write(priv, REG_ENA_AP, 0);
+
+ priv->audio_params.format = AFMT_UNUSED;
+
+ mutex_unlock(&priv->audio_mutex);
+}
+
+int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
+{
+ struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->audio_mutex);
+
+ tda998x_audio_mute(priv, enable);
+
+ mutex_unlock(&priv->audio_mutex);
+ return 0;
+}
+
+static int tda998x_audio_get_eld(struct device *dev, void *data,
+ uint8_t *buf, size_t len)
+{
+ struct tda998x_priv *priv = dev_get_drvdata(dev);
+ struct drm_mode_config *config = &priv->encoder.dev->mode_config;
+ struct drm_connector *connector;
+ int ret = -ENODEV;
+
+ mutex_lock(&config->mutex);
+ list_for_each_entry(connector, &config->connector_list, head) {
+ if (&priv->encoder == connector->encoder) {
+ memcpy(buf, connector->eld,
+ min(sizeof(connector->eld), len));
+ ret = 0;
+ }
+ }
+ mutex_unlock(&config->mutex);
+
+ return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+ .hw_params = tda998x_audio_hw_params,
+ .audio_shutdown = tda998x_audio_shutdown,
+ .digital_mute = tda998x_audio_digital_mute,
+ .get_eld = tda998x_audio_get_eld,
+};
+
+static int tda998x_audio_codec_init(struct tda998x_priv *priv,
+ struct device *dev)
+{
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &audio_codec_ops,
+ .max_i2s_channels = 2,
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
+ if (priv->audio_port[i].format == AFMT_I2S &&
+ priv->audio_port[i].config != 0)
+ codec_data.i2s = 1;
+ if (priv->audio_port[i].format == AFMT_SPDIF &&
+ priv->audio_port[i].config != 0)
+ codec_data.spdif = 1;
+ }
+
+ priv->audio_pdev = platform_device_register_data(
+ dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+ &codec_data, sizeof(codec_data));
+
+ return PTR_ERR_OR_ZERO(priv->audio_pdev);
+}
+
/* DRM connector functions */
static int tda998x_connector_dpms(struct drm_connector *connector, int mode)
@@ -1261,144 +1401,6 @@ static void tda998x_destroy(struct tda998x_priv *priv)
i2c_unregister_device(priv->cec);
}
-static int tda998x_audio_hw_params(struct device *dev, void *data,
- struct hdmi_codec_daifmt *daifmt,
- struct hdmi_codec_params *params)
-{
- struct tda998x_priv *priv = dev_get_drvdata(dev);
- int i, ret;
- struct tda998x_audio_params audio = {
- .sample_width = params->sample_width,
- .sample_rate = params->sample_rate,
- .cea = params->cea,
- };
-
- memcpy(audio.status, params->iec.status,
- min(sizeof(audio.status), sizeof(params->iec.status)));
-
- switch (daifmt->fmt) {
- case HDMI_I2S:
- if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
- daifmt->bit_clk_master || daifmt->frame_clk_master) {
- dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
- daifmt->bit_clk_inv, daifmt->frame_clk_inv,
- daifmt->bit_clk_master,
- daifmt->frame_clk_master);
- return -EINVAL;
- }
- for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
- if (priv->audio_port[i].format == AFMT_I2S)
- audio.config = priv->audio_port[i].config;
- audio.format = AFMT_I2S;
- break;
- case HDMI_SPDIF:
- for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
- if (priv->audio_port[i].format == AFMT_SPDIF)
- audio.config = priv->audio_port[i].config;
- audio.format = AFMT_SPDIF;
- break;
- default:
- dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
- return -EINVAL;
- }
-
- if (audio.config == 0) {
- dev_err(dev, "%s: No audio configutation found\n", __func__);
- return -EINVAL;
- }
-
- mutex_lock(&priv->audio_mutex);
- /* We must not program the TDA998x for audio if the sink is DVI. */
- if (priv->is_hdmi_config)
- ret = tda998x_configure_audio(priv, &audio);
- else
- ret = 0;
-
- if (ret == 0)
- priv->audio_params = audio;
- mutex_unlock(&priv->audio_mutex);
-
- return ret;
-}
-
-static void tda998x_audio_shutdown(struct device *dev, void *data)
-{
- struct tda998x_priv *priv = dev_get_drvdata(dev);
-
- mutex_lock(&priv->audio_mutex);
-
- reg_write(priv, REG_ENA_AP, 0);
-
- priv->audio_params.format = AFMT_UNUSED;
-
- mutex_unlock(&priv->audio_mutex);
-}
-
-int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
-{
- struct tda998x_priv *priv = dev_get_drvdata(dev);
-
- mutex_lock(&priv->audio_mutex);
-
- tda998x_audio_mute(priv, enable);
-
- mutex_unlock(&priv->audio_mutex);
- return 0;
-}
-
-static int tda998x_audio_get_eld(struct device *dev, void *data,
- uint8_t *buf, size_t len)
-{
- struct tda998x_priv *priv = dev_get_drvdata(dev);
- struct drm_mode_config *config = &priv->encoder.dev->mode_config;
- struct drm_connector *connector;
- int ret = -ENODEV;
-
- mutex_lock(&config->mutex);
- list_for_each_entry(connector, &config->connector_list, head) {
- if (&priv->encoder == connector->encoder) {
- memcpy(buf, connector->eld,
- min(sizeof(connector->eld), len));
- ret = 0;
- }
- }
- mutex_unlock(&config->mutex);
-
- return ret;
-}
-
-static const struct hdmi_codec_ops audio_codec_ops = {
- .hw_params = tda998x_audio_hw_params,
- .audio_shutdown = tda998x_audio_shutdown,
- .digital_mute = tda998x_audio_digital_mute,
- .get_eld = tda998x_audio_get_eld,
-};
-
-static int tda998x_audio_codec_init(struct tda998x_priv *priv,
- struct device *dev)
-{
- struct hdmi_codec_pdata codec_data = {
- .ops = &audio_codec_ops,
- .max_i2s_channels = 2,
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
- if (priv->audio_port[i].format == AFMT_I2S &&
- priv->audio_port[i].config != 0)
- codec_data.i2s = 1;
- if (priv->audio_port[i].format == AFMT_SPDIF &&
- priv->audio_port[i].config != 0)
- codec_data.spdif = 1;
- }
-
- priv->audio_pdev = platform_device_register_data(
- dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
- &codec_data, sizeof(codec_data));
-
- return PTR_ERR_OR_ZERO(priv->audio_pdev);
-}
-
/* I2C driver functions */
static int tda998x_get_audio_ports(struct tda998x_priv *priv,
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 07/10] drm/i2c: tda998x: separate connector initialisation
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Separate out the connector initialisation from the rest of the drivers
initialisation.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 58 +++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index bb5389fbd059..4379c6aa1c48 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -979,6 +979,37 @@ const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
.best_encoder = tda998x_connector_best_encoder,
};
+static int tda998x_connector_init(struct tda998x_priv *priv,
+ struct drm_device *drm)
+{
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ connector->interlace_allowed = 1;
+
+ if (priv->hdmi->irq)
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+
+ drm_connector_helper_add(connector, &tda998x_connector_helper_funcs);
+ ret = drm_connector_init(drm, connector, &tda998x_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_register(&priv->connector);
+ if (ret) {
+ drm_connector_cleanup(&priv->connector);
+ return ret;
+ }
+
+ drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
+
+ return 0;
+}
+
/* DRM encoder functions */
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
@@ -1212,16 +1243,6 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
mutex_unlock(&priv->audio_mutex);
}
-static void tda998x_encoder_set_polling(struct tda998x_priv *priv,
- struct drm_connector *connector)
-{
- if (priv->hdmi->irq)
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- else
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
-}
-
static void tda998x_destroy(struct tda998x_priv *priv)
{
/* disable all IRQs and free the IRQ handler */
@@ -1634,7 +1655,6 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
crtcs = 1 << 0;
}
- priv->connector.interlace_allowed = 1;
priv->encoder.possible_crtcs = crtcs;
ret = tda998x_create(client, priv);
@@ -1644,32 +1664,18 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
if (!dev->of_node && params)
tda998x_set_config(priv, params);
- tda998x_encoder_set_polling(priv, &priv->connector);
-
drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
goto err_encoder;
- drm_connector_helper_add(&priv->connector,
- &tda998x_connector_helper_funcs);
- ret = drm_connector_init(drm, &priv->connector,
- &tda998x_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA);
+ ret = tda998x_connector_init(priv, drm);
if (ret)
goto err_connector;
- ret = drm_connector_register(&priv->connector);
- if (ret)
- goto err_sysfs;
-
- drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
-
return 0;
-err_sysfs:
- drm_connector_cleanup(&priv->connector);
err_connector:
drm_encoder_cleanup(&priv->encoder);
err_encoder:
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 06/10] drm/i2c: tda998x: group connector functions and funcs together
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Group the TDA998x connector functions and funcs structures together
before the encoder support, rather than scattered amongst the rest of
the file. This keeps like code together.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 316 +++++++++++++++++++-------------------
1 file changed, 159 insertions(+), 157 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d3951aee2b09..bb5389fbd059 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -820,6 +820,165 @@ tda998x_configure_audio(struct tda998x_priv *priv,
return tda998x_write_aif(priv, ¶ms->cea);
}
+/* DRM connector functions */
+
+static int tda998x_connector_dpms(struct drm_connector *connector, int mode)
+{
+ if (drm_core_check_feature(connector->dev, DRIVER_ATOMIC))
+ return drm_atomic_helper_connector_dpms(connector, mode);
+ else
+ return drm_helper_connector_dpms(connector, mode);
+}
+
+static enum drm_connector_status
+tda998x_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
+ u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
+
+ return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static void tda998x_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs tda998x_connector_funcs = {
+ .dpms = tda998x_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = tda998x_connector_detect,
+ .destroy = tda998x_connector_destroy,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
+{
+ struct tda998x_priv *priv = data;
+ u8 offset, segptr;
+ int ret, i;
+
+ offset = (blk & 1) ? 128 : 0;
+ segptr = blk / 2;
+
+ reg_write(priv, REG_DDC_ADDR, 0xa0);
+ reg_write(priv, REG_DDC_OFFS, offset);
+ reg_write(priv, REG_DDC_SEGM_ADDR, 0x60);
+ reg_write(priv, REG_DDC_SEGM, segptr);
+
+ /* enable reading EDID: */
+ priv->wq_edid_wait = 1;
+ reg_write(priv, REG_EDID_CTRL, 0x1);
+
+ /* flag must be cleared by sw: */
+ reg_write(priv, REG_EDID_CTRL, 0x0);
+
+ /* wait for block read to complete: */
+ if (priv->hdmi->irq) {
+ i = wait_event_timeout(priv->wq_edid,
+ !priv->wq_edid_wait,
+ msecs_to_jiffies(100));
+ if (i < 0) {
+ dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i);
+ return i;
+ }
+ } else {
+ for (i = 100; i > 0; i--) {
+ msleep(1);
+ ret = reg_read(priv, REG_INT_FLAGS_2);
+ if (ret < 0)
+ return ret;
+ if (ret & INT_FLAGS_2_EDID_BLK_RD)
+ break;
+ }
+ }
+
+ if (i == 0) {
+ dev_err(&priv->hdmi->dev, "read edid timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length);
+ if (ret != length) {
+ dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n",
+ blk, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tda998x_connector_get_modes(struct drm_connector *connector)
+{
+ struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
+ struct edid *edid;
+ int n;
+
+ /*
+ * If we get killed while waiting for the HPD timeout, return
+ * no modes found: we are not in a restartable path, so we
+ * can't handle signals gracefully.
+ */
+ if (tda998x_edid_delay_wait(priv))
+ return 0;
+
+ if (priv->rev == TDA19988)
+ reg_clear(priv, REG_TX4, TX4_PD_RAM);
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (priv->rev == TDA19988)
+ reg_set(priv, REG_TX4, TX4_PD_RAM);
+
+ if (!edid) {
+ dev_warn(&priv->hdmi->dev, "failed to read EDID\n");
+ return 0;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+ priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ return n;
+}
+
+static int tda998x_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /* TDA19988 dotclock can go up to 165MHz */
+ struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
+
+ if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000))
+ return MODE_CLOCK_HIGH;
+ if (mode->htotal >= BIT(13))
+ return MODE_BAD_HVALUE;
+ if (mode->vtotal >= BIT(11))
+ return MODE_BAD_VVALUE;
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+tda998x_connector_best_encoder(struct drm_connector *connector)
+{
+ struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
+
+ return &priv->encoder;
+}
+
+static
+const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
+ .get_modes = tda998x_connector_get_modes,
+ .mode_valid = tda998x_connector_mode_valid,
+ .best_encoder = tda998x_connector_best_encoder,
+};
+
/* DRM encoder functions */
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
@@ -855,21 +1014,6 @@ static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
priv->dpms = mode;
}
-static int tda998x_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /* TDA19988 dotclock can go up to 165MHz */
- struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
-
- if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000))
- return MODE_CLOCK_HIGH;
- if (mode->htotal >= BIT(13))
- return MODE_BAD_HVALUE;
- if (mode->vtotal >= BIT(11))
- return MODE_BAD_VVALUE;
- return MODE_OK;
-}
-
static void
tda998x_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
@@ -1068,109 +1212,6 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
mutex_unlock(&priv->audio_mutex);
}
-static enum drm_connector_status
-tda998x_connector_detect(struct drm_connector *connector, bool force)
-{
- struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
- u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
-
- return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
- connector_status_disconnected;
-}
-
-static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
-{
- struct tda998x_priv *priv = data;
- u8 offset, segptr;
- int ret, i;
-
- offset = (blk & 1) ? 128 : 0;
- segptr = blk / 2;
-
- reg_write(priv, REG_DDC_ADDR, 0xa0);
- reg_write(priv, REG_DDC_OFFS, offset);
- reg_write(priv, REG_DDC_SEGM_ADDR, 0x60);
- reg_write(priv, REG_DDC_SEGM, segptr);
-
- /* enable reading EDID: */
- priv->wq_edid_wait = 1;
- reg_write(priv, REG_EDID_CTRL, 0x1);
-
- /* flag must be cleared by sw: */
- reg_write(priv, REG_EDID_CTRL, 0x0);
-
- /* wait for block read to complete: */
- if (priv->hdmi->irq) {
- i = wait_event_timeout(priv->wq_edid,
- !priv->wq_edid_wait,
- msecs_to_jiffies(100));
- if (i < 0) {
- dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i);
- return i;
- }
- } else {
- for (i = 100; i > 0; i--) {
- msleep(1);
- ret = reg_read(priv, REG_INT_FLAGS_2);
- if (ret < 0)
- return ret;
- if (ret & INT_FLAGS_2_EDID_BLK_RD)
- break;
- }
- }
-
- if (i == 0) {
- dev_err(&priv->hdmi->dev, "read edid timeout\n");
- return -ETIMEDOUT;
- }
-
- ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length);
- if (ret != length) {
- dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n",
- blk, ret);
- return ret;
- }
-
- return 0;
-}
-
-static int tda998x_connector_get_modes(struct drm_connector *connector)
-{
- struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
- struct edid *edid;
- int n;
-
- /*
- * If we get killed while waiting for the HPD timeout, return
- * no modes found: we are not in a restartable path, so we
- * can't handle signals gracefully.
- */
- if (tda998x_edid_delay_wait(priv))
- return 0;
-
- if (priv->rev == TDA19988)
- reg_clear(priv, REG_TX4, TX4_PD_RAM);
-
- edid = drm_do_get_edid(connector, read_edid_block, priv);
-
- if (priv->rev == TDA19988)
- reg_set(priv, REG_TX4, TX4_PD_RAM);
-
- if (!edid) {
- dev_warn(&priv->hdmi->dev, "failed to read EDID\n");
- return 0;
- }
-
- drm_mode_connector_update_edid_property(connector, edid);
- n = drm_add_edid_modes(connector, edid);
- priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
- drm_edid_to_eld(connector, edid);
-
- kfree(edid);
-
- return n;
-}
-
static void tda998x_encoder_set_polling(struct tda998x_priv *priv,
struct drm_connector *connector)
{
@@ -1550,45 +1591,6 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = {
.destroy = tda998x_encoder_destroy,
};
-static struct drm_encoder *
-tda998x_connector_best_encoder(struct drm_connector *connector)
-{
- struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
-
- return &priv->encoder;
-}
-
-static
-const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
- .get_modes = tda998x_connector_get_modes,
- .mode_valid = tda998x_connector_mode_valid,
- .best_encoder = tda998x_connector_best_encoder,
-};
-
-static void tda998x_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-
-static int tda998x_connector_dpms(struct drm_connector *connector, int mode)
-{
- if (drm_core_check_feature(connector->dev, DRIVER_ATOMIC))
- return drm_atomic_helper_connector_dpms(connector, mode);
- else
- return drm_helper_connector_dpms(connector, mode);
-}
-
-static const struct drm_connector_funcs tda998x_connector_funcs = {
- .dpms = tda998x_connector_dpms,
- .reset = drm_atomic_helper_connector_reset,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .detect = tda998x_connector_detect,
- .destroy = tda998x_connector_destroy,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
static void tda998x_set_config(struct tda998x_priv *priv,
const struct tda998x_encoder_params *p)
{
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 05/10] drm/i2c: tda998x: move and rename tda998x_encoder_set_config()
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
The naming of tda998x_encoder_set_config() is a left-over from when
TDA998x was a slave encoder. Since this is part of the initialisation,
drop the _encoder from the name, and move it near tda998x_bind().
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 40 +++++++++++++++++++--------------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 134cd3f26a07..d3951aee2b09 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -822,25 +822,6 @@ tda998x_configure_audio(struct tda998x_priv *priv,
/* DRM encoder functions */
-static void tda998x_encoder_set_config(struct tda998x_priv *priv,
- const struct tda998x_encoder_params *p)
-{
- priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
- (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
- VIP_CNTRL_0_SWAP_B(p->swap_b) |
- (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
- priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
- (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
- VIP_CNTRL_1_SWAP_D(p->swap_d) |
- (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
- priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
- (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
- VIP_CNTRL_2_SWAP_F(p->swap_f) |
- (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
-
- priv->audio_params = p->audio_params;
-}
-
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
@@ -1608,6 +1589,25 @@ static const struct drm_connector_funcs tda998x_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
+static void tda998x_set_config(struct tda998x_priv *priv,
+ const struct tda998x_encoder_params *p)
+{
+ priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
+ (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
+ VIP_CNTRL_0_SWAP_B(p->swap_b) |
+ (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
+ priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
+ (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
+ VIP_CNTRL_1_SWAP_D(p->swap_d) |
+ (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
+ priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
+ (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
+ VIP_CNTRL_2_SWAP_F(p->swap_f) |
+ (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
+
+ priv->audio_params = p->audio_params;
+}
+
static int tda998x_bind(struct device *dev, struct device *master, void *data)
{
struct tda998x_encoder_params *params = dev->platform_data;
@@ -1640,7 +1640,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
return ret;
if (!dev->of_node && params)
- tda998x_encoder_set_config(priv, params);
+ tda998x_set_config(priv, params);
tda998x_encoder_set_polling(priv, &priv->connector);
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 04/10] drm/i2c: tda998x: correct function name in comments
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Correct two references to tda998x_connector_get_modes() which were
incorrectly referring to tda998x_encoder_get_modes().
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 858237f7f4d6..134cd3f26a07 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -581,9 +581,9 @@ tda998x_reset(struct tda998x_priv *priv)
* HPD assertion: it needs a delay of 100ms to avoid timing out while
* trying to read EDID data.
*
- * However, tda998x_encoder_get_modes() may be called at any moment
+ * However, tda998x_connector_get_modes() may be called at any moment
* after tda998x_connector_detect() indicates that we are connected, so
- * we need to delay probing modes in tda998x_encoder_get_modes() after
+ * we need to delay probing modes in tda998x_connector_get_modes() after
* we have seen a HPD inactive->active transition. This code implements
* that delay.
*/
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 03/10] drm/i2c: tda998x: avoid racy access to mode clock
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
Avoid a racy access to the mode clock by storing the current mode clock
during a mode set under the audio mutex. This allows us to access it
from the audio path in a safe way.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 495ee3fed661..858237f7f4d6 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -48,6 +48,7 @@ struct tda998x_priv {
u8 vip_cntrl_0;
u8 vip_cntrl_1;
u8 vip_cntrl_2;
+ unsigned long tdms_clock;
struct tda998x_audio_params audio_params;
struct platform_device *audio_pdev;
@@ -714,8 +715,7 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
static int
tda998x_configure_audio(struct tda998x_priv *priv,
- struct tda998x_audio_params *params,
- unsigned mode_clock)
+ struct tda998x_audio_params *params)
{
u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
u32 n;
@@ -772,7 +772,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
* assume 100MHz requires larger divider.
*/
adiv = AUDIO_DIV_SERCLK_8;
- if (mode_clock > 100000)
+ if (priv->tdms_clock > 100000)
adiv++; /* AUDIO_DIV_SERCLK_16 */
/* S/PDIF asks for a larger divider */
@@ -1077,10 +1077,10 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
tda998x_write_avi(priv, adjusted_mode);
+ priv->tdms_clock = adjusted_mode->clock;
+
if (priv->audio_params.format != AFMT_UNUSED)
- tda998x_configure_audio(priv,
- &priv->audio_params,
- adjusted_mode->clock);
+ tda998x_configure_audio(priv, &priv->audio_params);
}
priv->is_hdmi_config = priv->is_hdmi_sink;
@@ -1230,9 +1230,6 @@ static int tda998x_audio_hw_params(struct device *dev, void *data,
.cea = params->cea,
};
- if (!priv->encoder.crtc)
- return -ENODEV;
-
memcpy(audio.status, params->iec.status,
min(sizeof(audio.status), sizeof(params->iec.status)));
@@ -1270,8 +1267,7 @@ static int tda998x_audio_hw_params(struct device *dev, void *data,
mutex_lock(&priv->audio_mutex);
/* We must not program the TDA998x for audio if the sink is DVI. */
if (priv->is_hdmi_config)
- ret = tda998x_configure_audio(priv, &audio,
- priv->encoder.crtc->hwmode.clock);
+ ret = tda998x_configure_audio(priv, &audio);
else
ret = 0;
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 02/10] drm/i2c: tda998x: avoid configuring audio for DVI mode
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
We must not configure the audio path when the sink is a DVI device, as
DVI has no capability to receive HDMI audio. HDMI audio is a HDMI only
feature, requiring HDMI infoframes.
There's a question concerning how to handle a DVI connected device when
the audio device is opened - we save the audio configuration, so that if
a HDMI device is hotplugged, audio will then work. This seems a
reasonable expectation.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d72bc30a3bce..495ee3fed661 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -44,6 +44,7 @@ struct tda998x_priv {
u8 current_page;
int dpms;
bool is_hdmi_sink;
+ bool is_hdmi_config;
u8 vip_cntrl_0;
u8 vip_cntrl_1;
u8 vip_cntrl_2;
@@ -971,6 +972,8 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
div = 3;
}
+ mutex_lock(&priv->audio_mutex);
+
/* mute the audio FIFO: */
reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
@@ -1074,13 +1077,14 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
tda998x_write_avi(priv, adjusted_mode);
- mutex_lock(&priv->audio_mutex);
if (priv->audio_params.format != AFMT_UNUSED)
tda998x_configure_audio(priv,
&priv->audio_params,
adjusted_mode->clock);
- mutex_unlock(&priv->audio_mutex);
}
+
+ priv->is_hdmi_config = priv->is_hdmi_sink;
+ mutex_unlock(&priv->audio_mutex);
}
static enum drm_connector_status
@@ -1264,9 +1268,12 @@ static int tda998x_audio_hw_params(struct device *dev, void *data,
}
mutex_lock(&priv->audio_mutex);
- ret = tda998x_configure_audio(priv,
- &audio,
- priv->encoder.crtc->hwmode.clock);
+ /* We must not program the TDA998x for audio if the sink is DVI. */
+ if (priv->is_hdmi_config)
+ ret = tda998x_configure_audio(priv, &audio,
+ priv->encoder.crtc->hwmode.clock);
+ else
+ ret = 0;
if (ret == 0)
priv->audio_params = audio;
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 01/10] drm/i2c: tda998x: avoid race in tda998x_encoder_mode_set()
From: Russell King @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023191002.GJ1041@n2100.armlinux.org.uk>
As priv->audio_params can now be changed at run time, we need to be more
careful about how we deal with a mode set. We must take the audio lock
while checking if there's a valid audio configuration.
However, it's slightly worse than that - during mode set, we mute the
audio, and it must not be unmuted until we have finished the mode set.
It is possible that the audio side may start while a mode set is in
progress, so take the audio_mutex lock around the whole mode setting
procedure.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 9798d400d817..d72bc30a3bce 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -1074,13 +1074,12 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
tda998x_write_avi(priv, adjusted_mode);
- if (priv->audio_params.format != AFMT_UNUSED) {
- mutex_lock(&priv->audio_mutex);
+ mutex_lock(&priv->audio_mutex);
+ if (priv->audio_params.format != AFMT_UNUSED)
tda998x_configure_audio(priv,
&priv->audio_params,
adjusted_mode->clock);
- mutex_unlock(&priv->audio_mutex);
- }
+ mutex_unlock(&priv->audio_mutex);
}
}
--
2.1.0
^ permalink raw reply related
* [PATCH RFC 0/10] tda998x initial cleanups from bridge conversion
From: Russell King - ARM Linux @ 2016-10-23 19:10 UTC (permalink / raw)
To: linux-arm-kernel
As part of the discussion about converting tda998x to a bridge, here's
some preparatory work for that, which includes a bunch of fixes. I'm
sending this out _early_ as I'm not going to be working on any kernel
stuff next week (it's likely I won't even be reading email.) So it may
be a little rough around the edges.
Essentially, this is a series of cleanups, complexity removal, and
avoiding races with the newly introduced audio support. Even without
the bridge conversion, I think all these are still worthwhile to have.
This series of changes can also be found in my drm-tda998x-devel branch
as an unstable series of commits (iow, I'm going to rebase/rework these
at some point, so the commit IDs are not stable, so do not merge this.)
git://git.armlinux.org.uk/~rmk/linux-arm.git drm-tda998x-devel
drivers/gpu/drm/i2c/tda998x_drv.c | 782 +++++++++++++++++++-------------------
1 file changed, 394 insertions(+), 388 deletions(-)
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH 0/6] clk: oxnas: Rework driver to add support for OX820
From: Michael Turquette @ 2016-10-23 17:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <147617274057.17481.2362814012745242458@resonance>
Quoting Michael Turquette (2016-10-11 00:59:00)
> Quoting Neil Armstrong (2016-10-05 17:07:46)
> > In order to to support the Oxford Semiconductor OX820 Soc clock gates,
> > rework the original driver with a structure inspired from the Qcom or Meson
> > drivers and using the new devm_clk_hw_register() call.
> >
> > The first patches add dt-bindings include file to clarify the clock indices.
> >
> > In future work, OX820 PLLs should also be handled by this driver.
>
> Series looks good to me. Will apply after -rc1 drops.
Applied.
Regards,
Mike
>
> Regards,
> Mike
>
> >
> > Neil Armstrong (6):
> > clk: oxnas: Add dt-bindings include file for OX810SE
> > clk: oxnas: Add dt-bindings include file for OX820
> > clk: oxnas: Rename to clk_oxnas_gate
> > clk: oxnas: Refactor to make use of devm_clk_hw_register()
> > clk: oxnas: Add OX820 Gate clocks
> > dt-bindings: clk: oxnas,stdclk: Add OX820 bindings
> >
> > .../devicetree/bindings/clock/oxnas,stdclk.txt | 19 +-
> > drivers/clk/clk-oxnas.c | 232 ++++++++++++++-------
> > include/dt-bindings/clock/oxsemi,ox810se.h | 30 +++
> > include/dt-bindings/clock/oxsemi,ox820.h | 40 ++++
> > 4 files changed, 231 insertions(+), 90 deletions(-)
> > create mode 100644 include/dt-bindings/clock/oxsemi,ox810se.h
> > create mode 100644 include/dt-bindings/clock/oxsemi,ox820.h
> >
> > --
> > 2.7.0
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> > the body of a message to majordomo at vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 01/14] dma: sun6i-dma: Add burst case of 4
From: Jean-Francois Moine @ 2016-10-23 16:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161004165553.GS5228@lukather>
On Tue, 4 Oct 2016 18:55:54 +0200
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> On Tue, Oct 04, 2016 at 12:40:11PM +0200, Jean-Francois Moine wrote:
> > On Tue, 4 Oct 2016 11:46:14 +0200
> > Myl?ne Josserand <mylene.josserand@free-electrons.com> wrote:
> >
> > > Add the case of a burst of 4 which is handled by the SoC.
> > >
> > > Signed-off-by: Myl?ne Josserand <mylene.josserand@free-electrons.com>
> > > ---
> > > drivers/dma/sun6i-dma.c | 2 ++
> > > 1 file changed, 2 insertions(+)
> > >
> > > diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> > > index 8346199..0485204 100644
> > > --- a/drivers/dma/sun6i-dma.c
> > > +++ b/drivers/dma/sun6i-dma.c
> > > @@ -240,6 +240,8 @@ static inline s8 convert_burst(u32 maxburst)
> > > switch (maxburst) {
> > > case 1:
> > > return 0;
> > > + case 4:
> > > + return 1;
> > > case 8:
> > > return 2;
> > > default:
> > > --
> > > 2.9.3
> >
> > This patch has already been rejected by Maxime in the threads
> > http://www.spinics.net/lists/dmaengine/msg08610.html
> > and
> > http://www.spinics.net/lists/dmaengine/msg08719.html
> >
> > I hope you will find the way he wants for this maxburst to be added.
>
> I was talking about something along these lines (not tested):
I wonder why you don't submit this yourself.
> -------8<---------
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index 83461994e418..573ac4608293 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -240,6 +240,8 @@ static inline s8 convert_burst(u32 maxburst)
> switch (maxburst) {
> case 1:
> return 0;
> + case 4:
> + return 1;
> case 8:
> return 2;
> default:
> @@ -1110,11 +1112,19 @@ static int sun6i_dma_probe(struct platform_device *pdev)
> sdc->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> + sdc->slave.dst_bursts = BIT(1) | BIT(8);
> + sdc->slave.src_bursts = BIT(1) | BIT(8);
> sdc->slave.directions = BIT(DMA_DEV_TO_MEM) |
> BIT(DMA_MEM_TO_DEV);
> sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
> sdc->slave.dev = &pdev->dev;
>
> + if (of_device_is_compatible(pdev->dev.of_node,
> + "allwinner,sun8i-h3-dma")) {
> + sdc->slave.dst_bursts |= BIT(4);
> + sdc->slave.src_bursts |= BIT(4);
> + }
> +
> sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels,
> sizeof(struct sun6i_pchan), GFP_KERNEL);
> if (!sdc->pchans)
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index cc535a478bae..f7bbec24bb58 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -673,6 +673,8 @@ struct dma_filter {
> * each type of direction, the dma controller should fill (1 <<
> * <TYPE>) and same should be checked by controller as well
> * @max_burst: max burst capability per-transfer
> + * @dst_bursts: bitfield of the available burst sizes for the destination
> + * @src_bursts: bitfield of the available burst sizes for the source
You did not define dst_bursts nor src_bursts.
> * @residue_granularity: granularity of the transfer residue reported
> * by tx_status
> * @device_alloc_chan_resources: allocate resources and return the
> @@ -800,6 +802,14 @@ struct dma_device {
> static inline int dmaengine_slave_config(struct dma_chan *chan,
> struct dma_slave_config *config)
> {
> + if (config->src_maxburst && config->device->src_bursts &&
> + !(BIT(config->src_maxburst) & config->device->src_bursts))
The maxburst may be as big as 4Kibytes, then, I am not sure that this
code will work!
> + return -EINVAL;
> +
> + if (config->dst_maxburst && config->device->dst_bursts &&
> + !(BIT(config->dst_maxburst) & config->device->dst_bursts))
> + return -EINVAL;
> +
> if (chan->device->device_config)
> return chan->device->device_config(chan, config);
> -------8<------------
Yes, I know that the burst size is always a power of 2.
The best way to check it would be to change the {src,dts}_maxburst to a
bitmap of the possible bursts as 0x0d for 1,4 and 8 bytes. But this
asks for a lot of changes...
--
Ken ar c'henta? | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
^ permalink raw reply
* [PATCH] ARM: dts: imx6sx: Fix LCDIF interrupt type
From: Shawn Guo @ 2016-10-23 14:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161002164435.5812-1-marex@denx.de>
On Sun, Oct 02, 2016 at 06:44:35PM +0200, Marek Vasut wrote:
> The LCDIF interrupt should be triggered by the rising edge of the
> IRQ line because we only want the interrupt to trigger once per each
> frame. It seems the LCDIF IRQ line cannot be explicitly de-asserted
> by software, so the previous behavior before this patch, where the
> interrupt was triggered by level-high status of the IRQ line, caused
> the interrupt to fire again immediatelly after it was handled, which
> caused the system to lock up due to the high rate of interrupts.
>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Cc: Lucas Stach <l.stach@pengutronix.de>
> Cc: Fabio Estevam <fabio.estevam@nxp.com>
> Cc: Shawn Guo <shawnguo@kernel.org>
Applied, thanks.
^ permalink raw reply
* [PATCH] ARM: dts: vfxxx: Add node corresponding to OCOTP
From: Shawn Guo @ 2016-10-23 13:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1475371822-28879-1-git-send-email-andrew.smirnov@gmail.com>
On Sat, Oct 01, 2016 at 06:30:22PM -0700, Andrey Smirnov wrote:
> Add node corresponding to OCOTP IP block.
>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Applied, thanks.
^ permalink raw reply
* [PATCH] ARM: dts: vf610-zii-dev-rev-b: Remove I2C3
From: Shawn Guo @ 2016-10-23 13:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1475371719-28736-1-git-send-email-andrew.smirnov@gmail.com>
On Sat, Oct 01, 2016 at 06:28:39PM -0700, Andrey Smirnov wrote:
> I2C3 bus was only brought out in revision A1 of the board and revision
> B1 only brings out 3 I2C busses (I2C0, I2C1 and I2C2).
>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Applied, thanks.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox