[alsa-devel] [RFC PATCH 0/7] AC97 device/driver model revamp
It all started in the pxa device-tree submission here : https://lkml.org/lkml/2016/2/25/965 It will be maintained in : git fetch https://github.com/rjarzmik/linux.git work/ac97
And now it transformed into this RFC, which would bring a ground for AC'97 devices closer to the linux device/driver model.
This is just an RFC to see if we understand each other Mark.
Amongst the driving ideas behind this RFC : - use device/driver model for AC'97 device/driver binding is based on vendor_id{1,2} - use auto-probe of the AC'97 bus to enumerate codecs and create devices - be compatible with previous platform_data model for codecs => this enables a smooth transition, where a codec (here wm9713) can be used either through platform_device or automatic discovery - struct snd_ac97 is not used This structure is really heavy, and doesn't represent an AC97 device, but rather an AC'97 codec internals IIUC. I think it's right place would be in include/sound/ac97/codec.h. This is also a problem for regmap adherence, hence the include/sound/ac97/compat.h. - a new AC'97 bus Kconfig is created This was done for a smooth transition ... let's see if it's a good idea. - split the AC'97 into : - the codec - the digital controller - the bus
Amongst the things that are not all touched yet : - ac97_codec.c The generic AC'97 codec in sound/pci/ac97/ac97_codec.c has really a lot features, which are not at all PCI related. They could be transfered to sound/ac97, without the pci specific field. => this deserves a deep ahead thinking, which I'll do if the current approach is accepted by the communauty.
The most important patch is 2/7 "ALSA: ac97: add an ac97 bus". This is the main evaluation point for the serie, the others are here so that I can test it all.
Well, this is a long term effort, which might need a complete rewrite according to the comments it'll get. Let's expose it for comments and see how I can progress with it.
Cheers.
-- Robert
Robert Jarzmik (7): ALSA: ac97: split out the generic ac97 registers ALSA: ac97: add an ac97 bus ASoC: wm9713: add ac97 new bus support ASoC: pxa: switch to new ac97 bus support ARM: pxa: mioa701 remove wm9713 from platform devices ASoC: mioa701_wm9713: convert to new ac97 bus ASoC: add new ac97 bus support
arch/arm/mach-pxa/mioa701.c | 1 - include/sound/ac97/codec.h | 98 ++++++++++++ include/sound/ac97/compat.h | 21 +++ include/sound/ac97/controller.h | 39 +++++ include/sound/ac97/regs.h | 262 +++++++++++++++++++++++++++++++ include/sound/ac97_codec.h | 239 +---------------------------- include/sound/pxa2xx-lib.h | 14 +- sound/Kconfig | 7 + sound/Makefile | 1 + sound/ac97/Kconfig | 9 ++ sound/ac97/Makefile | 5 + sound/ac97/ac97_core.h | 10 ++ sound/ac97/bus.c | 330 ++++++++++++++++++++++++++++++++++++++++ sound/ac97/codec.c | 15 ++ sound/ac97/snd_ac97_compat.c | 104 +++++++++++++ sound/arm/Kconfig | 1 - sound/arm/pxa2xx-ac97-lib.c | 31 ++-- sound/soc/Kconfig | 4 + sound/soc/codecs/wm9713.c | 62 +++++++- sound/soc/pxa/Kconfig | 4 +- sound/soc/pxa/mioa701_wm9713.c | 4 +- sound/soc/pxa/pxa2xx-ac97.c | 12 +- 22 files changed, 1000 insertions(+), 273 deletions(-) create mode 100644 include/sound/ac97/codec.h create mode 100644 include/sound/ac97/compat.h create mode 100644 include/sound/ac97/controller.h create mode 100644 include/sound/ac97/regs.h create mode 100644 sound/ac97/Kconfig create mode 100644 sound/ac97/Makefile create mode 100644 sound/ac97/ac97_core.h create mode 100644 sound/ac97/bus.c create mode 100644 sound/ac97/codec.c create mode 100644 sound/ac97/snd_ac97_compat.c
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- include/sound/ac97/regs.h | 262 +++++++++++++++++++++++++++++++++++++++++++++ include/sound/ac97_codec.h | 239 +---------------------------------------- 2 files changed, 263 insertions(+), 238 deletions(-) create mode 100644 include/sound/ac97/regs.h
diff --git a/include/sound/ac97/regs.h b/include/sound/ac97/regs.h new file mode 100644 index 000000000000..4bb86d379bd5 --- /dev/null +++ b/include/sound/ac97/regs.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela perex@perex.cz + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ +#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ +#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ +#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ +#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ +#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ +#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ +#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ +#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ +#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ +#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ +#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ +#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ +#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ +/* range 0x60-0x6f (page 1) - extended codec registers */ +#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ +#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ +#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ +#define AC97_FUNC_SELECT 0x66 /* Function Select */ +#define AC97_FUNC_INFO 0x68 /* Function Information */ +#define AC97_SENSE_INFO 0x6a /* Sense Details */ + +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + +/* slot allocation */ +#define AC97_SLOT_TAG 0 +#define AC97_SLOT_CMD_ADDR 1 +#define AC97_SLOT_CMD_DATA 2 +#define AC97_SLOT_PCM_LEFT 3 +#define AC97_SLOT_PCM_RIGHT 4 +#define AC97_SLOT_MODEM_LINE1 5 +#define AC97_SLOT_PCM_CENTER 6 +#define AC97_SLOT_MIC 6 /* input */ +#define AC97_SLOT_SPDIF_LEFT1 6 +#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ +#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT 7 +#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ +#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT 8 +#define AC97_SLOT_LFE 9 +#define AC97_SLOT_SPDIF_RIGHT1 9 +#define AC97_SLOT_MODEM_LINE2 10 +#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT2 10 +#define AC97_SLOT_HANDSET 11 /* output */ +#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT2 11 +#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ +#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ + +/* basic capabilities (reset register) */ +#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ +#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ +#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ +#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ +#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ +#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ +#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ +#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ +#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ +#define AC97_BC_DAC_MASK 0x00c0 +#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ +#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ +#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ +#define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ + +/* general purpose */ +#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ +#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ +#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ + +/* powerdown bits */ +#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ +#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ +#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ +#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ +#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ +#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ +#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ +#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ +#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ +#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ +#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ +#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ + +/* extended audio ID bit defines */ +#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ +#define AC97_EI_DRA 0x0002 /* Double rate supported */ +#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ +#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ +#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ +#define AC97_EI_DACS_SLOT_SHIFT 4 +#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ +#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ +#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ +#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ +#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ +#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ +#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ +#define AC97_EI_REV_SHIFT 10 +#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_EI_ADDR_SHIFT 14 + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_SLOT_SHIFT 4 +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_CC_SHIFT 4 +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_SHIFT 12 +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* Interrupt and Paging bit defines (AC'97 2.3) */ +#define AC97_PAGE_MASK 0x000f /* Page Selector */ +#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ +#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ +#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ +#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ +#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ +#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ +#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ + +/* extended modem ID bit defines */ +#define AC97_MEI_LINE1 0x0001 /* Line1 present */ +#define AC97_MEI_LINE2 0x0002 /* Line2 present */ +#define AC97_MEI_HANDSET 0x0004 /* Handset present */ +#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ +#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ +#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_MEI_ADDR_SHIFT 14 + +/* extended modem status and control bit defines */ +#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ +#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ +#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ +#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ +#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ +#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ +#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ +#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ +#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ +#define AC97_MEA_PRB 0x0200 /* reserved */ +#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ +#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ +#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ +#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ +#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ +#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ + +/* modem gpio status defines */ +#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ +#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ +#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ +#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ +#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ +#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ +#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ +#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ +#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ +#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ +#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ +#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ +#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ +#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ +#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ +#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ + diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 15aa5f07c955..89d311a503d3 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -28,6 +28,7 @@ #include <linux/bitops.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <sound/ac97/regs.h> #include <sound/pcm.h> #include <sound/control.h> #include <sound/info.h> @@ -35,244 +36,6 @@ /* maximum number of devices on the AC97 bus */ #define AC97_BUS_MAX_DEVICES 4
-/* - * AC'97 codec registers - */ - -#define AC97_RESET 0x00 /* Reset */ -#define AC97_MASTER 0x02 /* Master Volume */ -#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ -#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ -#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ -#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ -#define AC97_PHONE 0x0c /* Phone Volume (optional) */ -#define AC97_MIC 0x0e /* MIC Volume */ -#define AC97_LINE 0x10 /* Line In Volume */ -#define AC97_CD 0x12 /* CD Volume */ -#define AC97_VIDEO 0x14 /* Video Volume (optional) */ -#define AC97_AUX 0x16 /* AUX Volume (optional) */ -#define AC97_PCM 0x18 /* PCM Volume */ -#define AC97_REC_SEL 0x1a /* Record Select */ -#define AC97_REC_GAIN 0x1c /* Record Gain */ -#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ -#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ -#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ -#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ -#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ -/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ -#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ -#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ -#define AC97_SPDIF 0x3a /* S/PDIF control */ -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ -#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ -#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ -#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ -#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ -#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ -#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ -#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ -#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ -#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ -#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ -#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ -#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ -#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ -/* range 0x5a-0x7b - Vendor Specific */ -#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ -#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ -/* range 0x60-0x6f (page 1) - extended codec registers */ -#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ -#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ -#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ -#define AC97_FUNC_SELECT 0x66 /* Function Select */ -#define AC97_FUNC_INFO 0x68 /* Function Information */ -#define AC97_SENSE_INFO 0x6a /* Sense Details */ - -/* volume controls */ -#define AC97_MUTE_MASK_MONO 0x8000 -#define AC97_MUTE_MASK_STEREO 0x8080 - -/* slot allocation */ -#define AC97_SLOT_TAG 0 -#define AC97_SLOT_CMD_ADDR 1 -#define AC97_SLOT_CMD_DATA 2 -#define AC97_SLOT_PCM_LEFT 3 -#define AC97_SLOT_PCM_RIGHT 4 -#define AC97_SLOT_MODEM_LINE1 5 -#define AC97_SLOT_PCM_CENTER 6 -#define AC97_SLOT_MIC 6 /* input */ -#define AC97_SLOT_SPDIF_LEFT1 6 -#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ -#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT 7 -#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ -#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT 8 -#define AC97_SLOT_LFE 9 -#define AC97_SLOT_SPDIF_RIGHT1 9 -#define AC97_SLOT_MODEM_LINE2 10 -#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT2 10 -#define AC97_SLOT_HANDSET 11 /* output */ -#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT2 11 -#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ -#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ - -/* basic capabilities (reset register) */ -#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ -#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ -#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ -#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ -#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ -#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ -#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ -#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ -#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ -#define AC97_BC_DAC_MASK 0x00c0 -#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ -#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ -#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ -#define AC97_BC_ADC_MASK 0x0300 -#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ - -/* general purpose */ -#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ -#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ -#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ - -/* powerdown bits */ -#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ -#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ -#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ -#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ -#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ -#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ -#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ -#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ -#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ -#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ -#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ -#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ - -/* extended audio ID bit defines */ -#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ -#define AC97_EI_DRA 0x0002 /* Double rate supported */ -#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ -#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ -#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ -#define AC97_EI_DACS_SLOT_SHIFT 4 -#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ -#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ -#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ -#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ -#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ -#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ -#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ -#define AC97_EI_REV_SHIFT 10 -#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_EI_ADDR_SHIFT 14 - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_SLOT_SHIFT 4 -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_CC_SHIFT 4 -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_SHIFT 12 -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* Interrupt and Paging bit defines (AC'97 2.3) */ -#define AC97_PAGE_MASK 0x000f /* Page Selector */ -#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ -#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ -#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ -#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ -#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ -#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ -#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ - -/* extended modem ID bit defines */ -#define AC97_MEI_LINE1 0x0001 /* Line1 present */ -#define AC97_MEI_LINE2 0x0002 /* Line2 present */ -#define AC97_MEI_HANDSET 0x0004 /* Handset present */ -#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ -#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ -#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_MEI_ADDR_SHIFT 14 - -/* extended modem status and control bit defines */ -#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ -#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ -#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ -#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ -#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ -#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ -#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ -#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ -#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ -#define AC97_MEA_PRB 0x0200 /* reserved */ -#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ -#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ -#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ -#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ -#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ -#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ - -/* modem gpio status defines */ -#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ -#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ -#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ -#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ -#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ -#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ -#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ -#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ -#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ -#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ -#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ -#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ -#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ -#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ -#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ -#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ - /* specific - SigmaTel */ #define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */ #define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */
On Sat, Apr 30, 2016 at 11:15:33PM +0200, Robert Jarzmik wrote:
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
I don't entirely see the value in this one but equally it does no harm.
Mark Brown broonie@kernel.org writes:
On Sat, Apr 30, 2016 at 11:15:33PM +0200, Robert Jarzmik wrote:
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
I don't entirely see the value in this one but equally it does no harm.
Oh that's to have the defines AC97_VENDOR_ID1 and AC97_VENDOR_ID2, but without including ac97_codec.h in sound/ac97/bus.c.
Cheers.
On Tue, May 03, 2016 at 09:22:05PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
On Sat, Apr 30, 2016 at 11:15:33PM +0200, Robert Jarzmik wrote:
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
I don't entirely see the value in this one but equally it does no harm.
Oh that's to have the defines AC97_VENDOR_ID1 and AC97_VENDOR_ID2, but without including ac97_codec.h in sound/ac97/bus.c.
I see that, I just don't know why that helps.
Mark Brown broonie@kernel.org writes:
On Tue, May 03, 2016 at 09:22:05PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
On Sat, Apr 30, 2016 at 11:15:33PM +0200, Robert Jarzmik wrote:
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
I don't entirely see the value in this one but equally it does no harm.
Oh that's to have the defines AC97_VENDOR_ID1 and AC97_VENDOR_ID2, but without including ac97_codec.h in sound/ac97/bus.c.
I see that, I just don't know why that helps.
Ok, so let me explain.
As you already understood, AC97_VENDOR* is needed in bus.c for the autoscanning. The remaining is the reason why I don't want to include ac97_codec.h in bus.c, which follows.
In order to have a clean split between former ac97 bus implementation and this new one, I didn't want to include any former ac97 includes, excepting in sound/ac97/compat.c.
Amongst the thing I wanted to isolate : - didn't want the struct snd_ac97 in sound/ac97/* (except compat.c) to see not be "fooled" by a definition which would come with ac97_codec.h by "chance". - didn't want to have snd_a97_*() functions, as they rely on struct snd_ac97. - didn't want the struct snd_ac97_bus_ops, there is a new one => the main grudge I have is the void (*write)() and unsigned short (*read)() prototype which prevent from returning error codes such as -EIO, etc ...
Moreover, to have a smooth transition, my plan is : - have this new ac97 bus - provide any needed function in compat.c so that previous codecs, regmap, ... continue to work - port the ac97 "pci" codec to have a generic ac97 codec so that codecs can be shrinked from common ac97 code. - port the codecs ... several monthes of work, etc ... - once the port is done, remove compat.c
This transition will be easier if the new ac97 code is isolated as much as possible from the former code while providing backward compatibility ... hence this patch.
Cheers.
On Thu, May 05, 2016 at 09:06:44PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
I see that, I just don't know why that helps.
Ok, so let me explain.
As you already understood, AC97_VENDOR* is needed in bus.c for the autoscanning. The remaining is the reason why I don't want to include ac97_codec.h in bus.c, which follows.
In order to have a clean split between former ac97 bus implementation and this new one, I didn't want to include any former ac97 includes, excepting in sound/ac97/compat.c.
It's probably a good idea to articulate some of this in the commit log then!
This transition will be easier if the new ac97 code is isolated as much as possible from the former code while providing backward compatibility ... hence this patch.
It'd probably have been clearer to me if it were splitting out all the register definitions rather than just some of them.
Mark Brown broonie@kernel.org writes:
On Thu, May 05, 2016 at 09:06:44PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
I see that, I just don't know why that helps.
In order to have a clean split between former ac97 bus implementation and this new one, I didn't want to include any former ac97 includes, excepting in sound/ac97/compat.c.
It's probably a good idea to articulate some of this in the commit log then!
Fair point, I'll improve that.
This transition will be easier if the new ac97 code is isolated as much as possible from the former code while providing backward compatibility ... hence this patch.
It'd probably have been clearer to me if it were splitting out all the register definitions rather than just some of them.
Euh unless I'm missing something I have splitted out all the ac97 generic registers AFAIK. The only remaining ones are chip specific which I didn't take as they should be in codec specific code and not ac97 generic one.
Cheers.
On Thu, May 05, 2016 at 09:46:10PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
On Thu, May 05, 2016 at 09:06:44PM +0200, Robert Jarzmik wrote:
Mark Brown broonie@kernel.org writes:
This transition will be easier if the new ac97 code is isolated as much as possible from the former code while providing backward compatibility ... hence this patch.
It'd probably have been clearer to me if it were splitting out all the register definitions rather than just some of them.
Euh unless I'm missing something I have splitted out all the ac97 generic registers AFAIK. The only remaining ones are chip specific which I didn't take as they should be in codec specific code and not ac97 generic one.
The changelog read like it was trying to split things used by the controller vs things used by CODEC drivers, I didn't get as far as checking all the registers TBH.
The patch
ALSA: ac97: split out the generic ac97 registers
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 8e4f7d9b8c652b4eb3ceb790e1e9fe433f9e3b58 Mon Sep 17 00:00:00 2001
From: Robert Jarzmik robert.jarzmik@free.fr Date: Sat, 2 Sep 2017 21:54:03 +0200 Subject: [PATCH] ALSA: ac97: split out the generic ac97 registers
Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link.
This split encompasses all the AC97 standard registers, but not the codec specific ones.
In order to have a clean split between former ac97 bus implementation and the new coming one in sound/ac97, it is safer to not include any former ac97 includes, excepting in sound/ac97/compat.c.
Amongst the thing to isolate : - don't have the struct snd_ac97 in sound/ac97/* (except compat.c) to not be "fooled" by a definition which would come with ac97_codec.h by "chance". - don't have to have snd_a97_*() functions, as they rely on struct snd_ac97. - don't want the struct snd_ac97_bus_ops, there is a new one
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Reviewed-by: Takashi Iwai tiwai@suse.de Signed-off-by: Mark Brown broonie@kernel.org --- include/sound/ac97/regs.h | 262 +++++++++++++++++++++++++++++++++++++++++++++ include/sound/ac97_codec.h | 239 +---------------------------------------- 2 files changed, 263 insertions(+), 238 deletions(-) create mode 100644 include/sound/ac97/regs.h
diff --git a/include/sound/ac97/regs.h b/include/sound/ac97/regs.h new file mode 100644 index 000000000000..4bb86d379bd5 --- /dev/null +++ b/include/sound/ac97/regs.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela perex@perex.cz + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ +#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ +#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ +#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ +#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ +#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ +#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ +#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ +#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ +#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ +#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ +#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ +#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ +#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ +/* range 0x60-0x6f (page 1) - extended codec registers */ +#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ +#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ +#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ +#define AC97_FUNC_SELECT 0x66 /* Function Select */ +#define AC97_FUNC_INFO 0x68 /* Function Information */ +#define AC97_SENSE_INFO 0x6a /* Sense Details */ + +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + +/* slot allocation */ +#define AC97_SLOT_TAG 0 +#define AC97_SLOT_CMD_ADDR 1 +#define AC97_SLOT_CMD_DATA 2 +#define AC97_SLOT_PCM_LEFT 3 +#define AC97_SLOT_PCM_RIGHT 4 +#define AC97_SLOT_MODEM_LINE1 5 +#define AC97_SLOT_PCM_CENTER 6 +#define AC97_SLOT_MIC 6 /* input */ +#define AC97_SLOT_SPDIF_LEFT1 6 +#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ +#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT 7 +#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ +#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT 8 +#define AC97_SLOT_LFE 9 +#define AC97_SLOT_SPDIF_RIGHT1 9 +#define AC97_SLOT_MODEM_LINE2 10 +#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT2 10 +#define AC97_SLOT_HANDSET 11 /* output */ +#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT2 11 +#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ +#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ + +/* basic capabilities (reset register) */ +#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ +#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ +#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ +#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ +#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ +#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ +#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ +#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ +#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ +#define AC97_BC_DAC_MASK 0x00c0 +#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ +#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ +#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ +#define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ + +/* general purpose */ +#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ +#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ +#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ + +/* powerdown bits */ +#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ +#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ +#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ +#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ +#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ +#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ +#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ +#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ +#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ +#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ +#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ +#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ + +/* extended audio ID bit defines */ +#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ +#define AC97_EI_DRA 0x0002 /* Double rate supported */ +#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ +#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ +#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ +#define AC97_EI_DACS_SLOT_SHIFT 4 +#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ +#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ +#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ +#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ +#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ +#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ +#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ +#define AC97_EI_REV_SHIFT 10 +#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_EI_ADDR_SHIFT 14 + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_SLOT_SHIFT 4 +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_CC_SHIFT 4 +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_SHIFT 12 +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* Interrupt and Paging bit defines (AC'97 2.3) */ +#define AC97_PAGE_MASK 0x000f /* Page Selector */ +#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ +#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ +#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ +#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ +#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ +#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ +#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ + +/* extended modem ID bit defines */ +#define AC97_MEI_LINE1 0x0001 /* Line1 present */ +#define AC97_MEI_LINE2 0x0002 /* Line2 present */ +#define AC97_MEI_HANDSET 0x0004 /* Handset present */ +#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ +#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ +#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_MEI_ADDR_SHIFT 14 + +/* extended modem status and control bit defines */ +#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ +#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ +#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ +#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ +#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ +#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ +#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ +#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ +#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ +#define AC97_MEA_PRB 0x0200 /* reserved */ +#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ +#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ +#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ +#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ +#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ +#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ + +/* modem gpio status defines */ +#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ +#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ +#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ +#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ +#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ +#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ +#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ +#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ +#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ +#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ +#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ +#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ +#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ +#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ +#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ +#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ + diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 15aa5f07c955..89d311a503d3 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -28,6 +28,7 @@ #include <linux/bitops.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <sound/ac97/regs.h> #include <sound/pcm.h> #include <sound/control.h> #include <sound/info.h> @@ -35,244 +36,6 @@ /* maximum number of devices on the AC97 bus */ #define AC97_BUS_MAX_DEVICES 4
-/* - * AC'97 codec registers - */ - -#define AC97_RESET 0x00 /* Reset */ -#define AC97_MASTER 0x02 /* Master Volume */ -#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ -#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ -#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ -#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ -#define AC97_PHONE 0x0c /* Phone Volume (optional) */ -#define AC97_MIC 0x0e /* MIC Volume */ -#define AC97_LINE 0x10 /* Line In Volume */ -#define AC97_CD 0x12 /* CD Volume */ -#define AC97_VIDEO 0x14 /* Video Volume (optional) */ -#define AC97_AUX 0x16 /* AUX Volume (optional) */ -#define AC97_PCM 0x18 /* PCM Volume */ -#define AC97_REC_SEL 0x1a /* Record Select */ -#define AC97_REC_GAIN 0x1c /* Record Gain */ -#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ -#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ -#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ -#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ -#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ -/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ -#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ -#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ -#define AC97_SPDIF 0x3a /* S/PDIF control */ -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ -#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ -#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ -#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ -#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ -#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ -#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ -#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ -#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ -#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ -#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ -#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ -#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ -#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ -/* range 0x5a-0x7b - Vendor Specific */ -#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ -#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ -/* range 0x60-0x6f (page 1) - extended codec registers */ -#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ -#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ -#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ -#define AC97_FUNC_SELECT 0x66 /* Function Select */ -#define AC97_FUNC_INFO 0x68 /* Function Information */ -#define AC97_SENSE_INFO 0x6a /* Sense Details */ - -/* volume controls */ -#define AC97_MUTE_MASK_MONO 0x8000 -#define AC97_MUTE_MASK_STEREO 0x8080 - -/* slot allocation */ -#define AC97_SLOT_TAG 0 -#define AC97_SLOT_CMD_ADDR 1 -#define AC97_SLOT_CMD_DATA 2 -#define AC97_SLOT_PCM_LEFT 3 -#define AC97_SLOT_PCM_RIGHT 4 -#define AC97_SLOT_MODEM_LINE1 5 -#define AC97_SLOT_PCM_CENTER 6 -#define AC97_SLOT_MIC 6 /* input */ -#define AC97_SLOT_SPDIF_LEFT1 6 -#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ -#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT 7 -#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ -#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT 8 -#define AC97_SLOT_LFE 9 -#define AC97_SLOT_SPDIF_RIGHT1 9 -#define AC97_SLOT_MODEM_LINE2 10 -#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT2 10 -#define AC97_SLOT_HANDSET 11 /* output */ -#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT2 11 -#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ -#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ - -/* basic capabilities (reset register) */ -#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ -#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ -#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ -#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ -#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ -#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ -#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ -#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ -#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ -#define AC97_BC_DAC_MASK 0x00c0 -#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ -#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ -#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ -#define AC97_BC_ADC_MASK 0x0300 -#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ - -/* general purpose */ -#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ -#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ -#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ - -/* powerdown bits */ -#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ -#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ -#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ -#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ -#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ -#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ -#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ -#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ -#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ -#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ -#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ -#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ - -/* extended audio ID bit defines */ -#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ -#define AC97_EI_DRA 0x0002 /* Double rate supported */ -#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ -#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ -#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ -#define AC97_EI_DACS_SLOT_SHIFT 4 -#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ -#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ -#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ -#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ -#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ -#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ -#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ -#define AC97_EI_REV_SHIFT 10 -#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_EI_ADDR_SHIFT 14 - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_SLOT_SHIFT 4 -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_CC_SHIFT 4 -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_SHIFT 12 -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* Interrupt and Paging bit defines (AC'97 2.3) */ -#define AC97_PAGE_MASK 0x000f /* Page Selector */ -#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ -#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ -#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ -#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ -#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ -#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ -#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ - -/* extended modem ID bit defines */ -#define AC97_MEI_LINE1 0x0001 /* Line1 present */ -#define AC97_MEI_LINE2 0x0002 /* Line2 present */ -#define AC97_MEI_HANDSET 0x0004 /* Handset present */ -#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ -#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ -#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_MEI_ADDR_SHIFT 14 - -/* extended modem status and control bit defines */ -#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ -#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ -#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ -#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ -#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ -#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ -#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ -#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ -#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ -#define AC97_MEA_PRB 0x0200 /* reserved */ -#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ -#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ -#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ -#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ -#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ -#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ - -/* modem gpio status defines */ -#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ -#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ -#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ -#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ -#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ -#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ -#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ -#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ -#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ -#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ -#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ -#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ -#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ -#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ -#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ -#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ - /* specific - SigmaTel */ #define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */ #define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */
AC97 is a bus for sound usage. It enables for a AC97 AC-Link to link one controller to 0 to 4 AC97 codecs.
The goal of this new implementation is to implement a device/driver model for AC97, with an automatic scan of the bus and automatic discovery of AC97 codec devices.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- include/sound/ac97/codec.h | 98 ++++++++++++ include/sound/ac97/compat.h | 21 +++ include/sound/ac97/controller.h | 39 +++++ sound/ac97/Kconfig | 9 ++ sound/ac97/Makefile | 5 + sound/ac97/ac97_core.h | 10 ++ sound/ac97/bus.c | 330 ++++++++++++++++++++++++++++++++++++++++ sound/ac97/codec.c | 15 ++ sound/ac97/snd_ac97_compat.c | 104 +++++++++++++ 9 files changed, 631 insertions(+) create mode 100644 include/sound/ac97/codec.h create mode 100644 include/sound/ac97/compat.h create mode 100644 include/sound/ac97/controller.h create mode 100644 sound/ac97/Kconfig create mode 100644 sound/ac97/Makefile create mode 100644 sound/ac97/ac97_core.h create mode 100644 sound/ac97/bus.c create mode 100644 sound/ac97/codec.c create mode 100644 sound/ac97/snd_ac97_compat.c
diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h new file mode 100644 index 000000000000..4b8b3e570892 --- /dev/null +++ b/include/sound/ac97/codec.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ +#ifndef AC97_CODEC_H +#define AC97_CODEC_H + +#include <linux/device.h> + +#define AC97_ID(vendor_id1, vendor_id2) \ + (((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff)) +#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \ + { .id = ((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff), \ + .mask = ((mask_id1 & 0xffff) << 16) | (mask_id2 & 0xffff), \ + .data = _data } + +#define to_ac97_device(d) container_of(d, struct ac97_codec_device, dev) +#define to_ac97_driver(d) container_of(d, struct ac97_codec_driver, driver) + +struct ac97_controller; + +/** + * struct ac97_id - matches a codec device and driver on an ac97 bus + * @id: The significant bits if the codec vendor ID1 and ID2 + * @mask: Bitmask specifying which bits of the id field are significant when + * matching. A driver binds to a device when : + * ((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id. + * @data: Private data used by the driver. + */ +struct ac97_id { + unsigned int id; + unsigned int mask; + void *data; +}; + +/** + * ac97_codec_device - a ac97 codec + * @dev: the code device + * @vendor_id: the vendor_id of the codec, as sensed on the AC-link + * @num: the codec number, 0 is primary, 1 is first slave, etc ... + * @ac97_ctrl: ac97 digital controller on the same AC-link + * + * This is the device instanciated for each codec living on a AC-link. There are + * normally 0 to 4 codec devices per AC-link, and all of them are controlled by + * an AC97 digital controller. + */ +struct ac97_codec_device { + struct device dev; /* Must stay first member */ + unsigned int vendor_id; + unsigned int num; + struct list_head list; + struct ac97_controller *ac97_ctrl; +}; + +/** + * ac97_codec_driver - a ac97 codec driver + * @driver: the device driver structure + * @probe: the function called when a ac97_codec_device is matched + * @remove: the function called when the device is unbound/removed + * @suspend: suspend function (might be NULL) + * @resume: resume function (might be NULL) + * @shutdown: shutdown function (might be NULL) + * @id_table: ac97 vendor_id match table, { } member terminated + */ +struct ac97_codec_driver { + struct device_driver driver; + int (*probe)(struct ac97_codec_device *); + int (*remove)(struct ac97_codec_device *); + int (*suspend)(struct ac97_codec_device *); + int (*resume)(struct ac97_codec_device *); + void (*shutdown)(struct ac97_codec_device *); + struct ac97_id *id_table; +}; + +int ac97_codec_driver_register(struct ac97_codec_driver *drv); +void ac97_codec_driver_unregister(struct ac97_codec_driver *drv); + +static inline struct device * +ac97_codec_dev2dev(const struct ac97_codec_device *adev) +{ + return (struct device *)(adev); +} + +static inline void *ac97_get_drvdata(const struct ac97_codec_device *adev) +{ + return dev_get_drvdata(ac97_codec_dev2dev(adev)); +} + +static inline void ac97_set_drvdata(const struct ac97_codec_device *adev, + void *data) +{ + dev_set_drvdata(ac97_codec_dev2dev(adev), data); +} + +#endif diff --git a/include/sound/ac97/compat.h b/include/sound/ac97/compat.h new file mode 100644 index 000000000000..bf611f572f2d --- /dev/null +++ b/include/sound/ac97/compat.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file is for backward compatibility with snd_ac97 structure and its + * multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops. + * + */ +#ifndef AC97_COMPAT_H +#define AC97_COMPAT_H + +#include <sound/ac97_codec.h> +#include <sound/soc.h> + +struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec); +void compat_release_snd_ac97_codec(struct snd_ac97 *ac97); + +#endif diff --git a/include/sound/ac97/controller.h b/include/sound/ac97/controller.h new file mode 100644 index 000000000000..f1e5e645f5ef --- /dev/null +++ b/include/sound/ac97/controller.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ +#ifndef AC97_CONTROLLER_H +#define AC97_CONTROLLER_H + +#include <linux/list.h> + +struct device; +struct ac97_codec_device; + +struct ac97_controller_ops { + void (*reset)(struct ac97_codec_device *ac97); + void (*warm_reset)(struct ac97_codec_device *ac97); + int (*write)(struct ac97_codec_device *ac97, unsigned short reg, + unsigned short val); + int (*read)(struct ac97_codec_device *ac97, unsigned short reg); + void (*wait)(struct ac97_codec_device *ac97); + void (*init)(struct ac97_codec_device *ac97); +}; + +struct ac97_controller { + const struct ac97_controller_ops *ops; + struct list_head controllers; + struct device *dev; + int bus_idx; + int bound_codecs; + struct list_head codecs; +}; + +int ac97_digital_controller_register(const struct ac97_controller_ops *ops, + struct device *dev); +int ac97_digital_controller_unregister(const struct device *dev); + +#endif diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig new file mode 100644 index 000000000000..fd2c2d031e62 --- /dev/null +++ b/sound/ac97/Kconfig @@ -0,0 +1,9 @@ +# +# PCI configuration +# + +config AC97 + bool "AC97 bus" + help + Say Y here if you want to have AC97 devices, which are sound oriented + devices around an AC-Link. diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile new file mode 100644 index 000000000000..5575909d46e2 --- /dev/null +++ b/sound/ac97/Makefile @@ -0,0 +1,5 @@ +# +# make for AC97 bus drivers +# + +obj-y += bus.o codec.o snd_ac97_compat.o diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h new file mode 100644 index 000000000000..db6e27288357 --- /dev/null +++ b/sound/ac97/ac97_core.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ + +unsigned int ac97_bus_scan_one(struct ac97_controller *ac97, + int codec_num); diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c new file mode 100644 index 000000000000..f9bf5632d4aa --- /dev/null +++ b/sound/ac97/bus.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <sound/ac97/regs.h> + +#define AC97_BUS_MAX_CODECS 4 + +/* + * Protects ac97_controllers and each ac97_controller structure. + */ +static DEFINE_MUTEX(ac97_controllers_mutex); +static LIST_HEAD(ac97_controllers); +static int ac97_bus_idx; + +struct bus_type ac97_bus_type; + +static struct ac97_codec_device * +ac97_codec_find(struct ac97_controller *ac97_ctrl, int codec_num) +{ + struct ac97_codec_device *codec; + + list_for_each_entry(codec, &ac97_ctrl->codecs, list) + if (codec->num == codec_num) + return codec; + + return NULL; +} + +static void ac97_codec_release(struct device *dev) +{ + struct ac97_codec_device *codec; + + codec = container_of(dev, struct ac97_codec_device, dev); + list_del(&codec->list); + kfree(codec); +} + +static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx, + unsigned int vendor_id) +{ + struct ac97_codec_device *codec; + char *codec_name; + int ret; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + codec->vendor_id = vendor_id; + codec->dev.release = ac97_codec_release; + codec->dev.bus = &ac97_bus_type; + codec->dev.parent = ac97_ctrl->dev; + codec->num = idx; + codec->ac97_ctrl = ac97_ctrl; + INIT_LIST_HEAD(&codec->list); + list_move_tail(&codec->list, &ac97_ctrl->codecs); + + codec_name = kasprintf(GFP_KERNEL, "%s:%d", dev_name(ac97_ctrl->dev), + idx); + codec->dev.init_name = codec_name; + + ret = device_register(&codec->dev); + kfree(codec_name); + + return ret; +} + +unsigned int ac97_bus_scan_one(struct ac97_controller *ac97, + int codec_num) +{ + struct ac97_codec_device codec; + unsigned short vid1, vid2; + int ret; + + codec.dev = *ac97->dev; + codec.num = codec_num; + ret = ac97->ops->read(&codec, AC97_VENDOR_ID1); + vid1 = (ret & 0xffff); + if (ret < 0) + return 0; + + ret = ac97->ops->read(&codec, AC97_VENDOR_ID2); + vid2 = (ret & 0xffff); + if (ret < 0) + return 0; + + dev_dbg(&codec.dev, "%s(codec_num=%d): vendor_id=0x%08x\n", + __func__, codec_num, AC97_ID(vid1, vid2)); + return AC97_ID(vid1, vid2); +} + +static int ac97_bus_scan(struct ac97_controller *ac97_ctrl) +{ + int ret, i; + unsigned int vendor_id; + + for (i = 0; i < AC97_BUS_MAX_CODECS; i++) { + if (ac97_codec_find(ac97_ctrl, i)) + continue; + vendor_id = ac97_bus_scan_one(ac97_ctrl, i); + if (!vendor_id) + continue; + + ret = ac97_codec_add(ac97_ctrl, i, vendor_id); + if (ret < 0) + return ret; + } + return 0; +} + +void ac97_rescan_all_controllers(void) +{ + struct ac97_controller *ac97_ctrl; + int ret; + + mutex_lock(&ac97_controllers_mutex); + list_for_each_entry(ac97_ctrl, &ac97_controllers, controllers) { + ret = ac97_bus_scan(ac97_ctrl); + if (ret) + dev_warn(ac97_ctrl->dev, "scan failed: %d\n", ret); + } + mutex_unlock(&ac97_controllers_mutex); +} +EXPORT_SYMBOL(ac97_rescan_all_controllers); + +static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) +{ + struct ac97_codec_device codec; + + memset(&codec, 0, sizeof(codec)); + codec.dev = *ac97_ctrl->dev; + + ac97_ctrl->ops->reset(&codec); + return 0; +} + +/** + * ac97_codec_driver_register - register an AC97 codec driver + * @dev: AC97 driver codec to register + * + * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital + * controller. + * + * Returns 0 on success or error code + */ +int ac97_codec_driver_register(struct ac97_codec_driver *drv) +{ + int ret; + + drv->driver.bus = &ac97_bus_type; + + ret = driver_register(&drv->driver); + if (!ret) + ac97_rescan_all_controllers(); + + return ret; +} +EXPORT_SYMBOL(ac97_codec_driver_register); + +/** + * ac97_codec_driver_unregister - unregister an AC97 codec driver + * @dev: AC97 codec driver to unregister + * + * Unregister a previously registered ac97 codec driver. + */ +void ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(ac97_codec_driver_unregister); + +static int ac97_dc_codecs_unregister(struct ac97_controller *ac97_ctrl) +{ + struct ac97_codec_device *codec, *tmp; + + list_for_each_entry_safe(codec, tmp, &ac97_ctrl->codecs, list) + put_device(&codec->dev); + + return 0; +} + +/** + * ac97_digital_controller_register - register an ac97 controller + * @ops: the ac97 bus operations + * @dev: the device providing the ac97 DC function + * + * Register a digital controller which can control up to 4 ac97 codecs. This is + * the controller side of the AC97 AC-link, while the slave side are the codecs. + * + * Returns a positive bus index upon success, negative value upon error + */ +int ac97_digital_controller_register(const struct ac97_controller_ops *ops, + struct device *dev) +{ + struct ac97_controller *ac97_ctrl; + + ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL); + if (!ac97_ctrl) + return -ENOMEM; + + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl->ops = ops; + ac97_ctrl->bus_idx = ac97_bus_idx++; + ac97_ctrl->dev = dev; + INIT_LIST_HEAD(&ac97_ctrl->codecs); + list_add(&ac97_ctrl->controllers, &ac97_controllers); + mutex_unlock(&ac97_controllers_mutex); + + ac97_bus_reset(ac97_ctrl); + ac97_bus_scan(ac97_ctrl); + + return ac97_ctrl->bus_idx; +} +EXPORT_SYMBOL(ac97_digital_controller_register); + +/** + * ac97_digital_controller_unregister - unregister an ac97 controller + * @dev: the device previously provided to ac97_digital_controller_register() + * + * Returns 0 on success, negative upon error + */ +int ac97_digital_controller_unregister(const struct device *dev) +{ + struct ac97_controller *ac97_ctrl, *tmp; + int ret = -ENODEV; + + mutex_lock(&ac97_controllers_mutex); + list_for_each_entry_safe(ac97_ctrl, tmp, &ac97_controllers, + controllers) { + if (ac97_ctrl->dev != dev) + continue; + if (ac97_ctrl->bound_codecs) + ret = -EBUSY; + else + ret = ac97_dc_codecs_unregister(ac97_ctrl); + if (!ret) + list_del(&ac97_ctrl->controllers); + } + + mutex_unlock(&ac97_controllers_mutex); + return ret; +} +EXPORT_SYMBOL(ac97_digital_controller_unregister); + +static const struct dev_pm_ops ac97_pm = { + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, +}; + +static ssize_t vendor_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + + return sprintf(buf, "%08x", codec->vendor_id); +} + +static struct device_attribute ac97_dev_attrs[] = { + __ATTR_RO(vendor_id), + __ATTR_NULL, +}; + +int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(drv); + struct ac97_id *id = adrv->id_table; + + if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff) + return false; + + do { + if ((id->id & id->mask) == (adev->vendor_id & id->mask)) + return true; + } while (++id->id); + + return false; +} + +static int ac97_bus_probe(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + + return adrv->probe(adev); +} + +static int ac97_bus_remove(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + + return adrv->remove(adev); +} + +struct bus_type ac97_bus_type = { + .name = "ac97", + .dev_attrs = ac97_dev_attrs, + .match = ac97_bus_match, + .pm = &ac97_pm, + .probe = ac97_bus_probe, + .remove = ac97_bus_remove, +}; +EXPORT_SYMBOL(ac97_bus_type); + +static int __init ac97_bus_init(void) +{ + return bus_register(&ac97_bus_type); +} +subsys_initcall(ac97_bus_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Jarzmik robert.jarzmik@free.fr"); diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c new file mode 100644 index 000000000000..a835f03744bf --- /dev/null +++ b/sound/ac97/codec.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ + +#include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/soc.h> /* For compat_ac97_* */ + diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c new file mode 100644 index 000000000000..7e2f01c96fc9 --- /dev/null +++ b/sound/ac97/snd_ac97_compat.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr + * + * 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. + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <sound/soc.h> + +#include "ac97_core.h" + +static void compat_ac97_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->reset) + actrl->ops->reset(adev); +} + +static void compat_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->warm_reset) + actrl->ops->warm_reset(adev); +} + +static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + actrl->ops->write(adev, reg, val); +} + +static unsigned short compat_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + return actrl->ops->read(adev, reg); +} + +static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = { + .reset = compat_ac97_reset, + .warm_reset = compat_ac97_warm_reset, + .write = compat_ac97_write, + .read = compat_ac97_read, +}; + +static struct snd_ac97_bus compat_soc_ac97_bus = { + .ops = &compat_snd_ac97_bus_ops, +}; + +struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec) +{ + struct snd_ac97 *ac97; + + ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (ac97 == NULL) + return ERR_PTR(-ENOMEM); + + ac97->dev = *codec->dev; + ac97->private_data = codec->dev; + ac97->bus = &compat_soc_ac97_bus; + return ac97; +} +EXPORT_SYMBOL_GPL(compat_alloc_snd_ac97_codec); + +void compat_release_snd_ac97_codec(struct snd_ac97 *ac97) +{ + kfree(ac97); +} +EXPORT_SYMBOL_GPL(compat_release_snd_ac97_codec); + +int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, + unsigned int id_mask) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (try_warm) { + compat_ac97_warm_reset(ac97); + if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id) + return 1; + } + + compat_ac97_reset(ac97); + compat_ac97_warm_reset(ac97); + if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id) + return 0; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(snd_ac97_reset);
On Sat, Apr 30, 2016 at 11:15:34PM +0200, Robert Jarzmik wrote:
AC97 is a bus for sound usage. It enables for a AC97 AC-Link to link one controller to 0 to 4 AC97 codecs.
The goal of this new implementation is to implement a device/driver model for AC97, with an automatic scan of the bus and automatic discovery of AC97 codec devices.
I think this is basically what I was thinking of, yes. One thing we'll need to do is work out how to handle systems that need some explicit action to start clocks for the bus clock, though they were never very common and it's entirely possible nobody cares any more so perhaps we just punt and see if anyone notices for now.
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
Why "digital"?
Mark Brown broonie@kernel.org writes:
On Sat, Apr 30, 2016 at 11:15:34PM +0200, Robert Jarzmik wrote:
AC97 is a bus for sound usage. It enables for a AC97 AC-Link to link one controller to 0 to 4 AC97 codecs.
The goal of this new implementation is to implement a device/driver model for AC97, with an automatic scan of the bus and automatic discovery of AC97 codec devices.
I think this is basically what I was thinking of, yes. One thing we'll need to do is work out how to handle systems that need some explicit action to start clocks for the bus clock, though they were never very common and it's entirely possible nobody cares any more so perhaps we just punt and see if anyone notices for now.
You probably mean the BITCLK clock.
What is a bit pesky about this clock is that it can either be mastered by digital controller and the codec is a slave, or the other way around.
So we had either the BITCLK provided by : - the controller => this could be passed in ac97_digital_controller_register() - the codec => this is trouble, I don't really know how to handle this case
If the bus code has this clock, it can indeed prepare and enable it.
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
Why "digital"?
I copy-pasted this from Audio Codec '97 Revision 2.3, where in several places they call the controller a "digital controller".
Quoting chapter 1.4: The digital link that connects the AC ‘97 Digital Controller to the AC ‘97 Codec, referred to as AC-link, is a bi- directional, 5-wire, serial time domain multiplexed (TDM) format interface. AC-link supports connections between a single Controller and up to 4 CODECs on a circuit board and/or riser card.
Now if you prefer "ac97_controller" or something like that, that's as you wish, the name does not matter that much to me ;)
Cheers.
On Tue, May 03, 2016 at 09:43:20PM +0200, Robert Jarzmik wrote:
You probably mean the BITCLK clock.
What is a bit pesky about this clock is that it can either be mastered by digital controller and the codec is a slave, or the other way around.
That's a bit surprising - I've never encountered a system that impelemnts this, it may be permitted by the spec but it's always the CODEC. The master clock from the CODEC is often provided by the SoC but I've not seen systems where anything other than the CODEC drives the actual AC'97 bus.
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
Why "digital"?
I copy-pasted this from Audio Codec '97 Revision 2.3, where in several places they call the controller a "digital controller".
It's not really adding anything though, it's just clumsy wording on their part - it's not like we need to distinguish this from analogue or any other type of AC'97 controllers.
Now if you prefer "ac97_controller" or something like that, that's as you wish, the name does not matter that much to me ;)
Yes.
Mark Brown broonie@kernel.org writes:
On Tue, May 03, 2016 at 09:43:20PM +0200, Robert Jarzmik wrote:
You probably mean the BITCLK clock.
What is a bit pesky about this clock is that it can either be mastered by digital controller and the codec is a slave, or the other way around.
That's a bit surprising - I've never encountered a system that impelemnts this, it may be permitted by the spec but it's always the CODEC. The master clock from the CODEC is often provided by the SoC but I've not seen systems where anything other than the CODEC drives the actual AC'97 bus.
Ok, so let's assume a one direction. I'll have a look how I could add this clock to the probe() exactly as amba_probe() does.
It's not really adding anything though, it's just clumsy wording on their part - it's not like we need to distinguish this from analogue or any other type of AC'97 controllers.
Now if you prefer "ac97_controller" or something like that, that's as you wish, the name does not matter that much to me ;)
Yes.
Okay, let me add this to my todo list for v2. I already have suspend/resume/pm_suspend/pm_resume.
Cheers.
On Sat, 30 Apr 2016 23:15:34 +0200, Robert Jarzmik wrote:
diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h new file mode 100644 index 000000000000..4b8b3e570892 --- /dev/null +++ b/include/sound/ac97/codec.h @@ -0,0 +1,98 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#ifndef AC97_CODEC_H +#define AC97_CODEC_H
Let's be careful about the choice of the guard.
+#include <linux/device.h>
+#define AC97_ID(vendor_id1, vendor_id2) \
- (((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff))
+#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \
- { .id = ((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff), \
.mask = ((mask_id1 & 0xffff) << 16) | (mask_id2 & 0xffff), \
.data = _data }
Give parentheses around the macro arguments.
+#define to_ac97_device(d) container_of(d, struct ac97_codec_device, dev) +#define to_ac97_driver(d) container_of(d, struct ac97_codec_driver, driver)
+struct ac97_controller;
+/**
- struct ac97_id - matches a codec device and driver on an ac97 bus
- @id: The significant bits if the codec vendor ID1 and ID2
- @mask: Bitmask specifying which bits of the id field are significant when
matching. A driver binds to a device when :
((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id.
- @data: Private data used by the driver.
- */
+struct ac97_id {
- unsigned int id;
- unsigned int mask;
- void *data;
+};
+/**
- ac97_codec_device - a ac97 codec
- @dev: the code device
- @vendor_id: the vendor_id of the codec, as sensed on the AC-link
- @num: the codec number, 0 is primary, 1 is first slave, etc ...
- @ac97_ctrl: ac97 digital controller on the same AC-link
- This is the device instanciated for each codec living on a AC-link. There are
- normally 0 to 4 codec devices per AC-link, and all of them are controlled by
- an AC97 digital controller.
- */
+struct ac97_codec_device {
- struct device dev; /* Must stay first member */
This doesn't have to be the first element as long as you use container_of().
- unsigned int vendor_id;
- unsigned int num;
- struct list_head list;
- struct ac97_controller *ac97_ctrl;
+};
+/**
- ac97_codec_driver - a ac97 codec driver
- @driver: the device driver structure
- @probe: the function called when a ac97_codec_device is matched
- @remove: the function called when the device is unbound/removed
- @suspend: suspend function (might be NULL)
- @resume: resume function (might be NULL)
- @shutdown: shutdown function (might be NULL)
- @id_table: ac97 vendor_id match table, { } member terminated
- */
+struct ac97_codec_driver {
- struct device_driver driver;
- int (*probe)(struct ac97_codec_device *);
- int (*remove)(struct ac97_codec_device *);
- int (*suspend)(struct ac97_codec_device *);
- int (*resume)(struct ac97_codec_device *);
- void (*shutdown)(struct ac97_codec_device *);
- struct ac97_id *id_table;
Missing const?
+};
+int ac97_codec_driver_register(struct ac97_codec_driver *drv); +void ac97_codec_driver_unregister(struct ac97_codec_driver *drv);
+static inline struct device * +ac97_codec_dev2dev(const struct ac97_codec_device *adev) +{
- return (struct device *)(adev);
What's wrong with the simple &adev->dev ? Cast looks scary.
+}
+static inline void *ac97_get_drvdata(const struct ac97_codec_device *adev) +{
- return dev_get_drvdata(ac97_codec_dev2dev(adev));
+}
+static inline void ac97_set_drvdata(const struct ac97_codec_device *adev,
void *data)
+{
- dev_set_drvdata(ac97_codec_dev2dev(adev), data);
+}
+#endif diff --git a/include/sound/ac97/compat.h b/include/sound/ac97/compat.h new file mode 100644 index 000000000000..bf611f572f2d --- /dev/null +++ b/include/sound/ac97/compat.h @@ -0,0 +1,21 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- This file is for backward compatibility with snd_ac97 structure and its
- multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops.
- */
+#ifndef AC97_COMPAT_H +#define AC97_COMPAT_H
+#include <sound/ac97_codec.h> +#include <sound/soc.h>
+struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec); +void compat_release_snd_ac97_codec(struct snd_ac97 *ac97);
+#endif diff --git a/include/sound/ac97/controller.h b/include/sound/ac97/controller.h new file mode 100644 index 000000000000..f1e5e645f5ef --- /dev/null +++ b/include/sound/ac97/controller.h @@ -0,0 +1,39 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#ifndef AC97_CONTROLLER_H +#define AC97_CONTROLLER_H
+#include <linux/list.h>
+struct device; +struct ac97_codec_device;
+struct ac97_controller_ops {
- void (*reset)(struct ac97_codec_device *ac97);
- void (*warm_reset)(struct ac97_codec_device *ac97);
- int (*write)(struct ac97_codec_device *ac97, unsigned short reg,
unsigned short val);
- int (*read)(struct ac97_codec_device *ac97, unsigned short reg);
- void (*wait)(struct ac97_codec_device *ac97);
- void (*init)(struct ac97_codec_device *ac97);
+};
+struct ac97_controller {
- const struct ac97_controller_ops *ops;
- struct list_head controllers;
- struct device *dev;
- int bus_idx;
What is this bus_idx for?
- int bound_codecs;
- struct list_head codecs;
+};
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
+#endif diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig new file mode 100644 index 000000000000..fd2c2d031e62 --- /dev/null +++ b/sound/ac97/Kconfig @@ -0,0 +1,9 @@ +# +# PCI configuration +#
Still only for PCI? :)
+config AC97
- bool "AC97 bus"
- help
Say Y here if you want to have AC97 devices, which are sound oriented
devices around an AC-Link.
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile new file mode 100644 index 000000000000..5575909d46e2 --- /dev/null +++ b/sound/ac97/Makefile @@ -0,0 +1,5 @@ +# +# make for AC97 bus drivers +#
+obj-y += bus.o codec.o snd_ac97_compat.o
No possibility for modules?
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h new file mode 100644 index 000000000000..db6e27288357 --- /dev/null +++ b/sound/ac97/ac97_core.h @@ -0,0 +1,10 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num);
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c new file mode 100644 index 000000000000..f9bf5632d4aa --- /dev/null +++ b/sound/ac97/bus.c @@ -0,0 +1,330 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#include <linux/module.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <sound/ac97/regs.h>
+#define AC97_BUS_MAX_CODECS 4
+/*
- Protects ac97_controllers and each ac97_controller structure.
- */
+static DEFINE_MUTEX(ac97_controllers_mutex); +static LIST_HEAD(ac97_controllers); +static int ac97_bus_idx;
+struct bus_type ac97_bus_type;
+static struct ac97_codec_device * +ac97_codec_find(struct ac97_controller *ac97_ctrl, int codec_num) +{
- struct ac97_codec_device *codec;
- list_for_each_entry(codec, &ac97_ctrl->codecs, list)
if (codec->num == codec_num)
return codec;
- return NULL;
+}
It's a question whether we need to manage the codecs in the linked list. There can be at most 4 codecs, so it fits in an array well, too. Then some codes like this would be simpler. (And it'll even reduce the footprint, too.)
+static void ac97_codec_release(struct device *dev) +{
- struct ac97_codec_device *codec;
- codec = container_of(dev, struct ac97_codec_device, dev);
- list_del(&codec->list);
- kfree(codec);
+}
+static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
unsigned int vendor_id)
+{
- struct ac97_codec_device *codec;
- char *codec_name;
- int ret;
- codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (!codec)
return -ENOMEM;
- codec->vendor_id = vendor_id;
- codec->dev.release = ac97_codec_release;
- codec->dev.bus = &ac97_bus_type;
- codec->dev.parent = ac97_ctrl->dev;
- codec->num = idx;
- codec->ac97_ctrl = ac97_ctrl;
- INIT_LIST_HEAD(&codec->list);
- list_move_tail(&codec->list, &ac97_ctrl->codecs);
- codec_name = kasprintf(GFP_KERNEL, "%s:%d", dev_name(ac97_ctrl->dev),
idx);
- codec->dev.init_name = codec_name;
- ret = device_register(&codec->dev);
- kfree(codec_name);
- return ret;
+}
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num)
+{
- struct ac97_codec_device codec;
- unsigned short vid1, vid2;
- int ret;
- codec.dev = *ac97->dev;
- codec.num = codec_num;
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
- vid1 = (ret & 0xffff);
- if (ret < 0)
return 0;
Hmm. This looks pretty hackish and dangerous.
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID2);
- vid2 = (ret & 0xffff);
- if (ret < 0)
return 0;
- dev_dbg(&codec.dev, "%s(codec_num=%d): vendor_id=0x%08x\n",
__func__, codec_num, AC97_ID(vid1, vid2));
- return AC97_ID(vid1, vid2);
+}
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl) +{
- int ret, i;
- unsigned int vendor_id;
- for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
if (ac97_codec_find(ac97_ctrl, i))
continue;
vendor_id = ac97_bus_scan_one(ac97_ctrl, i);
if (!vendor_id)
continue;
ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
if (ret < 0)
return ret;
This is one of concerns: we don't know whether the device really reacts well if you access to a non-existing slot. At least, it'd be safer to have the masks for the devices we already know the slots.
- }
- return 0;
+}
+void ac97_rescan_all_controllers(void) +{
- struct ac97_controller *ac97_ctrl;
- int ret;
- mutex_lock(&ac97_controllers_mutex);
- list_for_each_entry(ac97_ctrl, &ac97_controllers, controllers) {
ret = ac97_bus_scan(ac97_ctrl);
if (ret)
dev_warn(ac97_ctrl->dev, "scan failed: %d\n", ret);
- }
- mutex_unlock(&ac97_controllers_mutex);
+} +EXPORT_SYMBOL(ac97_rescan_all_controllers);
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) +{
- struct ac97_codec_device codec;
- memset(&codec, 0, sizeof(codec));
- codec.dev = *ac97_ctrl->dev;
- ac97_ctrl->ops->reset(&codec);
So, this assumes that reset ops is mandatory? Then document it at least.
thanks,
Takashi
- return 0;
+}
+/**
- ac97_codec_driver_register - register an AC97 codec driver
- @dev: AC97 driver codec to register
- Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
- controller.
- Returns 0 on success or error code
- */
+int ac97_codec_driver_register(struct ac97_codec_driver *drv) +{
- int ret;
- drv->driver.bus = &ac97_bus_type;
- ret = driver_register(&drv->driver);
- if (!ret)
ac97_rescan_all_controllers();
- return ret;
+} +EXPORT_SYMBOL(ac97_codec_driver_register);
+/**
- ac97_codec_driver_unregister - unregister an AC97 codec driver
- @dev: AC97 codec driver to unregister
- Unregister a previously registered ac97 codec driver.
- */
+void ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{
- driver_unregister(&drv->driver);
+} +EXPORT_SYMBOL(ac97_codec_driver_unregister);
+static int ac97_dc_codecs_unregister(struct ac97_controller *ac97_ctrl) +{
- struct ac97_codec_device *codec, *tmp;
- list_for_each_entry_safe(codec, tmp, &ac97_ctrl->codecs, list)
put_device(&codec->dev);
- return 0;
+}
+/**
- ac97_digital_controller_register - register an ac97 controller
- @ops: the ac97 bus operations
- @dev: the device providing the ac97 DC function
- Register a digital controller which can control up to 4 ac97 codecs. This is
- the controller side of the AC97 AC-link, while the slave side are the codecs.
- Returns a positive bus index upon success, negative value upon error
- */
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev)
+{
- struct ac97_controller *ac97_ctrl;
- ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
- if (!ac97_ctrl)
return -ENOMEM;
- mutex_lock(&ac97_controllers_mutex);
- ac97_ctrl->ops = ops;
- ac97_ctrl->bus_idx = ac97_bus_idx++;
- ac97_ctrl->dev = dev;
- INIT_LIST_HEAD(&ac97_ctrl->codecs);
- list_add(&ac97_ctrl->controllers, &ac97_controllers);
- mutex_unlock(&ac97_controllers_mutex);
- ac97_bus_reset(ac97_ctrl);
- ac97_bus_scan(ac97_ctrl);
- return ac97_ctrl->bus_idx;
+} +EXPORT_SYMBOL(ac97_digital_controller_register);
+/**
- ac97_digital_controller_unregister - unregister an ac97 controller
- @dev: the device previously provided to ac97_digital_controller_register()
- Returns 0 on success, negative upon error
- */
+int ac97_digital_controller_unregister(const struct device *dev) +{
- struct ac97_controller *ac97_ctrl, *tmp;
- int ret = -ENODEV;
- mutex_lock(&ac97_controllers_mutex);
- list_for_each_entry_safe(ac97_ctrl, tmp, &ac97_controllers,
controllers) {
if (ac97_ctrl->dev != dev)
continue;
if (ac97_ctrl->bound_codecs)
ret = -EBUSY;
else
ret = ac97_dc_codecs_unregister(ac97_ctrl);
if (!ret)
list_del(&ac97_ctrl->controllers);
- }
- mutex_unlock(&ac97_controllers_mutex);
- return ret;
+} +EXPORT_SYMBOL(ac97_digital_controller_unregister);
+static const struct dev_pm_ops ac97_pm = {
- .suspend = pm_generic_suspend,
- .resume = pm_generic_resume,
- .freeze = pm_generic_freeze,
- .thaw = pm_generic_thaw,
- .poweroff = pm_generic_poweroff,
- .restore = pm_generic_restore,
+};
+static ssize_t vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
+{
- struct ac97_codec_device *codec = to_ac97_device(dev);
- return sprintf(buf, "%08x", codec->vendor_id);
+}
+static struct device_attribute ac97_dev_attrs[] = {
- __ATTR_RO(vendor_id),
- __ATTR_NULL,
+};
+int ac97_bus_match(struct device *dev, struct device_driver *drv) +{
- struct ac97_codec_device *adev = to_ac97_device(dev);
- struct ac97_codec_driver *adrv = to_ac97_driver(drv);
- struct ac97_id *id = adrv->id_table;
- if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
return false;
- do {
if ((id->id & id->mask) == (adev->vendor_id & id->mask))
return true;
- } while (++id->id);
- return false;
+}
+static int ac97_bus_probe(struct device *dev) +{
- struct ac97_codec_device *adev = to_ac97_device(dev);
- struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
- return adrv->probe(adev);
+}
+static int ac97_bus_remove(struct device *dev) +{
- struct ac97_codec_device *adev = to_ac97_device(dev);
- struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
- return adrv->remove(adev);
+}
+struct bus_type ac97_bus_type = {
- .name = "ac97",
- .dev_attrs = ac97_dev_attrs,
- .match = ac97_bus_match,
- .pm = &ac97_pm,
- .probe = ac97_bus_probe,
- .remove = ac97_bus_remove,
+}; +EXPORT_SYMBOL(ac97_bus_type);
+static int __init ac97_bus_init(void) +{
- return bus_register(&ac97_bus_type);
+} +subsys_initcall(ac97_bus_init);
+MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Jarzmik robert.jarzmik@free.fr"); diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c new file mode 100644 index 000000000000..a835f03744bf --- /dev/null +++ b/sound/ac97/codec.c @@ -0,0 +1,15 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/soc.h> /* For compat_ac97_* */
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c new file mode 100644 index 000000000000..7e2f01c96fc9 --- /dev/null +++ b/sound/ac97/snd_ac97_compat.c @@ -0,0 +1,104 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#include <linux/list.h> +#include <linux/slab.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <sound/soc.h>
+#include "ac97_core.h"
+static void compat_ac97_reset(struct snd_ac97 *ac97) +{
- struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
- struct ac97_controller *actrl = adev->ac97_ctrl;
- if (actrl->ops->reset)
actrl->ops->reset(adev);
+}
+static void compat_ac97_warm_reset(struct snd_ac97 *ac97) +{
- struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
- struct ac97_controller *actrl = adev->ac97_ctrl;
- if (actrl->ops->warm_reset)
actrl->ops->warm_reset(adev);
+}
+static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
+{
- struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
- struct ac97_controller *actrl = adev->ac97_ctrl;
- actrl->ops->write(adev, reg, val);
+}
+static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
+{
- struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
- struct ac97_controller *actrl = adev->ac97_ctrl;
- return actrl->ops->read(adev, reg);
+}
+static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
- .reset = compat_ac97_reset,
- .warm_reset = compat_ac97_warm_reset,
- .write = compat_ac97_write,
- .read = compat_ac97_read,
+};
+static struct snd_ac97_bus compat_soc_ac97_bus = {
- .ops = &compat_snd_ac97_bus_ops,
+};
+struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec) +{
- struct snd_ac97 *ac97;
- ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
- if (ac97 == NULL)
return ERR_PTR(-ENOMEM);
- ac97->dev = *codec->dev;
- ac97->private_data = codec->dev;
- ac97->bus = &compat_soc_ac97_bus;
- return ac97;
+} +EXPORT_SYMBOL_GPL(compat_alloc_snd_ac97_codec);
+void compat_release_snd_ac97_codec(struct snd_ac97 *ac97) +{
- kfree(ac97);
+} +EXPORT_SYMBOL_GPL(compat_release_snd_ac97_codec);
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
- unsigned int id_mask)
+{
- struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
- struct ac97_controller *actrl = adev->ac97_ctrl;
- if (try_warm) {
compat_ac97_warm_reset(ac97);
if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id)
return 1;
- }
- compat_ac97_reset(ac97);
- compat_ac97_warm_reset(ac97);
- if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id)
return 0;
- return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
2.1.4
Takashi Iwai tiwai@suse.de writes:
On Sat, 30 Apr 2016 23:15:34 +0200, Robert Jarzmik wrote:
diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h new file mode 100644 index 000000000000..4b8b3e570892 --- /dev/null +++ b/include/sound/ac97/codec.h @@ -0,0 +1,98 @@ +/*
- Copyright (C) 2016 Robert Jarzmik robert.jarzmik@free.fr
- 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.
- */
+#ifndef AC97_CODEC_H +#define AC97_CODEC_H
Let's be careful about the choice of the guard.
Ok, would _SND_AC97_CODEC_H be better ?
+#define AC97_ID(vendor_id1, vendor_id2) \
- (((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff))
+#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \
- { .id = ((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff), \
.mask = ((mask_id1 & 0xffff) << 16) | (mask_id2 & 0xffff), \
.data = _data }
Give parentheses around the macro arguments.
Right, for RFC v2.
+struct ac97_codec_device {
- struct device dev; /* Must stay first member */
This doesn't have to be the first element as long as you use container_of().
Ah yes, that's a leftover from a former idea, I'll remove that comment.
In the initial code I'd done "struct ac97_codec_device" was hidden from this file (ie. there was only a "struct ac97_codec_device;" statement), the body of the struct was contained in sound/ac97/ac97_core.h.
The only provided macro to access the "struct device" inside "struct ac97_codec_device" was relying on this "trick" (that's a bit like in the video4linux area).
Anyway, good point, I'll remove that.
+struct ac97_codec_driver {
- struct device_driver driver;
- int (*probe)(struct ac97_codec_device *);
- int (*remove)(struct ac97_codec_device *);
- int (*suspend)(struct ac97_codec_device *);
- int (*resume)(struct ac97_codec_device *);
- void (*shutdown)(struct ac97_codec_device *);
- struct ac97_id *id_table;
Missing const?
Ah no, unfortunately not, or rather not yet.
I tried that one, not very hard, but at least ac97_bus_match() with the pair "struct ac97_id *id = adrv->id_table" and "do { } while (++id->id);" is not possible AFAIK with a const.
I will see if I can come up with something better for ac97_bus_match, such as array usage instead of pointer arithmetics.
+};
+int ac97_codec_driver_register(struct ac97_codec_driver *drv); +void ac97_codec_driver_unregister(struct ac97_codec_driver *drv);
+static inline struct device * +ac97_codec_dev2dev(const struct ac97_codec_device *adev) +{
- return (struct device *)(adev);
What's wrong with the simple &adev->dev ? Cast looks scary.
The same leftover than above, I'll change that for RFC v2.
+struct ac97_controller {
- const struct ac97_controller_ops *ops;
- struct list_head controllers;
- struct device *dev;
- int bus_idx;
What is this bus_idx for?
Initially it was to distinguish 2 different AC97 controllers. In the current patchset state, it's not usefull anymore AFAICS. So let's remove it.
- int bound_codecs;
The same comment would apply here. I don't think that information is important anymore. I thought I would use that to prevent AC97 controler removal while codecs are still bound.
In a second thought what would be better is to have get_device() called for each bound codec which will prevent ac97_digital_controller_unregister() to succeed (-EBUSY).
- struct list_head codecs;
+};
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
+#endif diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig new file mode 100644 index 000000000000..fd2c2d031e62 --- /dev/null +++ b/sound/ac97/Kconfig @@ -0,0 +1,9 @@ +# +# PCI configuration +#
Still only for PCI? :)
Ouch ;) I'll amend that for RFC v2.
+config AC97
- bool "AC97 bus"
- help
Say Y here if you want to have AC97 devices, which are sound oriented
devices around an AC-Link.
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile new file mode 100644 index 000000000000..5575909d46e2 --- /dev/null +++ b/sound/ac97/Makefile @@ -0,0 +1,5 @@ +# +# make for AC97 bus drivers +#
+obj-y += bus.o codec.o snd_ac97_compat.o
No possibility for modules?
There should be, so I'll put that on my TODO list for RFC v2.
+static struct ac97_codec_device * +ac97_codec_find(struct ac97_controller *ac97_ctrl, int codec_num) +{
- struct ac97_codec_device *codec;
- list_for_each_entry(codec, &ac97_ctrl->codecs, list)
if (codec->num == codec_num)
return codec;
- return NULL;
+}
It's a question whether we need to manage the codecs in the linked list. There can be at most 4 codecs, so it fits in an array well, too. Then some codes like this would be simpler. (And it'll even reduce the footprint, too.)
Agreed. For RFC v2.
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num)
+{
- struct ac97_codec_device codec;
- unsigned short vid1, vid2;
- int ret;
- codec.dev = *ac97->dev;
- codec.num = codec_num;
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
- vid1 = (ret & 0xffff);
- if (ret < 0)
return 0;
Hmm. This looks pretty hackish and dangerous.
You mean returning 0 even if the read failed, right ? A better prototype would probably be (for RFC v2): int ac97_bus_scan_one(struct ac97_controller *ac97, int codec_num, unsigned int *vendor_id);
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl) +{
- int ret, i;
- unsigned int vendor_id;
- for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
if (ac97_codec_find(ac97_ctrl, i))
continue;
vendor_id = ac97_bus_scan_one(ac97_ctrl, i);
if (!vendor_id)
continue;
ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
if (ret < 0)
return ret;
This is one of concerns: we don't know whether the device really reacts well if you access to a non-existing slot. At least, it'd be safer to have the masks for the devices we already know the slots.
Ah you mean upon ac97 controller registration, the ac97_digital_controller_register() should provide the information for each of the 4 slots : - does the controller enable this slot (default yes) - does the controller support auto-scan for this slot (default yes) I'm not sure this "feature" is required, it looks a bit over-engineered.
That could be a matter of 1 or 2 masks as input parameters to ac97_digital_controller_register().
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) +{
- struct ac97_codec_device codec;
- memset(&codec, 0, sizeof(codec));
- codec.dev = *ac97_ctrl->dev;
- ac97_ctrl->ops->reset(&codec);
So, this assumes that reset ops is mandatory? Then document it at least.
Ok, for RFC v2.
Thanks for your review and feedbacks Takashi, I'll work on both Mark and your comments in the next weeks.
Cheers.
On Sat, 14 May 2016 11:50:50 +0200, Robert Jarzmik wrote:
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num)
+{
- struct ac97_codec_device codec;
- unsigned short vid1, vid2;
- int ret;
- codec.dev = *ac97->dev;
- codec.num = codec_num;
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
- vid1 = (ret & 0xffff);
- if (ret < 0)
return 0;
Hmm. This looks pretty hackish and dangerous.
You mean returning 0 even if the read failed, right ?
No, my concern is that it's creating a dummy codec object temporarily on the stack just by copying some fields and calling the ops with it. (And actually the current code may work wrongly because lack of zero-clear of the object.)
IMO, a cleaner way would be to define the ops passed with both controller and codec objects as arguments, and pass NULL codec here.
Takashi
Takashi Iwai tiwai@suse.de writes:
On Sat, 14 May 2016 11:50:50 +0200, Robert Jarzmik wrote:
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num)
+{
- struct ac97_codec_device codec;
- unsigned short vid1, vid2;
- int ret;
- codec.dev = *ac97->dev;
- codec.num = codec_num;
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
- vid1 = (ret & 0xffff);
- if (ret < 0)
return 0;
Hmm. This looks pretty hackish and dangerous.
You mean returning 0 even if the read failed, right ?
No, my concern is that it's creating a dummy codec object temporarily on the stack just by copying some fields and calling the ops with it. (And actually the current code may work wrongly because lack of zero-clear of the object.)
Ah yes, I remember now, the on-stack generated device, indeed ugly.
IMO, a cleaner way would be to define the ops passed with both controller and codec objects as arguments, and pass NULL codec here.
It's rather unusual to need both the device and its controller in bus operations. I must admit I have no better idea so far, so I'll try that just to see how it looks like, and let's see next ...
Cheers.
On Sun, 15 May 2016 23:29:27 +0200, Robert Jarzmik wrote:
Takashi Iwai tiwai@suse.de writes:
On Sat, 14 May 2016 11:50:50 +0200, Robert Jarzmik wrote:
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
int codec_num)
+{
- struct ac97_codec_device codec;
- unsigned short vid1, vid2;
- int ret;
- codec.dev = *ac97->dev;
- codec.num = codec_num;
- ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
- vid1 = (ret & 0xffff);
- if (ret < 0)
return 0;
Hmm. This looks pretty hackish and dangerous.
You mean returning 0 even if the read failed, right ?
No, my concern is that it's creating a dummy codec object temporarily on the stack just by copying some fields and calling the ops with it. (And actually the current code may work wrongly because lack of zero-clear of the object.)
Ah yes, I remember now, the on-stack generated device, indeed ugly.
IMO, a cleaner way would be to define the ops passed with both controller and codec objects as arguments, and pass NULL codec here.
It's rather unusual to need both the device and its controller in bus operations. I must admit I have no better idea so far, so I'll try that just to see how it looks like, and let's see next ...
Thinking of this again, I wonder now why we need to pass the codec object at all. It's the read/write ops via ac97, so we just need the ac97_controller object and the address slot of the accessed codec?
Takashi
Takashi Iwai tiwai@suse.de writes:
On Sun, 15 May 2016 23:29:27 +0200, Robert Jarzmik wrote:
Takashi Iwai tiwai@suse.de writes:
On Sat, 14 May 2016 11:50:50 +0200,
No, my concern is that it's creating a dummy codec object temporarily on the stack just by copying some fields and calling the ops with it. (And actually the current code may work wrongly because lack of zero-clear of the object.)
Ah yes, I remember now, the on-stack generated device, indeed ugly.
IMO, a cleaner way would be to define the ops passed with both controller and codec objects as arguments, and pass NULL codec here.
It's rather unusual to need both the device and its controller in bus operations. I must admit I have no better idea so far, so I'll try that just to see how it looks like, and let's see next ...
Thinking of this again, I wonder now why we need to pass the codec object at all. It's the read/write ops via ac97, so we just need the ac97_controller object and the address slot of the accessed codec?
So far it would work. The only objection I would see is if in the future the bus operation needs a specialization which is ac97 codec dependent, such as a flag or a mask in ac97_codec_device structure.
Even if I'd like to not have these in bus operations, the struct snd_ac97 had a need for a 'caps', 'ext_id', ... fields for example. Yet these could be contained in the ac97_codec_device structure and not exposed to bus operations.
Another worry is the pattern (as an example) in atmel_ac97c_write() in sound/atmel/ac97.c, where the codec structure is used to get the controller through a container_of() type call. Yet passing the controller to bus operations takes care of this one.
From a "purely API" point of view the couple (ac97_controller, ac97_slot_id) is
what will route an ac97 bus operation, be that a read/write/reset/..., the remaining question is will it cover the cases we've not thought of ?
Cheers.
On Mon, 16 May 2016 10:53:56 +0200, Robert Jarzmik wrote:
Takashi Iwai tiwai@suse.de writes:
On Sun, 15 May 2016 23:29:27 +0200, Robert Jarzmik wrote:
Takashi Iwai tiwai@suse.de writes:
On Sat, 14 May 2016 11:50:50 +0200,
No, my concern is that it's creating a dummy codec object temporarily on the stack just by copying some fields and calling the ops with it. (And actually the current code may work wrongly because lack of zero-clear of the object.)
Ah yes, I remember now, the on-stack generated device, indeed ugly.
IMO, a cleaner way would be to define the ops passed with both controller and codec objects as arguments, and pass NULL codec here.
It's rather unusual to need both the device and its controller in bus operations. I must admit I have no better idea so far, so I'll try that just to see how it looks like, and let's see next ...
Thinking of this again, I wonder now why we need to pass the codec object at all. It's the read/write ops via ac97, so we just need the ac97_controller object and the address slot of the accessed codec?
So far it would work. The only objection I would see is if in the future the bus operation needs a specialization which is ac97 codec dependent, such as a flag or a mask in ac97_codec_device structure.
Even if I'd like to not have these in bus operations, the struct snd_ac97 had a need for a 'caps', 'ext_id', ... fields for example. Yet these could be contained in the ac97_codec_device structure and not exposed to bus operations.
Do we have any example of such exceptions? For AC97, we don't need to think of future extensions at all.
If any, we may provide two levels of ops abstractions: the lower ac97_controller_ops.read/write, and the upper ac97_codec_ops read/write that may override if defined, but as default it just wraps over controller ops. But I don't think we'd need such unless we really see the demand to be exposed outside the codec driver itself.
Another worry is the pattern (as an example) in atmel_ac97c_write() in sound/atmel/ac97.c, where the codec structure is used to get the controller through a container_of() type call. Yet passing the controller to bus operations takes care of this one.
Right.
From a "purely API" point of view the couple (ac97_controller, ac97_slot_id) is what will route an ac97 bus operation, be that a read/write/reset/..., the remaining question is will it cover the cases we've not thought of ?
The remaining ops are rather codec-specific operations, and they don't have to be implemented in the bus (controller) level. We should keep the bus ops as small as possible.
thanks,
Takashi
On Mon, May 16, 2016 at 02:58:13PM +0200, Takashi Iwai wrote:
Robert Jarzmik wrote:
Even if I'd like to not have these in bus operations, the struct snd_ac97 had a need for a 'caps', 'ext_id', ... fields for example. Yet these could be contained in the ac97_codec_device structure and not exposed to bus operations.
Do we have any example of such exceptions? For AC97, we don't need to think of future extensions at all.
I can't think of any, even for some of the more fancy CODECs used in embedded systems.
Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- sound/soc/codecs/wm9713.c | 62 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 9849643ef809..bd5229236a8a 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -22,7 +22,8 @@ #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> -#include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -1207,14 +1208,13 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); struct regmap *regmap;
- wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, - WM9713_VENDOR_ID_MASK); + wm9713->ac97 = compat_alloc_snd_ac97_codec(codec); if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97);
regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); if (IS_ERR(regmap)) { - snd_soc_free_ac97_codec(wm9713->ac97); + compat_release_snd_ac97_codec(wm9713->ac97); return PTR_ERR(regmap); }
@@ -1231,7 +1231,7 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec) struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9713->ac97); + compat_release_snd_ac97_codec(wm9713->ac97); return 0; }
@@ -1281,7 +1281,57 @@ static struct platform_driver wm9713_codec_driver = { .remove = wm9713_remove, };
-module_platform_driver(wm9713_codec_driver); +static int wm9713_ac97_probe(struct ac97_codec_device *adev) +{ + struct wm9713_priv *wm9713; + + wm9713 = devm_kzalloc(ac97_codec_dev2dev(adev), + sizeof(*wm9713), GFP_KERNEL); + if (wm9713 == NULL) + return -ENOMEM; + + mutex_init(&wm9713->lock); + + ac97_set_drvdata(adev, wm9713); + + return snd_soc_register_codec(ac97_codec_dev2dev(adev), + &soc_codec_dev_wm9713, wm9713_dai, + ARRAY_SIZE(wm9713_dai)); +} + +static int wm9713_ac97_remove(struct ac97_codec_device *adev) +{ + snd_soc_unregister_codec(ac97_codec_dev2dev(adev)); + return 0; +} + +static struct ac97_id wm9713_ac97_ids[] = { + { .id = WM9713_VENDOR_ID, .mask = WM9713_VENDOR_ID_MASK }, + { } +}; + +static struct ac97_codec_driver wm9713_ac97_driver = { + .driver = { + .name = "wm9713-codec", + }, + .probe = wm9713_ac97_probe, + .remove = wm9713_ac97_remove, + .id_table = wm9713_ac97_ids, +}; + +static int __init wm9713_init(void) +{ + ac97_codec_driver_register(&wm9713_ac97_driver); + return platform_driver_register(&wm9713_codec_driver); +} +module_init(wm9713_init); + +static void __exit wm9713_exit(void) +{ + ac97_codec_driver_unregister(&wm9713_ac97_driver); + return platform_driver_unregister(&wm9713_codec_driver); +} +module_exit(wm9713_exit);
MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); MODULE_AUTHOR("Liam Girdwood");
Switch to the new ac97 bus support in sound/ac97 instead of the legacy snd_ac97 one.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- include/sound/pxa2xx-lib.h | 14 ++++++++------ sound/arm/Kconfig | 1 - sound/arm/pxa2xx-ac97-lib.c | 31 +++++++++++++++++++------------ sound/soc/pxa/Kconfig | 4 ++-- sound/soc/pxa/pxa2xx-ac97.c | 12 +++++++----- 5 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 6ef629bde164..7072353234f9 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -2,7 +2,8 @@ #define PXA2XX_LIB_H
#include <linux/platform_device.h> -#include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h>
/* PCM */
@@ -21,12 +22,13 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
/* AC97 */
-extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg); -extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val); +extern int pxa2xx_ac97_read(struct ac97_codec_device *ac97, unsigned short reg); +extern int pxa2xx_ac97_write(struct ac97_codec_device *ac97, + unsigned short reg, unsigned short val);
-extern bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97); -extern bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97); -extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97); +extern bool pxa2xx_ac97_try_warm_reset(struct ac97_codec_device *ac97); +extern bool pxa2xx_ac97_try_cold_reset(struct ac97_codec_device *ac97); +extern void pxa2xx_ac97_finish_reset(struct ac97_codec_device *ac97);
extern int pxa2xx_ac97_hw_suspend(void); extern int pxa2xx_ac97_hw_resume(void); diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index e0406211716b..94f7539a2c1f 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -11,7 +11,6 @@ menuconfig SND_ARM
config SND_PXA2XX_LIB tristate - select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 select SND_DMAENGINE_PCM
config SND_PXA2XX_LIB_AC97 diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 39c3969ac1c7..bc5c6d72510a 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -20,7 +20,7 @@ #include <linux/io.h> #include <linux/gpio.h>
-#include <sound/ac97_codec.h> +#include <sound/ac97/controller.h> #include <sound/pxa2xx-lib.h>
#include <mach/irqs.h> @@ -46,11 +46,14 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); * 1 jiffy timeout if interrupt never comes). */
-unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +int pxa2xx_ac97_read(struct ac97_codec_device *ac97, unsigned short reg) { - unsigned short val = -1; + int val = -ENODEV; volatile u32 *reg_addr;
+ if (ac97->num > 0) + return -ENODEV; + mutex_lock(&car_mutex);
/* set up primary or secondary codec space */ @@ -63,21 +66,21 @@ unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) /* start read access across the ac97 link */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); if (reg == AC97_GPIO_STATUS) goto out; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && !((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); - val = -1; + val = -ETIMEDOUT; goto out; }
/* valid data now */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); /* but we've just started another cycle... */ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
@@ -86,10 +89,11 @@ out: mutex_unlock(&car_mutex); } EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
-void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) +int pxa2xx_ac97_write(struct ac97_codec_device *ac97, unsigned short reg, + unsigned short val) { volatile u32 *reg_addr; + int ret = 0;
mutex_lock(&car_mutex);
@@ -104,11 +108,14 @@ void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, gsr_bits = 0; *reg_addr = val; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && - !((GSR | gsr_bits) & GSR_CDONE)) + !((GSR | gsr_bits) & GSR_CDONE)) { printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); + ret = -EIO; + }
mutex_unlock(&car_mutex); + return ret; } EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
@@ -188,7 +195,7 @@ static inline void pxa_ac97_cold_pxa3xx(void) } #endif
-bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_warm_reset(struct ac97_codec_device *ac97) { unsigned long gsr; unsigned int timeout = 100; @@ -225,7 +232,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) } EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
-bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_cold_reset(struct ac97_codec_device *ac97) { unsigned long gsr; unsigned int timeout = 1000; @@ -263,7 +270,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
-void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97) +void pxa2xx_ac97_finish_reset(struct ac97_codec_device *ac97) { GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f2bf8661dd21..784e6dedf5bf 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -23,9 +23,9 @@ config SND_PXA2XX_AC97
config SND_PXA2XX_SOC_AC97 tristate - select AC97_BUS + select AC97_BUS_NEW select SND_PXA2XX_LIB_AC97 - select SND_SOC_AC97_BUS + select SND_SOC_AC97_BUS_NEW
config SND_PXA2XX_SOC_I2S tristate diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index f3de615aacd7..eb3ba630994b 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */
+#include <sound/ac97/controller.h> #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> @@ -29,21 +30,21 @@
#include "pxa2xx-ac97.h"
-static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_warm_reset(struct ac97_codec_device *ac97) { pxa2xx_ac97_try_warm_reset(ac97);
pxa2xx_ac97_finish_reset(ac97); }
-static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_cold_reset(struct ac97_codec_device *ac97) { pxa2xx_ac97_try_cold_reset(ac97);
pxa2xx_ac97_finish_reset(ac97); }
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { +static struct ac97_controller_ops pxa2xx_ac97_ops = { .read = pxa2xx_ac97_read, .write = pxa2xx_ac97_write, .warm_reset = pxa2xx_ac97_warm_reset, @@ -236,7 +237,8 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) return ret; }
- ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); + //RJK: no more ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); + ret = ac97_digital_controller_register(&pxa2xx_ac97_ops, &pdev->dev); if (ret != 0) return ret;
@@ -251,7 +253,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); - snd_soc_set_ac97_ops(NULL); + ac97_digital_controller_unregister(&pdev->dev); pxa2xx_ac97_hw_remove(pdev); return 0; }
As the ac97 autoprobing is introduced, remove the manually added wm9713 device.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- arch/arm/mach-pxa/mioa701.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c index 38a96a193dc4..e69e20febf83 100644 --- a/arch/arm/mach-pxa/mioa701.c +++ b/arch/arm/mach-pxa/mioa701.c @@ -690,7 +690,6 @@ static struct platform_device *devices[] __initdata = { &mioa701_gpio_keys, &mioa701_backlight, &mioa701_led, - &wm9713_acodec, &pxa2xx_pcm, &mioa701_sound, &power_dev,
Convert to the new auto-probing ac97 bus, which changes the device name.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- sound/soc/pxa/mioa701_wm9713.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 5c8f9db50a47..4ed10e6ff5d1 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -149,7 +149,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { .stream_name = "AC97 HiFi", .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9713-hifi", - .codec_name = "wm9713-codec", + .codec_name = "pxa2xx-ac97:0", .init = mioa701_wm9713_init, .platform_name = "pxa-pcm-audio", .ops = &mioa701_ops, @@ -159,7 +159,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { .stream_name = "AC97 Aux", .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9713-aux", - .codec_name = "wm9713-codec", + .codec_name = "pxa2xx-ac97:0", .platform_name = "pxa-pcm-audio", .ops = &mioa701_ops, },
Add the new ac97 bus support, with ac97 bus automatic probing.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- sound/Kconfig | 7 +++++++ sound/Makefile | 1 + sound/soc/Kconfig | 4 ++++ 3 files changed, 12 insertions(+)
diff --git a/sound/Kconfig b/sound/Kconfig index 5a240e050ae6..83d67eae3580 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -134,3 +134,10 @@ config AC97_BUS sound subsystem and other function drivers completely unrelated to sound although they're sharing the AC97 bus. Concerned drivers should "select" this. + +config AC97_BUS_NEW + tristate + help + This is the new AC97 bus type, successor of AC97_BUS. The ported + drivers which benefit from the AC97 automatic probing should "select" + this instead of the AC97_BUS. diff --git a/sound/Makefile b/sound/Makefile index 77320709fd26..74728aee25d5 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out obj-$(CONFIG_AC97_BUS) += ac97_bus.o +obj-$(CONFIG_AC97_BUS_NEW) += ac97/
ifeq ($(CONFIG_SND),y) obj-y += last.o diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 182d92efc7c8..5ad0a0422054 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -6,6 +6,7 @@ menuconfig SND_SOC tristate "ALSA for SoC audio support" select SND_PCM select AC97_BUS if SND_SOC_AC97_BUS + select AC97_BUS_NEW if SND_SOC_AC97_BUS_NEW select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER @@ -25,6 +26,9 @@ if SND_SOC config SND_SOC_AC97_BUS bool
+config SND_SOC_AC97_BUS_NEW + bool + config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM
On Sat, 30 Apr 2016 23:15:32 +0200, Robert Jarzmik wrote:
It all started in the pxa device-tree submission here : https://lkml.org/lkml/2016/2/25/965 It will be maintained in : git fetch https://github.com/rjarzmik/linux.git work/ac97
And now it transformed into this RFC, which would bring a ground for AC'97 devices closer to the linux device/driver model.
This is just an RFC to see if we understand each other Mark.
Amongst the driving ideas behind this RFC :
- use device/driver model for AC'97 device/driver binding is based on vendor_id{1,2}
- use auto-probe of the AC'97 bus to enumerate codecs and create devices
- be compatible with previous platform_data model for codecs => this enables a smooth transition, where a codec (here wm9713) can be used either through platform_device or automatic discovery
- struct snd_ac97 is not used This structure is really heavy, and doesn't represent an AC97 device, but rather an AC'97 codec internals IIUC. I think it's right place would be in include/sound/ac97/codec.h. This is also a problem for regmap adherence, hence the include/sound/ac97/compat.h.
- a new AC'97 bus Kconfig is created This was done for a smooth transition ... let's see if it's a good idea.
- split the AC'97 into :
- the codec
- the digital controller
- the bus
Amongst the things that are not all touched yet :
- ac97_codec.c The generic AC'97 codec in sound/pci/ac97/ac97_codec.c has really a lot features, which are not at all PCI related. They could be transfered to sound/ac97, without the pci specific field. => this deserves a deep ahead thinking, which I'll do if the current approach is accepted by the communauty.
The most important patch is 2/7 "ALSA: ac97: add an ac97 bus". This is the main evaluation point for the serie, the others are here so that I can test it all.
Well, this is a long term effort, which might need a complete rewrite according to the comments it'll get. Let's expose it for comments and see how I can progress with it.
I think it's good in general. The implementation looks fairly simple and thin enough.
An open question is whether migrating the former AC97 layer into the new bus. I'm not sure about this. Transition to a new layer always brings subtle bugs, especially when the target devices are in wide range of legacy ones... If any, we should start just wrapping via the new bus ops.
Some other nitpicks: - We usually use snd_ prefix for sound stuff. Better to keep this for exported symbols at least.
- I don't see much value in the usefulness of compat_* stuff. For example, it doesn't cover the actual reset procedure or such done as in the old ac97 code. So it won't work compatibly. If it's a few lines of changes, the direct call would be likely simpler in the end.
- The order of patches needs reconsideration. The current patchset will break the build, as the hook to sound/ac97/* is done in the last patch, while you're already building against to the new stuff beforehand.
I'll comment more on each patch if any...
thanks,
Takashi
Takashi Iwai tiwai@suse.de writes:
On Sat, 30 Apr 2016 23:15:32 +0200, Robert Jarzmik wrote:
Well, this is a long term effort, which might need a complete rewrite according to the comments it'll get. Let's expose it for comments and see how I can progress with it.
I think it's good in general. The implementation looks fairly simple and thin enough.
Thanks.
An open question is whether migrating the former AC97 layer into the new bus. I'm not sure about this. Transition to a new layer always brings subtle bugs, especially when the target devices are in wide range of legacy ones... If any, we should start just wrapping via the new bus ops.
I agree, the migration to the new layer will bring bugs. And the test might be impossible to carry out by lack of testers.
The wrapping of bus operations is the goal of sound/ac97/snd_ac97_compat.c.
Some other nitpicks:
- We usually use snd_ prefix for sound stuff. Better to keep this for exported symbols at least.
Of course, for RFC v2.
- I don't see much value in the usefulness of compat_* stuff.
That's mainly the bus operation wrapping and backward compatibility. The idea is to be able to convert only the probing/removal/suspend/resume of an ac97 codec or controller without changing its main code in a first patch, and then consider surgery (if applicable) to convert it wholly.
For example, it doesn't cover the actual reset procedure or such done as in the old ac97 code.
Ah then my code is incomplete and I must work on it.
For the reset procedure I have ported snd_ac97_reset(), as well as the snd_ac97_us_ops operation wrappers. Would you give me an example of the reset I'm missing (a file and a function) ?
So it won't work compatibly. If it's a few lines of changes, the direct call would be likely simpler in the end.
- The order of patches needs reconsideration. The current patchset will break the build, as the hook to sound/ac97/* is done in the last patch, while you're already building against to the new stuff beforehand.
Very true. kbuild robot objected as well :) For RFC v2.
Cheers.
participants (3)
-
Mark Brown
-
Robert Jarzmik
-
Takashi Iwai