Function | File |
---|---|
driver | sound/soc/codecs/adau1977.c |
driver | sound/soc/codecs/adau1977-spi.c |
driver | sound/soc/codecs/adau1977-i2c.c |
include | sound/soc/codecs/adau1977.h |
platform data | include/linux/platform_data/adau1977.h |
For compile time configuration, it’s common Linux practice to keep board- and application-specific configuration out of the main driver file, instead putting it into the board support file.
For devices on custom boards, as typical of embedded and SoC-(system-on-chip) based hardware, Linux uses platform_data to point to board-specific structures describing devices and how they are connected to the SoC. This can include available ports, chip variants, preferred modes, default initialization, additional pin roles, and so on. This shrinks the board-support packages (BSPs) and minimizes board and application specific #ifdefs in drivers.
The platform data for the ADAU1977 allow to specify the MICBIAS pin voltage. If no platform data is specified (i.e. set to NULL) the default voltage of 8.5 V will be used.
/** * enum adau1977_micbias - ADAU1977 MICBIAS pin voltage setting * @ADAU1977_MICBIAS_5V0: MICBIAS is set to 5.0 V * @ADAU1977_MICBIAS_5V5: MICBIAS is set to 5.5 V * @ADAU1977_MICBIAS_6V0: MICBIAS is set to 6.0 V * @ADAU1977_MICBIAS_6V5: MICBIAS is set to 6.5 V * @ADAU1977_MICBIAS_7V0: MICBIAS is set to 7.0 V * @ADAU1977_MICBIAS_7V5: MICBIAS is set to 7.5 V * @ADAU1977_MICBIAS_8V0: MICBIAS is set to 8.0 V * @ADAU1977_MICBIAS_8V5: MICBIAS is set to 8.5 V * @ADAU1977_MICBIAS_9V0: MICBIAS is set to 9.0 V */ enum adau1977_micbias { ADAU1977_MICBIAS_5V0 = 0x0, ADAU1977_MICBIAS_5V5 = 0x1, ADAU1977_MICBIAS_6V0 = 0x2, ADAU1977_MICBIAS_6V5 = 0x3, ADAU1977_MICBIAS_7V0 = 0x4, ADAU1977_MICBIAS_7V5 = 0x5, ADAU1977_MICBIAS_8V0 = 0x6, ADAU1977_MICBIAS_8V5 = 0x7, ADAU1977_MICBIAS_9V0 = 0x8, }; /** * struct adau1977_platform_data - Platform configuration data for the ADAU1977 * @micbias: Specifies the voltage for the MICBIAS pin */ struct adau1977_platform_data { enum adau1977_micbias micbias; };
The ADAU1977 driver expects a regulator supply for the AVDD voltage to be specified. This allows the driver to disable the supply when not needed, which helps to conserve power. Additionally the driver optionally accepts a supply for the DVDD voltage. If a supply for the DVDD voltage is specified the driver will use that regulator and disable the internal LDO.
When using machine board files to register the device the regulators can be specified in the board file.
static struct regulator_consumer_supply adau1977_avdd_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "spi0.1"), }; static struct regulator_init_data adau1977_avdd_reg_init_data = { .constraints = { .name = "3V3", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, .consumer_supplies = adau1977_avdd_consumer_supplies, .num_consumer_supplies = ARRAY_SIZE(adau1977_avdd_consumer_supplies), }; static struct fixed_voltage_config adau1977_avdd_pdata = { .supply_name = "board-3V3", .microvolts = 3300000, .gpio = -EINVAL, .enabled_at_boot = 0, .init_data = &adau1977_avdd_reg_init_data, }; static struct platform_device adau1977_avdd_voltage_regulator = { .name = "reg-fixed-voltage", .id = 0, .dev = { .platform_data = &adau1977_avdd_pdata, }, }; /* Optional */ static struct regulator_consumer_supply adau1977_dvdd_consumer_supplies[] = { REGULATOR_SUPPLY("DVDD", "spi0.1"), }; static struct regulator_init_data adau1977_dvdd_reg_init_data = { .constraints = { .name = "1V8", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, .consumer_supplies = adau1977_dvdd_consumer_supplies, .num_consumer_supplies = ARRAY_SIZE(adau1977_dvdd_consumer_supplies), }; static struct fixed_voltage_config adau1977_dvdd_pdata = { .supply_name = "board-1V8", .microvolts = 1800000, .gpio = -EINVAL, .enabled_at_boot = 0, .init_data = &adau1977_dvdd_reg_init_data, }; static struct platform_device adau1977_dvdd_voltage_regulator = { .name = "reg-fixed-voltage", .id = 1, .dev = { .platform_data = &adau1977_dvdd_pdata, }, };
When using devicetree the regulators should be specified in the devicetree. For more on how to specify the regulators using devicetree see the regulator devicetree bindings documentation and the devicetree examples below in the SPI and I2C sections.
The ADAU1977 driver is fully devicetree compatible. Depending on the bus type the device is connected to different properties must/can be specified.
Shared required properties:
Shared optional properties:
I2C required properties:
SPI required properties:
Unlike PCI or USB devices, I2C devices are not enumerated at the hardware level. Instead, the software must know which devices are connected on each I2C bus segment, and what address these devices are using. For this reason, the kernel code must instantiate I2C devices explicitly. There are different ways to achieve this, depending on the context and requirements. However the most common method is to declare the I2C devices by bus number.
This method is appropriate when the I2C bus is a system bus, as in many embedded systems, wherein each I2C bus has a number which is known in advance. It is thus possible to pre-declare the I2C devices that inhabit this bus. This is done with an array of struct i2c_board_info, which is registered by calling i2c_register_board_info().
So, to enable such a driver one need only edit the board support file by adding an appropriate entry to i2c_board_info.
For more information see: Documentation/i2c/instantiating-devices
The I2C device id depends on the ADDR0 and ADDR1 pin settings and needs to be set according to your board setup.
ADDR1 | ADDR0 | I2C device id |
---|---|---|
0 | 0 | 0x11 |
0 | 1 | 0x31 |
1 | 0 | 0x51 |
1 | 1 | 0x71 |
In this example we assume ADDR0=0 and ADDR1=0.
static struct adau1977_platform_data adau1977_pdata = { .micbias = ADAU1977_MICBIAS_6V5, }; static struct i2c_board_info __initdata bfin_i2c_board_info[] = { [--snip--] { I2C_BOARD_INFO("adau1977", 0x11), .platform_data = &adau1977_pdata, }, [--snip--] }
static int __init stamp_init(void) { [--snip--] i2c_register_board_info(0, bfin_i2c_board_info, ARRAY_SIZE(bfin_i2c_board_info)); [--snip--] return 0; } arch_initcall(board_init);
codec_supply: fixedregulator@0 { compatible = "regulator-fixed"; regulator-name = "AVDD"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; i2c@41600000 { compatible = "...; ... #size-cells = <0>; #address-cells = <1>; adau1977: codec@11 { compatible = "adi,adau1977"; reg = <0x11>; reset-gpios = <&gpio 5 0>; AVDD-supply = <&codec_supply>; }; };
Unlike PCI or USB devices, SPI devices are not enumerated at the hardware level. Instead, the software must know which devices are connected on each SPI bus segment, and what slave selects these devices are using. For this reason, the kernel code must instantiate SPI devices explicitly. The most common method is to declare the SPI devices by bus number.
This method is appropriate when the SPI bus is a system bus, as in many embedded systems, wherein each SPI bus has a number which is known in advance. It is thus possible to pre-declare the SPI devices that inhabit this bus. This is done with an array of struct spi_board_info, which is registered by calling spi_register_board_info().
For more information see: Documentation/spi/spi-summary
static struct adau1977_platform_data adau1977_pdata = { .micbias = ADAU1977_MICBIAS_7V5, }; static struct spi_board_info board_spi_board_info[] __initdata = { [--snip--] { .modalias = "adau1977", .max_speed_hz = 10000000, /* max spi clock (SCK) speed in HZ */ .bus_num = 0, .chip_select = GPIO_PF10 + MAX_CTRL_CS, /* CS, change it for your board */ .mode = SPI_MODE_0, .platform_data = &adau1977_pdata, }, [--snip--] };
static int __init board_init(void) { [--snip--] spi_register_board_info(board_spi_board_info, ARRAY_SIZE(board_spi_board_info)); [--snip--] return 0; } arch_initcall(board_init);
codec_supply: fixedregulator@0 { compatible = "regulator-fixed"; regulator-name = "AVDD"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; spi@41600000 { compatible = "...; ... #size-cells = <0>; #address-cells = <1>; adau1977: codec@1 { compatible = "adi,adau1977"; reg = <0x1>; spi-max-frequency = <10000000>; reset-gpios = <&gpio 5 0>; AVDD-supply = <&codec_supply>; adi,micbias = <5>; }; };
Name | Description | Configuration |
---|---|---|
AIN1 | Analog Input Channel 1 | |
AIN2 | Analog Input Channel 2 | |
AIN3 | Analog Input Channel 3 | |
AIN4 | Analog Input Channel 4 | |
VREF | Reference Voltage | |
MICBIAS | Microphone Bias Output | ADAU1977 only |
Name | Description |
---|---|
ADC1 Capture Volume | Post ADC gain for channel 1 |
ADC2 Capture Volume | Post ADC gain for channel 2 |
ADC3 Capture Volume | Post ADC gain for channel 3 |
ADC4 Capture Volume | Post ADC gain for channel 4 |
ADC1 Highpass-Filter Capture Switch | Enables/disables the highpass-filter for channel 1 |
ADC2 Highpass-Filter Capture Switch | Enables/disables the highpass-filter for channel 2 |
ADC3 Highpass-Filter Capture Switch | Enables/disables the highpass-filter for channel 3 |
ADC4 Highpass-Filter Capture Switch | Enables/disables the highpass-filter for channel 4 |
ADC1 DC Substraction Capture Switch | Enables/disables DC offset subtraction for channel 1 |
ADC2 DC Substraction Capture Switch | Enables/disables DC offset subtraction for channel 2 |
ADC3 DC Substraction Capture Switch | Enables/disables DC offset subtraction for channel 3 |
ADC4 DC Substraction Capture Switch | Enables/disables DC offset subtraction for channel 4 |
The codec driver registers one DAI: adau1977-hifi
Name | Supported by driver | Description | Notes |
---|---|---|---|
SND_SOC_DAIFMT_I2S | yes | I2S mode | |
SND_SOC_DAIFMT_RIGHT_J | yes | Right Justified mode | |
SND_SOC_DAIFMT_LEFT_J | yes | Left Justified mode | |
SND_SOC_DAIFMT_DSP_A | yes | data MSB after FRM LRC | |
SND_SOC_DAIFMT_DSP_B | yes | data MSB during FRM LRC | |
SND_SOC_DAIFMT_AC97 | no | AC97 mode | |
SND_SOC_DAIFMT_PDM | no | Pulse density modulation | |
SND_SOC_DAIFMT_NB_NF | yes | Normal bit- and frameclock | |
SND_SOC_DAIFMT_NB_IF | yes | Normal bitclock, inverted frameclock | |
SND_SOC_DAIFMT_IB_NF | yes | Inverted frameclock, normal bitclock | |
SND_SOC_DAIFMT_IB_IF | yes | Inverted bit- and frameclock | |
SND_SOC_DAIFMT_CBM_CFM | yes | Codec bit- and frameclock master | Master mode is not supported if the LRCLK is used as clock source |
SND_SOC_DAIFMT_CBS_CFM | no | Codec bitclock slave, frameclock master | |
SND_SOC_DAIFMT_CBM_CFS | no | Codec bitclock master, frameclock slave | |
SND_SOC_DAIFMT_CBS_CFS | yes | Codec bit- and frameclock slave |
The sysclk can either be provided by the internal PLL derived from the MCLK signal or by the LRCLK signal.
enum adau1977_clk_id { ADAU1977_SYSCLK, }; enum adau1977_sysclk_src { ADAU1977_SYSCLK_SRC_MCLK, ADAU1977_SYSCLK_SRC_LRCLK, };
If the MCLK is used the supported sampling rates of the device is constraint to those sample rates that can be derived from the the MCLK. The constraints are setup in the CODEC driver's startup routine. This means that the sysclk needs to be configured before the CODEC's startup callback is called. This can either be done from the machine driver's startup or init callback.
For some hardware configurations it might be possible to change the MCLK frequency at runtime (e.g. to support both 44.1kHz and 48kHz based sample rates). In this case in order for the CODEC driver to not set any sample rate constraints the sysclk frequency should be set to 0 until it has been decided which MCLK frequency is used. This can for example be implemented by setting the sysclk to 0 in the machine driver's startup callback and setting it to the actual frequency in the hw_params callback.
The TDM mode of the ADAU1977 can be configured using the snd_soc_dai_set_tdm_slot() function.
Example TDM configuration:
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0f, 4, 32);
To configure whether the unused TDM slots should be tristated or actively driven low the snd_soc_dai_set_tristate() function is used. By default they are driven low.
Example tristate configuration:
ret = snd_soc_dai_set_tristate(codec_dai, true);
static int eval_adau1977_init(struct snd_soc_pcm_runtime *rtd) { return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK, ADAU1977_SYSCLK_SRC_MCLK, 1228000, SND_SOC_CLOCK_IN); } static struct snd_soc_dai_link eval_adau1977_dai = { .name = "adau1977", .stream_name = "ADAU1977", .cpu_dai_name = "bfin-i2s.0", .codec_dai_name = "adau1977-hifi", .platform_name = "bfin-i2s-pcm-audio", .codec_name = "adau1977.0-0011", .init = eval_adau1977_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, };
static int eval_adau1977_init(struct snd_soc_pcm_runtime *rtd) { return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK, ADAU1977_SYSCLK_SRC_MCLK, 0, SND_SOC_CLOCK_IN); } static int eval_adau1977_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; if (params_rate(params) % 441000 == 0) mclk_rate = 11289600; else mclk_rate = 12288000; clk_set_rate(mclk, mclk_rate); return snd_snd_soc_codec_set_sysclk_set_sysclk(rtd->codec, ADAU1977_SYSCLK, ADAU1977_SYSCLK_SRC_MCLK, mclk_rate, SND_SOC_CLOCK_IN); } static struct snd_soc_ops eval_adau1977_ops = { .hw_params = bfin_eval_adau1x61_hw_params, }; static struct snd_soc_dai_link eval_adau1977_dai = { .name = "adau1977", .stream_name = "ADAU1977", .cpu_dai_name = "bfin-i2s.0", .codec_dai_name = "adau1977-hifi", .platform_name = "bfin-i2s-pcm-audio", .codec_name = "adau1977.0-0011", .ops = &eval_adau1977_ops, .init = eval_adau1977_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, };
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !