From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752059AbcJDE0h (ORCPT ); Tue, 4 Oct 2016 00:26:37 -0400 Received: from smtp.csie.ntu.edu.tw ([140.112.30.61]:50934 "EHLO smtp.csie.ntu.edu.tw" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750724AbcJDE0f (ORCPT ); Tue, 4 Oct 2016 00:26:35 -0400 MIME-Version: 1.0 In-Reply-To: <20161003114709.GC5228@lukather> References: <20161003110804.28235-1-wens@csie.org> <20161003110804.28235-6-wens@csie.org> <20161003114709.GC5228@lukather> From: Chen-Yu Tsai Date: Tue, 4 Oct 2016 12:26:08 +0800 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output To: Maxime Ripard Cc: Chen-Yu Tsai , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Rob Herring , Mark Rutland , Russell King , Linux-ALSA , linux-arm-kernel , linux-kernel , devicetree , linux-sunxi Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, Oct 3, 2016 at 7:47 PM, Maxime Ripard wrote: > Hi, > > On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote: >> The A31 has a similar codec to the A10/A20. The PCM parts are very >> similar, with just different register offsets. The analog paths are >> very different. There are more inputs and outputs. >> >> The quirks structure is expanded to include different register offsets >> and separate callbacks for creating the ASoC card. Also the DMA burst >> length is increased to 8. While the A10 DMA engine supports bursts of >> 1, 4 and 8, the A31 engine only supports 1 and 8. >> >> This patch adds support for the basic playback path of the A31 codec, >> from the DAC to the headphones. Headphone detection, microphone, >> signaling, other inputs/outputs and capture will be added later. >> >> Signed-off-by: Chen-Yu Tsai >> --- >> .../devicetree/bindings/sound/sun4i-codec.txt | 6 +- >> sound/soc/sunxi/sun4i-codec.c | 396 +++++++++++++++++---- >> 2 files changed, 334 insertions(+), 68 deletions(-) >> >> diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt >> index 0dce690f78f5..1d2411cea98d 100644 >> --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt >> +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt >> @@ -1,8 +1,10 @@ >> * Allwinner A10 Codec >> >> Required properties: >> -- compatible: must be either "allwinner,sun4i-a10-codec" or >> - "allwinner,sun7i-a20-codec" >> +- compatible: must be one of the following compatibles: >> + - "allwinner,sun4i-a10-codec" >> + - "allwinner,sun6i-a31-codec" >> + - "allwinner,sun7i-a20-codec" > > I'm guessing it needs a reset line? There isn't one. I know, weird. >> - reg: must contain the registers location and length >> - interrupts: must contain the codec interrupt >> - dmas: DMA channels for tx and rx dma. See the DMA client binding, >> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c >> index e047ec06d538..9916714ecb71 100644 >> --- a/sound/soc/sunxi/sun4i-codec.c >> +++ b/sound/soc/sunxi/sun4i-codec.c >> @@ -3,6 +3,7 @@ >> * Copyright 2014 Jon Smirl >> * Copyright 2015 Maxime Ripard >> * Copyright 2015 Adam Sampson >> + * Copyright 2016 Chen-Yu Tsai >> * >> * Based on the Allwinner SDK driver, released under the GPL. >> * >> @@ -24,8 +25,9 @@ >> #include >> #include >> #include >> -#include >> #include >> +#include >> +#include >> #include >> #include >> #include >> @@ -55,6 +57,8 @@ >> #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) >> #define SUN4I_CODEC_DAC_FIFOS (0x08) >> #define SUN4I_CODEC_DAC_TXDATA (0x0c) >> + >> +/* Codec DAC side analog signal controls */ >> #define SUN4I_CODEC_DAC_ACTL (0x10) >> #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) >> #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) >> @@ -81,6 +85,8 @@ >> #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) >> #define SUN4I_CODEC_ADC_FIFOS (0x20) >> #define SUN4I_CODEC_ADC_RXDATA (0x24) >> + >> +/* Codec ADC side analog signal controls */ >> #define SUN4I_CODEC_ADC_ACTL (0x28) >> #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) >> #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) >> @@ -93,18 +99,106 @@ >> #define SUN4I_CODEC_ADC_ACTL_DDE (3) >> #define SUN4I_CODEC_ADC_DEBUG (0x2c) >> >> -/* Other various ADC registers */ >> +/* FIFO counters */ >> #define SUN4I_CODEC_DAC_TXCNT (0x30) >> #define SUN4I_CODEC_ADC_RXCNT (0x34) >> + >> +/* Other various ADC registers */ >> #define SUN7I_CODEC_AC_DAC_CAL (0x38) >> #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) >> >> +/*** sun6i specific register offsets ***/ >> +#define SUN6I_CODEC_ADC_FIFOC (0x10) >> +#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28) >> +#define SUN6I_CODEC_ADC_FIFOS (0x14) >> +#define SUN6I_CODEC_ADC_RXDATA (0x18) >> +#define SUN6I_CODEC_OM_DACA_CTRL (0x20) >> +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31) >> +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8) >> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7) >> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6) >> +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0) >> +#define SUN6I_CODEC_OM_PA_CTRL (0x24) >> +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31) >> +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15) >> +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12) >> +#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9) >> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6) >> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3) >> +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0) >> +#define SUN6I_CODEC_MIC_CTRL (0x28) >> +#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31) >> +#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30) >> +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28) >> +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25) >> +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24) >> +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21) >> +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20) >> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19) >> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18) >> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17) >> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16) >> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11) >> +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8) >> +#define SUN6I_CODEC_ADC_ACTL (0x2c) >> +#define SUN6I_CODEC_ADC_ACTL_ADCREN (31) >> +#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30) >> +#define SUN6I_CODEC_ADC_ACTL_ADCRG (27) >> +#define SUN6I_CODEC_ADC_ACTL_ADCLG (24) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8) >> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1) >> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0) >> +#define SUN6I_CODEC_ADDA_TUNE (0x30) >> +#define SUN6I_CODEC_CALIBRATION (0x34) >> +#define SUN6I_CODEC_DAC_TXCNT (0x40) >> +#define SUN6I_CODEC_ADC_RXCNT (0x44) >> +#define SUN6I_CODEC_HMIC_CTL (0x50) >> +#define SUN6I_CODEC_HMIC_DATA (0x54) >> + >> +/* TODO sun6i DAP (Digital Audio Processing) bits */ >> + >> +struct sun4i_codec_regs { >> + u32 adc_fifoc; >> + u32 adc_fifos; >> + u32 adc_rxdata; >> +}; >> + >> struct sun4i_codec { >> struct device *dev; >> struct regmap *regmap; >> struct clk *clk_apb; >> struct clk *clk_module; >> struct gpio_desc *gpio_pa; >> + const struct sun4i_codec_regs *regs; > > You're reimplementing reg_field here. Are you suggesting we do reg_fields for each register? Or all the bit fields separately. The latter would add quite a few pointers. >> >> struct snd_dmaengine_dai_dma_data capture_dma_data; >> struct snd_dmaengine_dai_dma_data playback_dma_data; >> @@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) >> static void sun4i_codec_start_capture(struct sun4i_codec *scodec) >> { >> /* Enable ADC DRQ */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), >> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); >> } >> @@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec) >> static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) >> { >> /* Disable ADC DRQ */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); >> } >> >> @@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, >> >> >> /* Flush RX FIFO */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), >> BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); >> >> >> /* Set RX FIFO trigger level */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, >> 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); >> >> @@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, >> * Allwinner's code mentions that it is related >> * related to microphone gain >> */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, >> - 0x3 << 25, >> - 0x1 << 25); >> + if (!of_device_is_compatible(scodec->dev->of_node, >> + "allwinner,sun6i-a31-codec")) { >> + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, >> + 0x3 << 25, >> + 0x1 << 25); >> + } >> >> if (of_device_is_compatible(scodec->dev->of_node, >> "allwinner,sun7i-a20-codec")) >> @@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, >> 0x1 << 8); >> >> /* Fill most significant bits with valid data MSB */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), >> BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); >> >> @@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, >> unsigned int hwrate) >> { >> /* Set ADC sample rate */ >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, >> hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); >> >> /* Set the number of channels we want to use */ >> if (params_channels(params) == 1) >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), >> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); >> else >> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, >> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc, >> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); >> >> return 0; >> @@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec, >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); >> >> - /* Set TX FIFO mode to padding the LSBs with 0 */ >> + /* Use higher 24 bits of TX FIFO */ >> regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), >> 0); >> @@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec, >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), >> 0); >> >> - /* Set TX FIFO mode to repeat the MSB */ >> + /* Use lower 16 bits of TX FIFO */ >> regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), >> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); >> @@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { >> }, >> }; >> >> -/*** Codec ***/ >> +/*** sun4i Codec ***/ >> static const struct snd_kcontrol_new sun4i_codec_pa_mute = >> SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, >> SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); >> @@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = { >> }, >> }; >> >> +/*** sun6i Codec ***/ >> + >> +/* mixer controls */ >> +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { >> + SOC_DAPM_DOUBLE("DAC Playback Switch", >> + SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL, >> + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0), >> + SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", >> + SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR, >> + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0), >> +}; >> + >> +/* headphone controls */ >> +static const char * const sun6i_codec_hp_src_enum_text[] = { >> + "DAC", "Mixer", >> +}; >> + >> +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum, >> + SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_LHPIS, >> + SUN6I_CODEC_OM_DACA_CTRL_RHPIS, >> + sun6i_codec_hp_src_enum_text); >> + >> +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = { >> + SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum), >> +}; >> + >> +/* volume / mute controls */ >> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0); >> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1); >> + >> +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { >> + SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, >> + SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, >> + sun6i_codec_dvol_scale), >> + SOC_SINGLE_TLV("Headphone Playback Volume", >> + SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, >> + sun6i_codec_hp_vol_scale), >> + SOC_DOUBLE("Headphone Playback Switch", >> + SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE, >> + SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), >> +}; >> + >> +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { >> + /* Digital parts of the DACs */ >> + SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, >> + SUN4I_CODEC_DAC_DPC_EN_DA, 0, >> + NULL, 0), >> + >> + /* Analog parts of the DACs */ >> + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0), >> + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0), >> + >> + /* Mixers */ >> + SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0, >> + sun6i_codec_mixer_controls), >> + SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL, >> + SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0, >> + sun6i_codec_mixer_controls), >> + >> + /* Headphone output path */ >> + SND_SOC_DAPM_MUX("Headphone Source Playback Route", >> + SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src), >> + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL, >> + SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0), >> + SND_SOC_DAPM_OUTPUT("HP"), >> +}; >> + >> +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { >> + /* DAC Routes */ >> + { "Left DAC", NULL, "DAC Enable" }, >> + { "Right DAC", NULL, "DAC Enable" }, >> + >> + /* Left Mixer Routes */ >> + { "Left Mixer", "DAC Playback Switch", "Left DAC" }, >> + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, >> + >> + /* Right Mixer Routes */ >> + { "Right Mixer", "DAC Playback Switch", "Right DAC" }, >> + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, >> + >> + /* Headphone Routes */ >> + { "Headphone Source Playback Route", "DAC", "Left DAC" }, >> + { "Headphone Source Playback Route", "DAC", "Right DAC" }, >> + { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, >> + { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, >> + { "Headphone Amp", NULL, "Headphone Source Playback Route" }, >> + { "HP", NULL, "Headphone Amp" }, >> +}; >> + >> +static struct snd_soc_codec_driver sun6i_codec_codec = { >> + .component_driver = { >> + .controls = sun6i_codec_codec_widgets, >> + .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), >> + .dapm_widgets = sun6i_codec_codec_dapm_widgets, >> + .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets), >> + .dapm_routes = sun6i_codec_codec_dapm_routes, >> + .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes), >> + }, >> +}; >> + >> static const struct snd_soc_component_driver sun4i_codec_component = { >> .name = "sun4i-codec", >> }; >> @@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { >> }, >> }; >> >> -static const struct regmap_config sun4i_codec_regmap_config = { >> - .reg_bits = 32, >> - .reg_stride = 4, >> - .val_bits = 32, >> - .max_register = SUN4I_CODEC_ADC_RXCNT, >> -}; >> - >> -static const struct regmap_config sun7i_codec_regmap_config = { >> - .reg_bits = 32, >> - .reg_stride = 4, >> - .val_bits = 32, >> - .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, >> -}; >> - >> -struct sun4i_codec_quirks { >> - const struct regmap_config *regmap_config; >> -}; >> - >> -static const struct sun4i_codec_quirks sun4i_codec_quirks = { >> - .regmap_config = &sun4i_codec_regmap_config, >> -}; >> - >> -static const struct sun4i_codec_quirks sun7i_codec_quirks = { >> - .regmap_config = &sun7i_codec_regmap_config, >> -}; >> - >> -static const struct of_device_id sun4i_codec_of_match[] = { >> - { >> - .compatible = "allwinner,sun4i-a10-codec", >> - .data = &sun4i_codec_quirks, >> - }, >> - { >> - .compatible = "allwinner,sun7i-a20-codec", >> - .data = &sun7i_codec_quirks, >> - }, >> - {} >> -}; >> -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); >> - >> static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, >> int *num_links) >> { >> @@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) >> return card; >> }; >> >> +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev) >> +{ >> + struct snd_soc_card *card; >> + >> + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); >> + if (!card) >> + return NULL; >> + >> + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); >> + if (!card->dai_link) >> + return NULL; >> + >> + card->dev = dev; >> + card->name = "sun4i-codec"; >> + >> + return card; >> +}; >> + >> +static const struct regmap_config sun4i_codec_regmap_config = { >> + .reg_bits = 32, >> + .reg_stride = 4, >> + .val_bits = 32, >> + .max_register = SUN4I_CODEC_ADC_RXCNT, >> +}; >> + >> +static const struct regmap_config sun6i_codec_regmap_config = { >> + .reg_bits = 32, >> + .reg_stride = 4, >> + .val_bits = 32, >> + .max_register = SUN6I_CODEC_HMIC_DATA, >> +}; >> + >> +static const struct regmap_config sun7i_codec_regmap_config = { >> + .reg_bits = 32, >> + .reg_stride = 4, >> + .val_bits = 32, >> + .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, >> +}; >> + >> +static const struct sun4i_codec_regs sun4i_codec_regs = { >> + .adc_fifoc = SUN4I_CODEC_ADC_FIFOC, >> + .adc_fifos = SUN4I_CODEC_ADC_FIFOS, >> + .adc_rxdata = SUN4I_CODEC_ADC_RXDATA, >> +}; >> + >> +static const struct sun4i_codec_regs sun6i_codec_regs = { >> + .adc_fifoc = SUN6I_CODEC_ADC_FIFOC, >> + .adc_fifos = SUN6I_CODEC_ADC_FIFOS, >> + .adc_rxdata = SUN6I_CODEC_ADC_RXDATA, >> +}; >> + >> +struct sun4i_codec_quirks { >> + const struct regmap_config *regmap_config; >> + const struct snd_soc_codec_driver *codec; >> + const struct sun4i_codec_regs *regs; >> + struct snd_soc_card * (*create_card)(struct device *dev); >> +}; >> + >> +static const struct sun4i_codec_quirks sun4i_codec_quirks = { >> + .regmap_config = &sun4i_codec_regmap_config, >> + .regs = &sun4i_codec_regs, >> + .codec = &sun4i_codec_codec, >> + .create_card = sun4i_codec_create_card, >> +}; >> + >> +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { >> + .regmap_config = &sun6i_codec_regmap_config, >> + .regs = &sun6i_codec_regs, >> + .codec = &sun6i_codec_codec, >> + .create_card = sun6i_codec_create_card, >> +}; >> + >> +static const struct sun4i_codec_quirks sun7i_codec_quirks = { >> + .regmap_config = &sun7i_codec_regmap_config, >> + .regs = &sun4i_codec_regs, >> + .codec = &sun4i_codec_codec, >> + .create_card = sun4i_codec_create_card, >> +}; >> + >> +static const struct of_device_id sun4i_codec_of_match[] = { >> + { >> + .compatible = "allwinner,sun4i-a10-codec", >> + .data = &sun4i_codec_quirks, >> + }, >> + { >> + .compatible = "allwinner,sun6i-a31-codec", >> + .data = &sun6i_a31_codec_quirks, >> + }, >> + { >> + .compatible = "allwinner,sun7i-a20-codec", >> + .data = &sun7i_codec_quirks, >> + }, >> + {} >> +}; >> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); >> + > > I don't really like moving blocks of code over and over again, > especially in the middle of an unrelated patch. It's not completely unrelated. I want different create_card functions for the different SoCs, and that has to be part of the quirks, so the quirks and the of_match list have to be moved below them. I suppose I could leave the regmap parts in place, but keeping them together is nicer. If I split out the addition of the .create_card field and code movement into a separate patch, would that be OK? Thanks ChenYu > > Thanks! > Maxime > > -- > Maxime Ripard, Free Electrons > Embedded Linux and Kernel engineering > http://free-electrons.com