Part Number:TAS5805M
我司在RK3566-Android11上调试功放ICTAS5805M,发现该平台上只有老款的tas571x芯片的驱动,如附件:
tas571x.h
/* * TAS571x amplifier audio driver * * Copyright (C) 2015 Google, Inc. * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> * * TAS5721 support: * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> * * TAS5707 support: * Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.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. */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/stddef.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> #include <asm/unaligned.h> #include "tas571x.h" #define TAS571X_MAX_SUPPLIES 6 struct tas571x_chip { const char *const *supply_names; int num_supply_names; const struct snd_kcontrol_new *controls; int num_controls; const struct regmap_config *regmap_config; int vol_reg_size; }; struct tas571x_private { const struct tas571x_chip *chip; struct regmap *regmap; struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; struct clk *mclk; unsigned int format; struct gpio_desc *reset_gpio; struct gpio_desc *pdn_gpio; struct snd_soc_component_driver component_driver; }; static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) { switch (reg) { case TAS571X_MVOL_REG: case TAS571X_CH1_VOL_REG: case TAS571X_CH2_VOL_REG: return priv->chip->vol_reg_size; case TAS571X_INPUT_MUX_REG: case TAS571X_CH4_SRC_SELECT_REG: case TAS571X_PWM_MUX_REG: case TAS5717_CH1_RIGHT_CH_MIX_REG: case TAS5717_CH1_LEFT_CH_MIX_REG: case TAS5717_CH2_LEFT_CH_MIX_REG: case TAS5717_CH2_RIGHT_CH_MIX_REG: return 4; default: return 1; } } static int tas571x_reg_write(void *context, unsigned int reg,unsigned int value) { struct i2c_client *client = context; struct tas571x_private *priv = i2c_get_clientdata(client); unsigned int i, size; uint8_t buf[5]; int ret; size = tas571x_register_size(priv, reg); buf[0] = reg; for (i = size; i >= 1; --i) { buf[i] = value; value >>= 8; } ret = i2c_master_send(client, buf, size + 1); if (ret == size + 1) return 0; else if (ret < 0) return ret; else return -EIO; } static int tas571x_reg_read(void *context, unsigned int reg,unsigned int *value) { struct i2c_client *client = context; struct tas571x_private *priv = i2c_get_clientdata(client); uint8_t send_buf, recv_buf[4]; struct i2c_msg msgs[2]; unsigned int size; unsigned int i; int ret; size = tas571x_register_size(priv, reg); send_buf = reg; msgs[0].addr = client->addr; msgs[0].len = sizeof(send_buf); msgs[0].buf = &send_buf; msgs[0].flags = 0; msgs[1].addr = client->addr; msgs[1].len = size; msgs[1].buf = recv_buf; msgs[1].flags = I2C_M_RD; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; else if (ret != ARRAY_SIZE(msgs)) return -EIO; *value = 0; for (i = 0; i < size; i++) { *value <<= 8; *value |= recv_buf[i]; } return 0; } /* * register write for 8- and 20-byte registers */ static int tas571x_reg_write_multiword(struct i2c_client *client, unsigned int reg, const long values[], size_t len) { size_t i; uint8_t *buf, *p; int ret; size_t send_size = 1 + len * sizeof(uint32_t); buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA); if (!buf) return -ENOMEM; buf[0] = reg; for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t)) put_unaligned_be32(values[i], p); ret = i2c_master_send(client, buf, send_size); kfree(buf); if (ret == send_size) return 0; else if (ret < 0) return ret; else return -EIO; } /* * register read for 8- and 20-byte registers */ static int tas571x_reg_read_multiword(struct i2c_client *client, unsigned int reg, long values[], size_t len) { unsigned int i; uint8_t send_buf; uint8_t *recv_buf, *p; struct i2c_msg msgs[2]; unsigned int recv_size = len * sizeof(uint32_t); int ret; recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA); if (!recv_buf) return -ENOMEM; send_buf = reg; msgs[0].addr = client->addr; msgs[0].len = sizeof(send_buf); msgs[0].buf = &send_buf; msgs[0].flags = 0; msgs[1].addr = client->addr; msgs[1].len = recv_size; msgs[1].buf = recv_buf; msgs[1].flags = I2C_M_RD; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) goto err_ret; else if (ret != ARRAY_SIZE(msgs)) { ret = -EIO; goto err_ret; } for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t)) values[i] = get_unaligned_be32(p); err_ret: kfree(recv_buf); return ret; } /* * Integer array controls for setting biquad, mixer, DRC coefficients. * According to the datasheet each coefficient is effectively 26bits, * i.e. stored as 32bits, where bits [31:26] are ignored. * TI's TAS57xx Graphical Development Environment tool however produces * coefficients with more than 26 bits. For this reason we allow values * in the full 32-bits reange. * The coefficients are ordered as given in the TAS571x data sheet: * b0, b1, b2, a1, a2 */ static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) { int numcoef = kcontrol->private_value >> 16; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = numcoef; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0xffffffff; return 0; } static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct i2c_client *i2c = to_i2c_client(component->dev); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; return tas571x_reg_read_multiword(i2c, index, ucontrol->value.integer.value, numcoef); } static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct i2c_client *i2c = to_i2c_client(component->dev); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; return tas571x_reg_write_multiword(i2c, index, ucontrol->value.integer.value, numcoef); } static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) { struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component); priv->format = format; return 0; } static int tas571x_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct snd_soc_dai *dai) { struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component); u32 val; switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: val = 0x00; break; case SND_SOC_DAIFMT_I2S: val = 0x03; break; case SND_SOC_DAIFMT_LEFT_J: val = 0x06; break; default: return -EINVAL; } if (params_width(params) >= 24) val += 2; else if (params_width(params) >= 20) val += 1; return regmap_update_bits(priv->regmap, TAS571X_SDI_REG,TAS571X_SDI_FMT_MASK, val); } static int tas571x_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; u8 sysctl2; int ret; sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; ret = snd_soc_component_update_bits(component,TAS571X_SYS_CTRL_2_REG,TAS571X_SYS_CTRL_2_SDN_MASK,sysctl2); usleep_range(1000, 2000); return ret; } static int tas571x_set_bias_level(struct snd_soc_component *component,enum snd_soc_bias_level level) { struct tas571x_private *priv = snd_soc_component_get_drvdata(component); int ret; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { if (!IS_ERR(priv->mclk)) { ret = clk_prepare_enable(priv->mclk); if (ret) { dev_err(component->dev, "Failed to enable master clock: %d\n", ret); return ret; } } } break; case SND_SOC_BIAS_OFF: if (!IS_ERR(priv->mclk)) clk_disable_unprepare(priv->mclk); break; } return 0; } static const struct snd_soc_dai_ops tas571x_dai_ops = { .set_fmt = tas571x_set_dai_fmt, .hw_params = tas571x_hw_params, .digital_mute = tas571x_mute, }; #define BIQUAD_COEFS(xname, reg) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = tas571x_coefficient_info, \ .get = tas571x_coefficient_get,\ .put = tas571x_coefficient_put, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .private_value = reg | (5 << 16) } static const char *const tas5711_supply_names[] = { "AVDD", "DVDD", "PVDD_A", "PVDD_B", "PVDD_C", "PVDD_D", }; static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); static const struct snd_kcontrol_new tas5711_controls[] = { SOC_SINGLE_TLV("Master Volume",TAS571X_MVOL_REG,0, 0xff, 1, tas5711_volume_tlv), SOC_DOUBLE_R_TLV("Speaker Volume",TAS571X_CH1_VOL_REG,TAS571X_CH2_VOL_REG,0, 0xff, 1, tas5711_volume_tlv), SOC_DOUBLE("Speaker Switch",TAS571X_SOFT_MUTE_REG,TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,1, 1), }; static const struct regmap_range tas571x_readonly_regs_range[] = { regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), }; static const struct regmap_range tas571x_volatile_regs_range[] = { regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), }; static const struct regmap_access_table tas571x_write_regs = { .no_ranges = tas571x_readonly_regs_range, .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), }; static const struct regmap_access_table tas571x_volatile_regs = { .yes_ranges = tas571x_volatile_regs_range, .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), }; static const struct reg_default tas5711_reg_defaults[] = { { 0x04, 0x05 }, { 0x05, 0x40 }, { 0x06, 0x00 }, { 0x07, 0xff }, { 0x08, 0x30 }, { 0x09, 0x30 }, { 0x1b, 0x82 }, }; static const struct regmap_config tas5711_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = 0xff, .reg_read = tas571x_reg_read, .reg_write = tas571x_reg_write, .reg_defaults = tas5711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), .cache_type = REGCACHE_RBTREE, .wr_table = &tas571x_write_regs, .volatile_table = &tas571x_volatile_regs, }; static const struct tas571x_chip tas5711_chip = { .supply_names = tas5711_supply_names, .num_supply_names = ARRAY_SIZE(tas5711_supply_names), .controls = tas5711_controls, .num_controls = ARRAY_SIZE(tas5711_controls), .regmap_config = &tas5711_regmap_config, .vol_reg_size = 1, }; static const struct regmap_range tas5707_volatile_regs_range[] = { regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), regmap_reg_range(TAS5707_CH1_BQ0_REG, TAS5707_CH2_BQ6_REG), }; static const struct regmap_access_table tas5707_volatile_regs = { .yes_ranges = tas5707_volatile_regs_range, .n_yes_ranges = ARRAY_SIZE(tas5707_volatile_regs_range), }; static const DECLARE_TLV_DB_SCALE(tas5707_volume_tlv, -7900, 50, 1); static const char * const tas5707_volume_slew_step_txt[] = { "256", "512", "1024", "2048", }; static const unsigned int tas5707_volume_slew_step_values[] = { 3, 0, 1, 2, }; static SOC_VALUE_ENUM_SINGLE_DECL(tas5707_volume_slew_step_enum,TAS571X_VOL_CFG_REG, 0, 0x3,tas5707_volume_slew_step_txt,tas5707_volume_slew_step_values); static const struct snd_kcontrol_new tas5707_controls[] = { SOC_SINGLE_TLV("Master Volume",TAS571X_MVOL_REG,0, 0xff, 1, tas5707_volume_tlv), SOC_DOUBLE_R_TLV("Speaker Volume",TAS571X_CH1_VOL_REG,TAS571X_CH2_VOL_REG,0, 0xff, 1, tas5707_volume_tlv), SOC_DOUBLE("Speaker Switch",TAS571X_SOFT_MUTE_REG,TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,1, 1), SOC_ENUM("Slew Rate Steps", tas5707_volume_slew_step_enum), BIQUAD_COEFS("CH1 - Biquad 0", TAS5707_CH1_BQ0_REG), BIQUAD_COEFS("CH1 - Biquad 1", TAS5707_CH1_BQ1_REG), BIQUAD_COEFS("CH1 - Biquad 2", TAS5707_CH1_BQ2_REG), BIQUAD_COEFS("CH1 - Biquad 3", TAS5707_CH1_BQ3_REG), BIQUAD_COEFS("CH1 - Biquad 4", TAS5707_CH1_BQ4_REG), BIQUAD_COEFS("CH1 - Biquad 5", TAS5707_CH1_BQ5_REG), BIQUAD_COEFS("CH1 - Biquad 6", TAS5707_CH1_BQ6_REG), BIQUAD_COEFS("CH2 - Biquad 0", TAS5707_CH2_BQ0_REG), BIQUAD_COEFS("CH2 - Biquad 1", TAS5707_CH2_BQ1_REG), BIQUAD_COEFS("CH2 - Biquad 2", TAS5707_CH2_BQ2_REG), BIQUAD_COEFS("CH2 - Biquad 3", TAS5707_CH2_BQ3_REG), BIQUAD_COEFS("CH2 - Biquad 4", TAS5707_CH2_BQ4_REG), BIQUAD_COEFS("CH2 - Biquad 5", TAS5707_CH2_BQ5_REG), BIQUAD_COEFS("CH2 - Biquad 6", TAS5707_CH2_BQ6_REG), }; static const struct reg_default tas5707_reg_defaults[] = { {TAS571X_CLK_CTRL_REG, 0x6c}, {TAS571X_DEV_ID_REG, 0x70}, {TAS571X_ERR_STATUS_REG, 0x00}, {TAS571X_SYS_CTRL_1_REG, 0xa0}, {TAS571X_SDI_REG, 0x05}, {TAS571X_SYS_CTRL_2_REG, 0x40}, {TAS571X_SOFT_MUTE_REG, 0x00}, {TAS571X_MVOL_REG, 0xff}, {TAS571X_CH1_VOL_REG, 0x30}, {TAS571X_CH2_VOL_REG, 0x30}, {TAS571X_VOL_CFG_REG, 0x91}, {TAS571X_MODULATION_LIMIT_REG, 0x02}, {TAS571X_IC_DELAY_CH1_REG, 0xac}, {TAS571X_IC_DELAY_CH2_REG, 0x54}, {TAS571X_IC_DELAY_CH3_REG, 0xac}, {TAS571X_IC_DELAY_CH4_REG, 0x54}, {TAS571X_START_STOP_PERIOD_REG, 0x0f}, {TAS571X_OSC_TRIM_REG, 0x82}, {TAS571X_BKND_ERR_REG, 0x02}, {TAS571X_INPUT_MUX_REG, 0x17772}, {TAS571X_PWM_MUX_REG, 0x1021345}, }; static const struct regmap_config tas5707_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = 0xff, .reg_read = tas571x_reg_read, .reg_write = tas571x_reg_write, .reg_defaults = tas5707_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults), .cache_type = REGCACHE_RBTREE, .wr_table = &tas571x_write_regs, .volatile_table = &tas5707_volatile_regs, }; static const struct tas571x_chip tas5707_chip = { .supply_names = tas5711_supply_names, .num_supply_names = ARRAY_SIZE(tas5711_supply_names), .controls = tas5707_controls, .num_controls = ARRAY_SIZE(tas5707_controls), .regmap_config = &tas5707_regmap_config, .vol_reg_size = 1, }; static const char *const tas5717_supply_names[] = { "AVDD", "DVDD", "HPVDD", "PVDD_AB", "PVDD_CD", }; static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); static const struct snd_kcontrol_new tas5717_controls[] = { /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ SOC_SINGLE_TLV("Master Volume",TAS571X_MVOL_REG, 1, 0x1ff, 1,tas5717_volume_tlv), SOC_DOUBLE_R_TLV("Speaker Volume",TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,1, 0x1ff, 1, tas5717_volume_tlv), SOC_DOUBLE("Speaker Switch",TAS571X_SOFT_MUTE_REG,TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,1, 1), SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",TAS5717_CH1_LEFT_CH_MIX_REG,TAS5717_CH1_RIGHT_CH_MIX_REG,16, 0, 0x80, 0), SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",TAS5717_CH2_LEFT_CH_MIX_REG,TAS5717_CH2_RIGHT_CH_MIX_REG,16, 0, 0x80, 0), /** The biquads are named according to the register names.* Please note that TI's TAS57xx Graphical Development Environment* tool names them different.*/ BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG), BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG), BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG), BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG), BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG), BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG), BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG), BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG), BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG), BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG), BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG), BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG), BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG), BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG), BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG), BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG), BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG), BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG), BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG), BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG), BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG), BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG), BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG), BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG), BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG), BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG), BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG), BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG), }; static const struct reg_default tas5717_reg_defaults[] = { { 0x04, 0x05 }, { 0x05, 0x40 }, { 0x06, 0x00 }, { 0x07, 0x03ff }, { 0x08, 0x00c0 }, { 0x09, 0x00c0 }, { 0x1b, 0x82 }, { TAS5717_CH1_RIGHT_CH_MIX_REG, 0x0 }, { TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000}, { TAS5717_CH2_LEFT_CH_MIX_REG, 0x0 }, { TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000}, }; static const struct regmap_config tas5717_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = 0xff, .reg_read = tas571x_reg_read, .reg_write = tas571x_reg_write, .reg_defaults = tas5717_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), .cache_type = REGCACHE_RBTREE, .wr_table = &tas571x_write_regs, .volatile_table = &tas571x_volatile_regs, }; /* This entry is reused for tas5719 as the software interface is identical. */ static const struct tas571x_chip tas5717_chip = { .supply_names = tas5717_supply_names, .num_supply_names = ARRAY_SIZE(tas5717_supply_names), .controls = tas5717_controls, .num_controls = ARRAY_SIZE(tas5717_controls), .regmap_config = &tas5717_regmap_config, .vol_reg_size = 2, }; static const char *const tas5721_supply_names[] = { "AVDD", "DVDD", "DRVDD", "PVDD", }; static const struct snd_kcontrol_new tas5721_controls[] = { SOC_SINGLE_TLV("Master Volume",TAS571X_MVOL_REG,0, 0xff, 1, tas5711_volume_tlv), SOC_DOUBLE_R_TLV("Speaker Volume",TAS571X_CH1_VOL_REG,TAS571X_CH2_VOL_REG,0, 0xff, 1, tas5711_volume_tlv), SOC_DOUBLE("Speaker Switch",TAS571X_SOFT_MUTE_REG,TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,1, 1), }; static const struct reg_default tas5721_reg_defaults[] = { {TAS571X_CLK_CTRL_REG, 0x6c}, {TAS571X_DEV_ID_REG, 0x00}, {TAS571X_ERR_STATUS_REG, 0x00}, {TAS571X_SYS_CTRL_1_REG, 0xa0}, {TAS571X_SDI_REG, 0x05}, {TAS571X_SYS_CTRL_2_REG, 0x40}, {TAS571X_SOFT_MUTE_REG, 0x00}, {TAS571X_MVOL_REG, 0xff}, {TAS571X_CH1_VOL_REG, 0x30}, {TAS571X_CH2_VOL_REG, 0x30}, {TAS571X_CH3_VOL_REG, 0x30}, {TAS571X_VOL_CFG_REG, 0x91}, {TAS571X_MODULATION_LIMIT_REG, 0x02}, {TAS571X_IC_DELAY_CH1_REG, 0xac}, {TAS571X_IC_DELAY_CH2_REG, 0x54}, {TAS571X_IC_DELAY_CH3_REG, 0xac}, {TAS571X_IC_DELAY_CH4_REG, 0x54}, {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, {TAS571X_START_STOP_PERIOD_REG, 0x0f}, {TAS571X_OSC_TRIM_REG, 0x82}, {TAS571X_BKND_ERR_REG, 0x02}, {TAS571X_INPUT_MUX_REG, 0x17772}, {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, {TAS571X_PWM_MUX_REG, 0x1021345}, }; static const struct regmap_config tas5721_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = 0xff, .reg_read = tas571x_reg_read, .reg_write = tas571x_reg_write, .reg_defaults = tas5721_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), .cache_type = REGCACHE_RBTREE, .wr_table = &tas571x_write_regs, .volatile_table = &tas571x_volatile_regs, }; static const struct tas571x_chip tas5721_chip = { .supply_names = tas5721_supply_names, .num_supply_names = ARRAY_SIZE(tas5721_supply_names), .controls = tas5711_controls, .num_controls = ARRAY_SIZE(tas5711_controls), .regmap_config = &tas5721_regmap_config, .vol_reg_size = 1, }; static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("OUT_A"), SND_SOC_DAPM_OUTPUT("OUT_B"), SND_SOC_DAPM_OUTPUT("OUT_C"), SND_SOC_DAPM_OUTPUT("OUT_D"), }; static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { { "DACL", NULL, "Playback" }, { "DACR", NULL, "Playback" }, { "OUT_A", NULL, "DACL" }, { "OUT_B", NULL, "DACL" }, { "OUT_C", NULL, "DACR" }, { "OUT_D", NULL, "DACR" }, }; static const struct snd_soc_component_driver tas571x_component = { .set_bias_level = tas571x_set_bias_level, .dapm_widgets = tas571x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), .dapm_routes = tas571x_dapm_routes, .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), .use_pmdown_time = 1, .endianness = 1, .non_legacy_dai_naming = 1, }; static struct snd_soc_dai_driver tas571x_dai = { .name = "tas571x-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S32_LE |SNDRV_PCM_FMTBIT_S24_LE |SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tas571x_dai_ops, }; static const struct of_device_id tas571x_of_match[]; static int tas571x_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) { struct tas571x_private *priv; struct device *dev = &client->dev; const struct of_device_id *of_id; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; i2c_set_clientdata(client, priv); of_id = of_match_device(tas571x_of_match, dev); if (of_id) priv->chip = of_id->data; else priv->chip = (void *) id->driver_data; priv->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { dev_err(dev, "Failed to request mclk: %ld\n", PTR_ERR(priv->mclk)); return PTR_ERR(priv->mclk); } if (WARN_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES)) return -EINVAL; for (i = 0; i < priv->chip->num_supply_names; i++) priv->supplies[i].supply = priv->chip->supply_names[i]; ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names,priv->supplies); if (ret) { dev_err(dev, "Failed to get supplies: %d\n", ret); return ret; } ret = regulator_bulk_enable(priv->chip->num_supply_names,priv->supplies); if (ret) { dev_err(dev, "Failed to enable supplies: %d\n", ret); return ret; } priv->regmap = devm_regmap_init(dev, NULL, client, priv->chip->regmap_config); if (IS_ERR(priv->regmap)) { ret = PTR_ERR(priv->regmap); goto disable_regs; } priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); if (IS_ERR(priv->pdn_gpio)) { dev_err(dev, "error requesting pdn_gpio: %ld\n", PTR_ERR(priv->pdn_gpio)); return PTR_ERR(priv->pdn_gpio); } priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",GPIOD_OUT_HIGH); if (IS_ERR(priv->reset_gpio)) { dev_err(dev, "error requesting reset_gpio: %ld\n", PTR_ERR(priv->reset_gpio)); return PTR_ERR(priv->reset_gpio); } else if (priv->reset_gpio) { /* pulse the active low reset line for ~100us */ usleep_range(100, 200); gpiod_set_value(priv->reset_gpio, 0); usleep_range(13500, 20000); } ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); if (ret) goto disable_regs; usleep_range(50000, 60000); memcpy(&priv->component_driver, &tas571x_component, sizeof(priv->component_driver)); priv->component_driver.controls = priv->chip->controls; priv->component_driver.num_controls = priv->chip->num_controls; if (priv->chip->vol_reg_size == 2) { /** The master volume defaults to 0x3ff (mute), but we ignore* (zero) the LSB because the hardware step size is 0.125 dB* and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB.*/ ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); if (ret) goto disable_regs; } ret = devm_snd_soc_register_component(&client->dev,&priv->component_driver,&tas571x_dai, 1); if (ret) goto disable_regs; return ret; disable_regs: regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); return ret; } static int tas571x_i2c_remove(struct i2c_client *client) { struct tas571x_private *priv = i2c_get_clientdata(client); regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); return 0; } static const struct of_device_id tas571x_of_match[] = { { .compatible = "ti,tas5707", .data = &tas5707_chip, }, { .compatible = "ti,tas5711", .data = &tas5711_chip, }, { .compatible = "ti,tas5717", .data = &tas5717_chip, }, { .compatible = "ti,tas5719", .data = &tas5717_chip, }, { .compatible = "ti,tas5721", .data = &tas5721_chip, }, { } }; MODULE_DEVICE_TABLE(of, tas571x_of_match); static const struct i2c_device_id tas571x_i2c_id[] = { { "tas5707", (kernel_ulong_t) &tas5707_chip }, { "tas5711", (kernel_ulong_t) &tas5711_chip }, { "tas5717", (kernel_ulong_t) &tas5717_chip }, { "tas5719", (kernel_ulong_t) &tas5717_chip }, { "tas5721", (kernel_ulong_t) &tas5721_chip }, { } }; MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); static struct i2c_driver tas571x_i2c_driver = { .driver = { .name = "tas571x", .of_match_table = of_match_ptr(tas571x_of_match), }, .probe = tas571x_i2c_probe, .remove = tas571x_i2c_remove, .id_table = tas571x_i2c_id, }; module_i2c_driver(tas571x_i2c_driver); MODULE_DESCRIPTION("ASoC TAS571x driver"); MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); MODULE_LICENSE("GPL");
我们询问瑞芯微原厂,那边只有在老的kernel4.4版本的平台上调试过tas5805m,如附件:
3326.tas5805m.h
/* * sound/soc/codecs/amlogic/tas5805m.c * * Copyright (C) 2019 Amlogic, Inc. All rights reserved. * * 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. * */ #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> #include <sound/tas57xx.h> //#include <sound/aml_gpio_consumer.h>//wenb #include <linux/gpio.h>//wenb #include <linux/of_gpio.h> #include <linux/input.h>//wenb //#include <string> //#include "tas5805.h" //#include "tas5805_2e.h" #include "tas5805new.h" //#include "0710TAS5805M_Tuning_48K_1SPW.h" #include "0724_C_S-6064_TAS5805M_Tuning_48K_1SPW_2E.h" #include "0724_C_S-6064_TAS5805M_Tuning_48K_1SPW_2E_4W.h" #include "0724_LeRe_S-6064_TAS5805M_Tuning_48K_1SPW_2F.h" #include "0724_LR_S-6064_TAS5805M_Tuning_48K_1SPW_2C.h" #include "0724_LsRs_S-6064_TAS5805M_Tuning_48K_1SPW_2D.h" #define CONTROL_EACH_CHANNEL 0 #define CONFIG_PA_PM #define DEV_NAME "tas5805m" #define DET_GPIO 33 #define DET_NAME"det_gpio" int det_irq = -1; static int soundbar_connect = 0; static struct input_dev *tas_input_dev = NULL; #ifdef CONFIG_HAS_EARLYSUSPEND #include <linux/earlysuspend.h> static void tas5805m_early_suspend(struct early_suspend *h); static void tas5805m_late_resume(struct early_suspend *h); #endif static struct delayed_work det_connect_worker; #define tas5805m_RATES (SNDRV_PCM_RATE_8000 | \SNDRV_PCM_RATE_11025 | \SNDRV_PCM_RATE_16000 | \SNDRV_PCM_RATE_22050 | \SNDRV_PCM_RATE_32000 | \SNDRV_PCM_RATE_44100 | \SNDRV_PCM_RATE_48000) #define tas5805m_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \SNDRV_PCM_FMTBIT_S32_LE) #define DEFAULT_VOLUME (180) enum BITSIZE_MODE { BITSIZE_MODE_16BITS = 0, BITSIZE_MODE_20BITS = 1, BITSIZE_MODE_24BITS = 2, BITSIZE_MODE_32BITS = 3, }; /* codec private data */ struct tas5805m_priv { struct i2c_client *i2c; struct regmap *regmap; struct snd_soc_codec *codec; struct tas57xx_platform_data *pdata; //struct work_struct work; struct workqueue_struct *workqueue; struct delayed_work dwork; /*Platform provided EQ configuration */ int num_eq_conf_texts; const char **eq_conf_texts; int eq_cfg; struct soc_enum eq_conf_enum; #if (CONTROL_EACH_CHANNEL == 1) unsigned char Ch1_vol; unsigned char Ch2_vol; #endif unsigned char master_vol; unsigned char mute; //0:unmuted 1:muted unsigned int mclk; unsigned int EQ_enum_value; unsigned int DRC_enum_value; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; #endif }; static int tas5805m_master_vol_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) { uinfo->type= SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ| SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0xff; uinfo->value.integer.step = 1; return 0; } #if (CONTROL_EACH_CHANNEL == 1) static int tas5805m_ch1_vol_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) { uinfo->type= SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ| SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0xff; uinfo->value.integer.step = 1; return 0; } static int tas5805m_ch2_vol_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) { uinfo->type= SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ| SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0xff; uinfo->value.integer.step = 1; return 0; } #endif static int tas5805m_mute_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) { uinfo->type= SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ| SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; uinfo->value.integer.step = 1; return 0; } static int tas5805m_master_vol_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = tas5805m->master_vol; return 0; } #if (CONTROL_EACH_CHANNEL == 1) static int tas5805m_ch1_vol_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = tas5805m->Ch1_vol; return 0; } static int tas5805m_ch2_vol_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = tas5805m->Ch2_vol; return 0; } #endif static int tas5805m_mute_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = tas5805m->mute; return 0; } static void tas5805m_set_volume(struct snd_soc_codec *codec,int value, int channel) { #if 1 struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); unsigned char buf[8] = {0}; int count = 0; unsigned char vol_set = 0; int ret = 0; if (value < 0) value = 0; if (value > 255) value = 255; vol_set = 255 - value; buf[0] = 0x00, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x7F, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x00, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x4C, buf[1] = vol_set, count = 2; ret = i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x4D, buf[1] = vol_set, count = 2; ret = i2c_master_send(tas5805m->i2c, buf, count); if(count != ret) { pr_err("%s %d i2c_master_send failed! ret:%d\n", __func__, __LINE__, ret); } else { pr_info("%s %d slave:0x%x vol_set:%d\n", __func__, __LINE__, tas5805m->i2c->addr, vol_set); } #endif } static void tas5805m_set_mute(struct snd_soc_codec *codec, int value) { #if 1 struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); unsigned char buf[8] = {0}; int count = 0; int ret = 0; buf[0] = 0x00, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x7F, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x00, buf[1] = 0x00, count = 2; i2c_master_send(tas5805m->i2c, buf, count); buf[0] = 0x03; buf[1] = (1 == value) ? 0x0B : 0x03; count = 2; ret = i2c_master_send(tas5805m->i2c, buf, count); if(count != ret) { pr_err("%s %d i2c_master_send failed! ret:%d\n", __func__, __LINE__, ret); } else { pr_info("%s %d slave:0x%x mute:%d\n", __func__, __LINE__, tas5805m->i2c->addr, value); } #endif } static int tas5805m_master_vol_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); int value; value = ucontrol->value.integer.value[0]; tas5805m->master_vol = value; pr_info("%s %d master_vol: %d\n", __func__, __LINE__, tas5805m->master_vol); tas5805m_set_volume(codec, value, 0xFF); return 0; } #if (CONTROL_EACH_CHANNEL == 1) static int tas5805m_ch1_vol_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); int value = ucontrol->value.integer.value[0]; tas5805m->Ch1_vol = value; pr_info("%s %d Ch1_vol: %d\n", __func__, __LINE__, tas5805m->Ch1_vol); tas5805m_set_volume(codec, value, 0); return 0; } static int tas5805m_ch2_vol_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); int value = ucontrol->value.integer.value[0]; tas5805m->Ch2_vol = value; pr_info("%s %d Ch2_vol: %d\n", __func__, __LINE__, tas5805m->Ch2_vol); tas5805m_set_volume(codec, value, 1); return 0; } #endif static int tas5805m_mute_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); int value = ucontrol->value.integer.value[0]; tas5805m->mute = value & 0x1; pr_info("%s %d mute: %d\n", __func__, __LINE__, tas5805m->mute); tas5805m_set_mute(codec, value); return 0; } static const struct snd_kcontrol_new tas5805m_snd_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Volume", .info = tas5805m_master_vol_info, .get= tas5805m_master_vol_get, .put= tas5805m_master_vol_put, }, #if (CONTROL_EACH_CHANNEL == 1) { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Ch1 Volume", .info = tas5805m_ch1_vol_info, .get= tas5805m_ch1_vol_get, .put= tas5805m_ch1_vol_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Ch2 Volume", .info = tas5805m_ch2_vol_info, .get= tas5805m_ch2_vol_get, .put= tas5805m_ch2_vol_put, }, #endif { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mute Control", .info = tas5805m_mute_info, .get= tas5805m_mute_get, .put= tas5805m_mute_put, } }; static int tas5805m_set_dai_sysclk(struct snd_soc_dai *codec_dai,int clk_id, unsigned int freq, int dir) { return 0; } static int tas5805m_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: break; default: return 0;//-EINVAL; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_LEFT_J: break; default: return 0;//-EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_NB_IF: break; default: return 0;//-EINVAL; } return 0; } static int tas5805m_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { unsigned int rate; rate = params_rate(params); pr_debug("rate: %u\n", rate); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_BE: pr_debug("24bit\n"); /* fall through */ case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S20_3LE: case SNDRV_PCM_FORMAT_S20_3BE: pr_debug("20bit\n"); break; case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_BE: pr_debug("16bit\n"); break; default: return -EINVAL; } return 0; } static int tas5805m_set_bias_level(struct snd_soc_codec *codec,enum snd_soc_bias_level level) { pr_debug("level = %d\n", level); switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: /* Full power on */ break; case SND_SOC_BIAS_STANDBY: break; case SND_SOC_BIAS_OFF: /* The chip runs through the power down sequence for us. */ break; } codec->component.dapm.bias_level = level; return 0; } static const struct snd_soc_dai_ops tas5805m_dai_ops = { .hw_params = tas5805m_hw_params, .set_sysclk = tas5805m_set_dai_sysclk, .set_fmt = tas5805m_set_dai_fmt, }; static struct snd_soc_dai_driver tas5805m_dai = { .name = DEV_NAME, .playback = { .stream_name = "HIFI Playback", .channels_min = 2, .channels_max = 8, .rates = tas5805m_RATES, .formats = tas5805m_FORMATS, }, .ops = &tas5805m_dai_ops, }; static int reset_tas5805m_GPIO(struct snd_soc_codec *codec) { struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); struct tas57xx_platform_data *pdata = tas5805m->pdata; int ret = 0; if (pdata->reset_pin < 0) return 0; ret = devm_gpio_request_one(codec->dev, pdata->reset_pin,GPIOF_OUT_INIT_LOW, "tas5805m-reset-pin"); if (ret < 0) { pr_err("failed!!! devm_gpio_request_one = %d!\n", ret); return -1; } gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_HIGH); udelay(1000); gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_LOW); udelay(1000); gpio_direction_output(pdata->reset_pin, GPIOF_OUT_INIT_HIGH); msleep(20); pr_err("%s %d gpio reset done!\n", __func__, __LINE__); return 0; } /*static int power_tas5805m_GPIO(struct snd_soc_codec *codec) { int ret; ret = gpio_request(152, "5140_reset_gpio"); if (ret){ pr_err("%s %d gpio power done!\n", __func__, __LINE__); } ret = gpio_direction_output(152,0); if (ret){ pr_err("%s %d gpio power done!\n", __func__, __LINE__); } else{ gpio_set_value(152,0); pr_err("%s %d gpio power done!\n", __func__, __LINE__); } pr_err("%s %d gpio power done!\n", __func__, __LINE__); return 0; }*/ static void tas5805m_reg_init(struct tas5805m_priv *tas5805m) { struct snd_soc_codec *codec; struct i2c_client *i2c; #if 1 int i = 0, total_w = 0; int power_val; int value_count; unsigned char buf[64] = {0}; int write_count = 0; // struct reg_default temp_init_sequence[] = {0}; #endif codec = tas5805m->codec; //reset_tas5805m_GPIO(codec); //power_tas5805m_GPIO(codec); //init register tas5805m = snd_soc_codec_get_drvdata(codec); i2c = tas5805m->i2c; pr_info("%s %d slave_addr:0x%x i2c_flag = 0x%x\n", __func__, __LINE__, i2c->addr, i2c->flags); #if 1power_val = gpio_get_value(DET_GPIO);if(power_val == 1){ if(i2c->addr == 0x2c) { value_count = ARRAY_SIZE(tas5805m_init_sequence_new2c); }else if(i2c->addr == 0x2d){ value_count = ARRAY_SIZE(tas5805m_init_sequence_new2d); }else if(i2c->addr == 0x2e){ value_count = ARRAY_SIZE(tas5805m_init_sequence_new2e); }else if(i2c->addr == 0x2f){ value_count = ARRAY_SIZE(tas5805m_init_sequence_new2f); }else{ value_count = ARRAY_SIZE(tas5805m_init_sequence_new); } for (i = 0; i < value_count; i++) { write_count = 2; if(i2c->addr == 0x2c) { buf[0] = tas5805m_init_sequence_new2c[i].reg; buf[1] = tas5805m_init_sequence_new2c[i].def;}else if(i2c->addr == 0x2d){ buf[0] = tas5805m_init_sequence_new2d[i].reg; buf[1] = tas5805m_init_sequence_new2d[i].def; }else if(i2c->addr == 0x2e){ buf[0] = tas5805m_init_sequence_new2e[i].reg; buf[1] = tas5805m_init_sequence_new2e[i].def; }else if(i2c->addr == 0x2f){ buf[0] = tas5805m_init_sequence_new2f[i].reg; buf[1] = tas5805m_init_sequence_new2f[i].def; } else{ buf[0] = tas5805m_init_sequence_new[i].reg; buf[1] = tas5805m_init_sequence_new[i].def; } if(buf[0] == CFG_META_DELAY){ mdelay(20); }else{ if (write_count != i2c_master_send(i2c, buf, write_count)) { pr_err("%s %d !!!!! i2c_master_send error !!!!!\n", __func__, __LINE__); mdelay(1); break; } } total_w++; } pr_info("%s %d total_w == %d\n", __func__, __LINE__, total_w); #endif // tas5805m_set_mute(codec, 0); // tas5805m->mute = 0; // tas5805m_set_volume(codec, 255, 0xFF);//DEFAULT_VOLUME // tas5805m->master_vol = 255; //DEFAULT_VOLUME pr_info("%s : %d power_val = %d=======DC supply power============>\n", __func__, __LINE__,power_val);}else{ if(i2c->addr == 0x2e) value_count = ARRAY_SIZE(tas5805m_init_sequence_new2e_4W); for (i = 0; i < value_count; i++) { write_count = 2; if(i2c->addr == 0x2e){ buf[0] = tas5805m_init_sequence_new2e_4W[i].reg; buf[1] = tas5805m_init_sequence_new2e_4W[i].def; } if(buf[0] == CFG_META_DELAY){ mdelay(20); }else{ if (write_count != i2c_master_send(i2c, buf, write_count)) { pr_err("%s %d !!!!! i2c_master_send error !!!!!\n",__func__, __LINE__); mdelay(1); break; } } total_w++; } pr_info("%s %d total_w == %d\n", __func__, __LINE__, total_w); pr_info("%s : %d power_val = %d=======POE supply power============>\n", __func__, __LINE__,power_val); //tas5805m_set_volume(codec, 0xBA, 0xFF);} #if (CONTROL_EACH_CHANNEL == 1) tas5805m_set_volume(codec, DEFAULT_VOLUME, 0); tas5805m->Ch1_vol = DEFAULT_VOLUME; tas5805m_set_volume(codec, DEFAULT_VOLUME, 1); tas5805m->Ch2_vol = DEFAULT_VOLUME; #endif } static struct tas5805m_priv *TAS5805M; static unsigned char init_flag = 0; static void tas5805_task(struct work_struct *work) { if(init_flag == 0){// mdelay(500); //TAS5805M->i2c->addr = 0x2d ; //tas5805m_reg_init(TAS5805M); TAS5805M->i2c->addr = 0x2e ; tas5805m_reg_init(TAS5805M); //TAS5805M->i2c->addr = 0x2f ; //tas5805m_reg_init(TAS5805M); //TAS5805M->i2c->addr = 0x2c ; //tas5805m_reg_init(TAS5805M); //TAS5805M->i2c->addr = 0x2d ; //tas5805m_reg_init(TAS5805M); //init_flag = 1; /*mdelay(5); TAS5805M->i2c->addr = 0x2d ; tas5805m_reg_init(TAS5805M); TAS5805M->i2c->addr = 0x2e ; tas5805m_reg_init(TAS5805M); TAS5805M->i2c->addr = 0x2f ; tas5805m_reg_init(TAS5805M); TAS5805M->i2c->addr = 0x2c ; tas5805m_reg_init(TAS5805M); TAS5805M->i2c->addr = 0x2d ; tas5805m_reg_init(TAS5805M);*/ init_flag = 1; } } static int tas5805m_probe(struct snd_soc_codec *codec) { struct tas5805m_priv *tas5805m; #ifdef CONFIG_HAS_EARLYSUSPEND tas5805m->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; tas5805m->early_suspend.suspend = tas5805m_early_suspend; tas5805m->early_suspend.resume = tas5805m_late_resume; tas5805m->early_suspend.param = codec; register_early_suspend(&(tas5805m->early_suspend)); #endif tas5805m = snd_soc_codec_get_drvdata(codec); tas5805m->codec = codec; reset_tas5805m_GPIO(codec); //power_tas5805m_GPIO(codec); //tas5805m_reg_init(tas5805m); //wenb add TAS5805M = tas5805m; //INIT_WORK(&tas5805m->work, tas5805m_reg_init); //schedule_work(&tas5805m->work); tas5805m->workqueue = create_singlethread_workqueue("tas5805_workqueue"); INIT_DELAYED_WORK(&tas5805m->dwork,tas5805_task); return 0; } static int tas5805m_remove(struct snd_soc_codec *codec) { struct tas5805m_priv *tas5805m; #ifdef CONFIG_HAS_EARLYSUSPEND struct tas5805m_priv *tas5805m = snd_soc_codec_get_drvdata(codec); unregister_early_suspend(&(tas5805m->early_suspend)); #endif tas5805m = snd_soc_codec_get_drvdata(codec); //cancel_work_sync(&tas5805m->work); return 0; } #ifdef CONFIG_PM static int tas5805m_suspend(struct snd_soc_codec *codec) { struct tas57xx_platform_data *pdata = dev_get_platdata(codec->dev); dev_info(codec->dev, "tas5805m_suspend!\n"); if (pdata && pdata->suspend_func) pdata->suspend_func(); return 0; } static int tas5805m_resume(struct snd_soc_codec *codec) { struct tas57xx_platform_data *pdata = dev_get_platdata(codec->dev); struct tas5805m_priv *tas5805m; dev_info(codec->dev, "tas5805m_resume!\n"); if (pdata && pdata->resume_func) pdata->resume_func(); tas5805m = snd_soc_codec_get_drvdata(codec); tas5805m->codec = codec; //tas5805m_reg_init(tas5805m); //INIT_WORK(&tas5805m->work, tas5805m_reg_init); //schedule_work(&tas5805m->work); return 0; } #endif #ifdef CONFIG_HAS_EARLYSUSPEND static void tas5805m_early_suspend(struct early_suspend *h) { } static void tas5805m_late_resume(struct early_suspend *h) { } #endif static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "HIFI Playback", SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_codec_driver soc_codec_dev_tas5805m = { .probe = tas5805m_probe, .remove = tas5805m_remove, #ifdef CONFIG_PM .suspend = tas5805m_suspend, .resume = tas5805m_resume, #endif .set_bias_level = tas5805m_set_bias_level, .component_driver = { .controls = tas5805m_snd_controls, .num_controls = ARRAY_SIZE(tas5805m_snd_controls), .dapm_widgets = tas5805m_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tas5805m_dapm_widgets), } }; /* *static const struct regmap_config tas5805m_regmap = { * .reg_bits = 8, * .val_bits = 8, * * .max_register = tas5805m_REGISTER_COUNT, * .reg_defaults = tas5805m_reg_defaults, * .num_reg_defaults = * sizeof(tas5805m_reg_defaults)/sizeof(tas5805m_reg_defaults[0]), * .cache_type = REGCACHE_RBTREE, *}; */ static int tas5805m_parse_dts(struct tas5805m_priv *tas5805m,struct device_node *np) { int reset_pin = -1; reset_pin = of_get_named_gpio(np, "reset_pin", 0); if (reset_pin < 0) { pr_err("%s fail to get reset pin from dts!\n", __func__); } else { pr_debug("%s pdata->reset_pin = %d!\n", __func__, reset_pin); } tas5805m->pdata->reset_pin = reset_pin; return 0; } #if 1 static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,char *buf) /* buf是返回给用户空间的值 */ { //tas5805m_reg_init(TAS5805M); queue_delayed_work(TAS5805M->workqueue,&TAS5805M->dwork,3); return 0; } static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) { init_flag = 0;return 0; } static struct kobj_attribute foo_attribute = __ATTR(mpu6050_accelerationd, 0664, foo_show, foo_store); static struct attribute *attrs[] = {&foo_attribute.attr,NULL, /* 需要用NULL来表示属性列表的结束 */ }; static struct attribute_group attr_group = {.attrs = attrs, }; #endif //�жϴ������ static irqreturn_t dev_interrupt(int irq, void *p){ schedule_delayed_work(&det_connect_worker,msecs_to_jiffies(400)); return IRQ_RETVAL(IRQ_HANDLED);} static void det_worker(struct work_struct *work) { int val1 = 0; int val2 = 0; int val3 = 0; int val4 = 1; int val5 = 1; int val6 = 1; if(!soundbar_connect){ val1= gpio_get_value(DET_GPIO); udelay(150); val2= gpio_get_value(DET_GPIO); udelay(300); val3= gpio_get_value(DET_GPIO); if(val1 == val2 && val2 == val3 && val3 == 1){ //input_report_key(tas_input_dev, KEY_F3, 1); //input_report_key(tas_input_dev, KEY_F3, 0); //input_sync(tas_input_dev); //struct snd_soc_codec *codec = TAS5805M->codec; //TAS5805M->i2c->addr = 0x2e ; //tas5805m_reg_init(TAS5805M); //����soundbar���� soundbar_connect = 1; pr_info("%s : %d =======tas5805_interrupt in KEY_F3============>\n", __func__, __LINE__); }else{ pr_info("%s : %d =======tas5805_interrupt ignore event!============>\n", __func__, __LINE__);} }else if(soundbar_connect){ val4= gpio_get_value(DET_GPIO); udelay(100); val5= gpio_get_value(DET_GPIO); udelay(300); val6= gpio_get_value(DET_GPIO); if((val4 == val5 && val5 == val6 && val6 == 0)){ //input_report_key(tas_input_dev, KEY_F4, 1); //input_report_key(tas_input_dev, KEY_F4, 0); //input_sync(tas_input_dev); //struct snd_soc_codec *codec = TAS5805M->codec; TAS5805M->i2c->addr = 0x2e ; tas5805m_reg_init(TAS5805M); //����soundbar���� soundbar_connect = 0; pr_info("%s : %d =======tas5805_interrupt in KEY_F4============>\n", __func__, __LINE__); }else{ pr_info("%s : %d =======tas5805_interrupt ignore event!============>\n", __func__, __LINE__);} } } static int tas5805m_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id) { struct tas5805m_priv *tas5805m; struct tas57xx_platform_data *pdata; int ret,err = -1,irq = -1; const char *codec_name = NULL;//struct kobject *example_kobj; int retval; unsigned long irqflags; tas5805m = devm_kzalloc(&i2c->dev,sizeof(struct tas5805m_priv), GFP_KERNEL); if (!tas5805m) return -ENOMEM; #if 1//example_kobj = kobject_create_and_add("mpu6050_i2c", kernel_kobj); //if (!example_kobj) //return -ENOMEM;/* Create the files associated with this kobject */retval = sysfs_create_group(&i2c->dev.kobj, &attr_group);//if (retval)// kobject_put(example_kobj); #endif pr_info("%s %d i2c:%p i2c->addr=0x%x\n", __func__, __LINE__, i2c, i2c->addr); /** tas5805m->regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap);* if (IS_ERR(tas5805m->regmap)) {* ret = PTR_ERR(tas5805m->regmap);* dev_err(&i2c->dev,* "Failed to allocate register map: %d\n", ret);* return ret;* }*/ pdata = devm_kzalloc(&i2c->dev,sizeof(struct tas57xx_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; tas5805m->pdata = pdata; tas5805m_parse_dts(tas5805m, i2c->dev.of_node); if (of_property_read_string(i2c->dev.of_node, "codec_name", &codec_name)) { pr_info("no codec name\n"); ret = -1; } pr_info("codec_name=%s\n", codec_name); if (codec_name) { dev_set_name(&i2c->dev, "%s", codec_name); } tas5805m->i2c = i2c; i2c_set_clientdata(i2c, tas5805m); ret = snd_soc_register_codec(&i2c->dev,&soc_codec_dev_tas5805m, &tas5805m_dai, 1); if (ret != 0) dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret); tas_input_dev = input_allocate_device();if (tas_input_dev == NULL) {printk(KERN_ERR "notenough memory\n");err= - ENOMEM;}tas_input_dev->name = "tas_gpio";set_bit(EV_KEY, tas_input_dev->evbit);set_bit(KEY_F3, tas_input_dev->keybit); set_bit(KEY_F4, tas_input_dev->keybit);err = input_register_device(tas_input_dev);if(err) {printk(KERN_ERR "failedto register device\n");} err = gpio_request(DET_GPIO, "det_gpio"); if(err < 0){ printk("request gpio failed...\n");} err = gpio_direction_input(DET_GPIO); if (err < 0) { printk("gpio_direction_input gpio failed...\n");}det_irq = (irq = gpio_to_irq(DET_GPIO));if (irq < 0) {err = irq;printk("Unable to get irq number for GPIO\n");}irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;if (request_irq(irq, dev_interrupt, irqflags, DET_NAME, NULL)) {printk("cannotallocate irq\n");} INIT_DELAYED_WORK(&det_connect_worker,det_worker); gpio_export(DET_GPIO,1); return ret; } static int tas5805m_i2c_remove(struct i2c_client *client) { //kobject_put(example_kobj); snd_soc_unregister_codec(&client->dev); return 0; } static const struct i2c_device_id tas5805m_i2c_id[] = { { DEV_NAME, 0 }, {} }; static const struct of_device_id tas5805m_of_id[] = { {.compatible = "ti,tas5805m",}, { /* senitel */ } }; MODULE_DEVICE_TABLE(of, tas5805m_of_id); static struct i2c_driver tas5805m_i2c_driver = { .driver = { .name = DEV_NAME, .of_match_table = tas5805m_of_id, .owner = THIS_MODULE, }, .probe = tas5805m_i2c_probe, .remove = tas5805m_i2c_remove, .id_table = tas5805m_i2c_id, }; module_i2c_driver(tas5805m_i2c_driver); MODULE_DESCRIPTION("ASoC tas5805m driver"); MODULE_AUTHOR("AML MM team"); MODULE_LICENSE("GPL");
该驱动因为版本问题,代码中的snd_soc_codec方法被弃用,我们尝试修改代码,但是修改依旧有很多函数报错无法编译通过拿来使用。
而后我们在github上找了一份tas5805m的驱动,如下:
TAS5805m-main.zip
这份驱动可以编译通过,我们将TAS5805M通过I2C挂着之后,驱动proble成功,有如下打印:
[ 2.269386] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe start <<<<<<<<<<<<<<<<
[ 2.269454] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe1 <<<<<<<<<<<<<<<<
[ 2.269535] tas5805m 2-002e: Linked as a consumer to regulator.2
[ 2.269560] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe2 <<<<<<<<<<<<<<<<
[ 2.269596] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3 <<<<<<<<<<<<<<<<
[ 2.269616] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-1 <<<<<<<<<<<<<<<<
[ 2.269631] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-2 <<<<<<<<<<<<<<<<
[ 2.269642] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-3 <<<<<<<<<<<<<<<<
[ 2.269724] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-4 <<<<<<<<<<<<<<<<
[ 2.269737] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-5 <<<<<<<<<<<<<<<<
[ 2.269750] tas5805m 2-002e: firmware not found, using minimal 88.2kHz config for PVDD=24V
[ 2.269765] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-3 <<<<<<<<<<<<<<<<
[ 2.269808] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-4 <<<<<<<<<<<<<<<<
[ 2.269821] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-5 <<<<<<<<<<<<<<<<
[ 2.269829] tas5805m 2-002e: firmware not found, using minimal 96kHz config for PVDD=24V
[ 2.269837] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-3 <<<<<<<<<<<<<<<<
[ 2.269875] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-4 <<<<<<<<<<<<<<<<
[ 2.269889] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe3-5 <<<<<<<<<<<<<<<<
[ 2.269901] tas5805m 2-002e: firmware not found, using minimal 192kHz config for PVDD=24V
[ 2.269916] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe4 <<<<<<<<<<<<<<<<
[ 2.435031] tas5805m:>>>>>>>>>>>> snd_soc_register_component ret: 0
[ 2.435064] tas5805m:>>>>>>>>>>>> tas5805m_i2c_probe end <<<<<<<<<<<<<<<<
通过命令查看TAS5805M在RK3566上被成功注册为声卡。如下:
console:/ # cat /proc/asound/cards
0 [rockchiphdmi ]: rockchip_hdmi – rockchip,hdmi
rockchip,hdmi
1 [rockchiptas5805]: rockchip_tas580 – rockchip,tas5805m
rockchip,tas5805m
2 [ROCKCHIPSPDIF ]: ROCKCHIP_SPDIF – ROCKCHIP,SPDIF
ROCKCHIP,SPDIF
但是喇叭并没有声音出来,我们量了TAS5805M的供电都正常。硬件设计如下:
想请问是否有在RK3566-Android11平台上使用的TAS5805M驱动,或者能否仿照老版本tas5805m的驱动帮忙修改一份能在RK3566上使用的TAS5805M的驱动出来。
我们因为对音频驱动不是很熟悉,尝试修改无法解决,能否帮忙支持一下。
Amy Luo:
您好,
很抱歉没有RK3566-Android11平台上使用的TAS5805M驱动,建议您将问题发布在E2E英文技术论坛上,看那边的工程师是否可以提供帮助。英文论坛对应子论坛链接:
https://e2e.ti.com/support/audio-group/audio/f/audio-forum