| /* |
| * aw8896.c aw8896 codec module |
| * |
| * Version: v1.0.7 |
| * |
| * Copyright (c) 2017 AWINIC Technology CO., LTD |
| * |
| * Author: Nick Li <liweilei@awinic.com.cn> |
| * |
| * 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/module.h> |
| #include <linux/i2c.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/soc.h> |
| #include <linux/of_gpio.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/firmware.h> |
| #include <linux/i2c.h> |
| #include <linux/debugfs.h> |
| #include <linux/version.h> |
| #include <linux/input.h> |
| #include <sound/tlv.h> |
| #include "aw8896.h" |
| #include "aw8896_reg.h" |
| |
| #define AW8896_I2C_NAME "aw889x_smartpa" |
| #define AW8896_VERSION "v1.0.7" |
| #define AW8896_RATES SNDRV_PCM_RATE_8000_48000 |
| #define AW8896_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ |
| SNDRV_PCM_FMTBIT_S24_LE | \ |
| SNDRV_PCM_FMTBIT_S32_LE) |
| |
| #define AW_I2C_RETRIES 5 |
| #define AW_I2C_RETRY_DELAY 5 /* 5ms */ |
| #define AW_READ_CHIPID_RETRIES 5 |
| #define AW_READ_CHIPID_RETRY_DELAY 5 |
| #define AW8896_MAX_DSP_START_TRY_COUNT 10 |
| #define DT_MAX_PROP_SIZE 80 |
| |
| static int aw8896_spk_control; |
| static int aw8896_rcv_control; |
| |
| #define AW8896_FW_NAME_MAX 64 |
| #define AW8896_MAX_FIRMWARE_LOAD_CNT 20 |
| static char *aw8896_reg_name = "aw8896_reg.bin"; |
| static char aw8896_fw_name[][AW8896_FW_NAME_MAX] = { |
| {"aw8896_fw_d.bin"}, |
| {"aw8896_fw_e.bin"}, |
| }; |
| static char *aw8896_cfg_name = "aw8896_cfg.bin"; |
| |
| static int aw8896_i2c_write(struct aw8896 *aw8896, |
| unsigned char reg_addr, unsigned int reg_data) |
| { |
| int ret = -1; |
| unsigned char cnt = 0; |
| |
| while (cnt < AW_I2C_RETRIES) { |
| ret = regmap_write(aw8896->regmap, reg_addr, reg_data); |
| if (ret < 0) |
| pr_err("%s: regmap_write cnt=%d error=%d\n", __func__, |
| cnt, ret); |
| else |
| break; |
| cnt++; |
| } |
| |
| return ret; |
| } |
| |
| static int aw8896_i2c_read(struct aw8896 *aw8896, |
| unsigned char reg_addr, unsigned int *reg_data) |
| { |
| int ret = -1; |
| unsigned char cnt = 0; |
| |
| while (cnt < AW_I2C_RETRIES) { |
| ret = regmap_read(aw8896->regmap, reg_addr, reg_data); |
| if (ret < 0) |
| pr_err("%s: regmap_read cnt=%d error=%d\n", __func__, |
| cnt, ret); |
| else |
| break; |
| cnt++; |
| } |
| |
| return ret; |
| } |
| |
| static int aw8896_i2c_writes(struct aw8896 *aw8896, |
| unsigned char reg_addr, unsigned char *buf, unsigned int len) |
| { |
| int ret = -1; |
| unsigned char *data = NULL; |
| |
| data = kmalloc(len + 1, GFP_KERNEL); |
| if (data == NULL) |
| return -ENOMEM; |
| |
| data[0] = reg_addr; |
| memcpy(&data[1], buf, len); |
| |
| ret = i2c_master_send(aw8896->i2c, data, len + 1); |
| if (ret < 0) |
| pr_err("%s: i2c master send error\n", __func__); |
| |
| kfree(data); |
| |
| return ret; |
| } |
| |
| static void aw8896_run_mute(struct aw8896 *aw8896, bool mute) |
| { |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_PWMCTRL, ®_val); |
| if (mute) |
| reg_val |= AW8896_BIT_PWMCTRL_HMUTE; |
| else |
| reg_val &= (~AW8896_BIT_PWMCTRL_HMUTE); |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_PWMCTRL, reg_val); |
| } |
| |
| static void aw8896_run_pwd(struct aw8896 *aw8896, bool pwd) |
| { |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val); |
| if (pwd) |
| reg_val |= AW8896_BIT_SYSCTRL_PWDN; |
| else |
| reg_val &= (~AW8896_BIT_SYSCTRL_PWDN); |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val); |
| } |
| |
| static void aw8896_dsp_enable(struct aw8896 *aw8896, bool dsp) |
| { |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val); |
| if (dsp) |
| reg_val &= (~AW8896_BIT_SYSCTRL_DSPBY); |
| else |
| reg_val |= AW8896_BIT_SYSCTRL_DSPBY; |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val); |
| } |
| |
| static int aw8896_get_iis_status(struct aw8896 *aw8896) |
| { |
| int ret = -1; |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val); |
| if (reg_val & AW8896_BIT_SYSST_PLLS) |
| ret = 0; |
| |
| return ret; |
| } |
| |
| static int aw8896_get_dsp_status(struct aw8896 *aw8896) |
| { |
| int ret = -1; |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_WDT, ®_val); |
| if (reg_val) |
| ret = 0; |
| |
| return ret; |
| } |
| |
| static void aw8896_spk_rcv_mode(struct aw8896 *aw8896) |
| { |
| unsigned int reg_val = 0; |
| |
| pr_debug("%s spk_rcv=%d\n", __func__, aw8896->spk_rcv_mode); |
| |
| if (aw8896->spk_rcv_mode == AW8896_SPEAKER_MODE) { |
| /* RFB IDAC = 6V */ |
| aw8896_i2c_read(aw8896, AW8896_REG_AMPDBG1, ®_val); |
| reg_val |= AW8896_BIT_AMPDBG1_OPD; |
| reg_val &= (~AW8896_BIT_AMPDBG1_IPWM_20UA); |
| reg_val &= (~AW8896_BIT_AMPDBG1_RFB_MASK); |
| reg_val |= 0x0002; |
| aw8896_i2c_write(aw8896, AW8896_REG_AMPDBG1, reg_val); |
| |
| /* Speaker Mode */ |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val); |
| reg_val &= (~AW8896_BIT_SYSCTRL_RCV_MODE); |
| aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val); |
| |
| } else if (aw8896->spk_rcv_mode == AW8896_RECEIVER_MODE) { |
| /* RFB IDAC = 4V */ |
| aw8896_i2c_read(aw8896, AW8896_REG_AMPDBG1, ®_val); |
| reg_val |= AW8896_BIT_AMPDBG1_OPD; |
| reg_val |= (AW8896_BIT_AMPDBG1_IPWM_20UA); |
| reg_val &= (~AW8896_BIT_AMPDBG1_RFB_MASK); |
| reg_val |= 0x000B; |
| aw8896_i2c_write(aw8896, AW8896_REG_AMPDBG1, reg_val); |
| |
| /* Receiver Mode */ |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val); |
| reg_val |= AW8896_BIT_SYSCTRL_RCV_MODE; |
| aw8896_i2c_write(aw8896, AW8896_REG_SYSCTRL, reg_val); |
| } |
| } |
| |
| static void aw8896_tx_cfg(struct aw8896 *aw8896) |
| { |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_I2STXCFG, ®_val); |
| reg_val |= AW8896_BIT_I2STXCFG_TXEN; |
| aw8896_i2c_write(aw8896, AW8896_REG_I2STXCFG, reg_val); |
| |
| if (aw8896->dsp_fw_ver == AW8896_DSP_FW_VER_D) { |
| aw8896_i2c_read(aw8896, AW8896_REG_DBGCTRL, ®_val); |
| reg_val |= AW8896_BIT_DBGCTRL_LPBK_NEARE; |
| aw8896_i2c_write(aw8896, AW8896_REG_DBGCTRL, reg_val); |
| } |
| } |
| |
| static void aw8896_start(struct aw8896 *aw8896) |
| { |
| aw8896_run_pwd(aw8896, false); |
| aw8896_run_mute(aw8896, false); |
| } |
| |
| static void aw8896_stop(struct aw8896 *aw8896) |
| { |
| aw8896_run_mute(aw8896, true); |
| aw8896_run_pwd(aw8896, true); |
| } |
| |
| static void aw8896_dsp_container_update(struct aw8896 *aw8896, |
| struct aw8896_container *aw8896_cont, int base) |
| { |
| int i = 0; |
| unsigned char tmp_val = 0; |
| unsigned int tmp_len = 0; |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, base); |
| for (i = 0; i < aw8896_cont->len; i += 2) { |
| tmp_val = aw8896_cont->data[i + 0]; |
| aw8896_cont->data[i + 0] = aw8896_cont->data[i + 1]; |
| aw8896_cont->data[i + 1] = tmp_val; |
| } |
| |
| for (i = 0; i < aw8896_cont->len; i += MAX_RAM_WRITE_BYTE_SIZE) { |
| |
| if ((aw8896_cont->len - i) < MAX_RAM_WRITE_BYTE_SIZE) |
| tmp_len = aw8896_cont->len - i; |
| else |
| tmp_len = MAX_RAM_WRITE_BYTE_SIZE; |
| |
| aw8896_i2c_writes(aw8896, AW8896_REG_DSPMDAT, |
| &aw8896_cont->data[i], tmp_len); |
| } |
| |
| pr_debug("%s exit\n", __func__); |
| } |
| |
| static void aw8896_cfg_loaded(const struct firmware *cont, void *context) |
| { |
| struct aw8896 *aw8896 = context; |
| struct aw8896_container *aw8896_cfg; |
| int ret = -1; |
| |
| if (!aw8896) { |
| pr_err("%s: aw8896 is null\n", __func__); |
| release_firmware(cont); |
| return; |
| } |
| aw8896->dsp_cfg_state = AW8896_DSP_CFG_FAIL; |
| |
| if (!cont) { |
| pr_err("%s: failed to read %s\n", __func__, aw8896_cfg_name); |
| release_firmware(cont); |
| return; |
| } |
| |
| pr_debug("%s: loaded %s - size: %zu\n", __func__, aw8896_cfg_name, |
| cont ? cont->size : 0); |
| |
| aw8896_cfg = kzalloc(cont->size + sizeof(int), GFP_KERNEL); |
| if (!aw8896_cfg) { |
| release_firmware(cont); |
| pr_err("%s: error allocating memory\n", __func__); |
| return; |
| } |
| aw8896_cfg->len = cont->size; |
| memcpy(aw8896_cfg->data, cont->data, cont->size); |
| release_firmware(cont); |
| |
| mutex_lock(&aw8896->lock); |
| aw8896_dsp_container_update(aw8896, aw8896_cfg, AW8896_DSP_CFG_BASE); |
| |
| aw8896->dsp_cfg_len = aw8896_cfg->len; |
| |
| kfree(aw8896_cfg); |
| pr_debug("%s: cfg update complete\n", __func__); |
| |
| aw8896_dsp_enable(aw8896, true); |
| |
| /* delay 1ms wait for dsp start run */ |
| udelay(1000); |
| |
| ret = aw8896_get_dsp_status(aw8896); |
| if (ret) { |
| aw8896_dsp_enable(aw8896, false); |
| aw8896_run_mute(aw8896, true); |
| pr_err("%s: dsp working wdt, dsp fw&cfg update failed\n", |
| __func__); |
| } else { |
| aw8896_tx_cfg(aw8896); |
| aw8896_spk_rcv_mode(aw8896); |
| aw8896_start(aw8896); |
| if (!(aw8896->flags & AW8896_FLAG_SKIP_INTERRUPTS)) { |
| aw8896_interrupt_clear(aw8896); |
| aw8896_interrupt_setup(aw8896); |
| } |
| aw8896->init = 1; |
| aw8896->dsp_cfg_state = AW8896_DSP_CFG_OK; |
| aw8896->dsp_fw_state = AW8896_DSP_FW_OK; |
| pr_debug("%s: init ok\n", __func__); |
| } |
| mutex_unlock(&aw8896->lock); |
| } |
| |
| static int aw8896_load_cfg(struct aw8896 *aw8896) |
| { |
| if (aw8896->dsp_cfg_state == AW8896_DSP_CFG_OK) { |
| pr_debug("%s: dsp cfg ok\n", __func__); |
| return 0; |
| } |
| |
| aw8896->dsp_cfg_state = AW8896_DSP_CFG_PENDING; |
| |
| return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, |
| aw8896_cfg_name, aw8896->dev, GFP_KERNEL, |
| aw8896, aw8896_cfg_loaded); |
| } |
| |
| static void aw8896_fw_loaded(const struct firmware *cont, void *context) |
| { |
| struct aw8896 *aw8896 = context; |
| struct aw8896_container *aw8896_fw = NULL; |
| int ret = -1; |
| |
| if (!aw8896) { |
| pr_err("%s: aw8896 is null\n", __func__); |
| release_firmware(cont); |
| return; |
| } |
| aw8896->dsp_fw_state = AW8896_DSP_FW_FAIL; |
| |
| if (!cont) { |
| pr_err("%s: failed to read %s\n", __func__, |
| aw8896_fw_name[aw8896->dsp_fw_ver]); |
| release_firmware(cont); |
| return; |
| } |
| |
| pr_debug("%s: loaded %s - size: %zu\n", __func__, |
| aw8896_fw_name[aw8896->dsp_fw_ver], |
| cont ? cont->size : 0); |
| |
| aw8896_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); |
| if (!aw8896_fw) { |
| release_firmware(cont); |
| pr_err("%s: Error allocating memory\n", __func__); |
| return; |
| } |
| aw8896_fw->len = cont->size; |
| memcpy(aw8896_fw->data, cont->data, cont->size); |
| release_firmware(cont); |
| |
| mutex_lock(&aw8896->lock); |
| aw8896_dsp_container_update(aw8896, aw8896_fw, AW8896_DSP_FW_BASE); |
| mutex_unlock(&aw8896->lock); |
| |
| aw8896->dsp_fw_len = aw8896_fw->len; |
| kfree(aw8896_fw); |
| pr_debug("%s: fw update complete\n", __func__); |
| |
| ret = aw8896_load_cfg(aw8896); |
| if (ret) |
| pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); |
| } |
| |
| static int aw8896_load_fw(struct aw8896 *aw8896) |
| { |
| unsigned int reg_val = 0; |
| unsigned int tmp_val = 0; |
| |
| if (!aw8896) { |
| pr_err("%s: aw8896 is null\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (aw8896->dsp_fw_state == AW8896_DSP_FW_OK) { |
| pr_debug("%s: dsp fw ok\n", __func__); |
| return 0; |
| } |
| |
| aw8896->dsp_fw_state = AW8896_DSP_FW_PENDING; |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, AW8896_DSP_FW_VER_BASE); |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT, ®_val); |
| tmp_val |= reg_val; |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, |
| AW8896_DSP_FW_VER_BASE + 1); |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT, ®_val); |
| tmp_val |= (reg_val << 16); |
| if (tmp_val == 0xdeac97d6) { |
| aw8896->dsp_fw_ver = AW8896_DSP_FW_VER_E; |
| pr_debug("%s: dsp fw read version: AW8896_DSP_VER_FW_E: 0x%x\n", |
| __func__, tmp_val); |
| } else if (tmp_val == 0) { |
| aw8896->dsp_fw_ver = AW8896_DSP_FW_VER_D; |
| pr_debug("%s: dsp fw read version: AW8896_DSP_FW_VER_D: 0x%x\n", |
| __func__, tmp_val); |
| } else { |
| pr_err("%s: dsp fw read version err: 0x%x\n", __func__, |
| tmp_val); |
| return -EINVAL; |
| } |
| |
| aw8896_run_mute(aw8896, true); |
| aw8896_dsp_enable(aw8896, false); |
| return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, |
| aw8896_fw_name[aw8896->dsp_fw_ver], |
| aw8896->dev, GFP_KERNEL, aw8896, |
| aw8896_fw_loaded); |
| } |
| |
| static void aw8896_reg_container_update(struct aw8896 *aw8896, |
| struct aw8896_container *aw8896_cont) |
| { |
| int i = 0; |
| int reg_addr = 0; |
| int reg_val = 0; |
| |
| mutex_lock(&aw8896->lock); |
| for (i = 0; i < aw8896_cont->len; i += 4) { |
| reg_addr = (aw8896_cont->data[i + 1] << 8) |
| + aw8896_cont->data[i + 0]; |
| reg_val = (aw8896_cont->data[i + 3] << 8) |
| + aw8896_cont->data[i + 2]; |
| pr_debug("%s: reg=0x%04x, val = 0x%04x\n", |
| __func__, reg_addr, reg_val); |
| aw8896_i2c_write(aw8896, (unsigned char)reg_addr, |
| (unsigned int)reg_val); |
| } |
| mutex_unlock(&aw8896->lock); |
| |
| pr_debug("%s exit\n", __func__); |
| } |
| |
| static void aw8896_reg_loaded(const struct firmware *cont, void *context) |
| { |
| struct aw8896 *aw8896 = context; |
| struct aw8896_container *aw8896_reg = NULL; |
| int ret = -1; |
| |
| if (!aw8896) { |
| pr_err("%s: aw8896 is null\n", __func__); |
| release_firmware(cont); |
| return; |
| } |
| |
| if (!cont) { |
| pr_err("%s: failed to read %s\n", __func__, aw8896_reg_name); |
| release_firmware(cont); |
| return; |
| } |
| |
| pr_debug("%s: loaded %s - size: %zu\n", __func__, aw8896_reg_name, |
| cont ? cont->size : 0); |
| |
| aw8896_reg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); |
| if (!aw8896_reg) { |
| release_firmware(cont); |
| pr_err("%s: error allocating memory\n", __func__); |
| return; |
| } |
| aw8896_reg->len = cont->size; |
| memcpy(aw8896_reg->data, cont->data, cont->size); |
| release_firmware(cont); |
| |
| aw8896_reg_container_update(aw8896, aw8896_reg); |
| |
| kfree(aw8896_reg); |
| |
| ret = aw8896_get_iis_status(aw8896); |
| if (ret < 0) { |
| pr_err("%s: get no iis signal, ret=%d\n", __func__, ret); |
| } else { |
| ret = aw8896_load_fw(aw8896); |
| if (ret) |
| pr_err("%s: cfg loading requested failed: %d\n", |
| __func__, ret); |
| } |
| } |
| |
| static int aw8896_load_reg(struct aw8896 *aw8896) |
| { |
| return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, |
| aw8896_reg_name, aw8896->dev, GFP_KERNEL, |
| aw8896, aw8896_reg_loaded); |
| } |
| |
| static void aw8896_cold_start(struct aw8896 *aw8896) |
| { |
| int ret = -1; |
| |
| ret = aw8896_load_reg(aw8896); |
| if (ret) |
| pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); |
| } |
| |
| static void aw8896_smartpa_cfg(struct aw8896 *aw8896, bool flag) |
| { |
| pr_debug("%s, flag = %d\n", __func__, flag); |
| |
| if (flag == true) { |
| if (aw8896->init == 0) { |
| pr_debug("%s, init = %d\n", __func__, aw8896->init); |
| aw8896_cold_start(aw8896); |
| } else { |
| aw8896_spk_rcv_mode(aw8896); |
| aw8896_start(aw8896); |
| } |
| } else { |
| aw8896_stop(aw8896); |
| } |
| } |
| |
| static const char *const spk_function[] = { "Off", "On" }; |
| static const char *const rcv_function[] = { "Off", "On" }; |
| static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0); |
| |
| struct soc_mixer_control aw8896_mixer = { |
| .reg = AW8896_REG_DSPCFG, |
| .shift = AW8896_VOL_REG_SHIFT, |
| .max = AW8896_VOLUME_MAX, |
| .min = AW8896_VOLUME_MIN, |
| }; |
| |
| static int aw8896_volume_info(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_info *uinfo) |
| { |
| struct soc_mixer_control *mc = (struct soc_mixer_control *) |
| kcontrol->private_value; |
| |
| /* set kcontrol info */ |
| uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
| uinfo->count = 1; |
| uinfo->value.integer.min = 0; |
| uinfo->value.integer.max = mc->max - mc->min; |
| return 0; |
| } |
| |
| static int aw8896_volume_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| int value; |
| struct soc_mixer_control *mc = (struct soc_mixer_control *) |
| kcontrol->private_value; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPCFG, &value); |
| ucontrol->value.integer.value[0] = (value >> mc->shift) |
| & (~AW8896_BIT_DSPCFG_VOL_MASK); |
| return 0; |
| } |
| |
| static int aw8896_volume_put(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = (struct soc_mixer_control *) |
| kcontrol->private_value; |
| struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| unsigned int value = 0; |
| unsigned int reg_value = 0; |
| |
| value = ucontrol->value.integer.value[0]; |
| if (value > (mc->max-mc->min) || value < 0) { |
| pr_err("%s:value over range\n", __func__); |
| return -EINVAL; |
| } |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_value); |
| if (!(reg_value & AW8896_BIT_SYSST_PLLS)) { |
| pr_err("%s: NO I2S CLK ,can not write reg\n", __func__); |
| return 0; |
| } |
| value = value << mc->shift & AW8896_BIT_DSPCFG_VOL_MASK; |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPCFG, ®_value); |
| value = value | (reg_value & 0x00ff); |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_value); |
| if (reg_value & AW8896_BIT_SYSCTRL_DSPBY) { |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPCFG, value); |
| } else { |
| aw8896_dsp_enable(aw8896, false); |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPCFG, value); |
| aw8896_dsp_enable(aw8896, true); |
| } |
| |
| return 0; |
| } |
| |
| static struct snd_kcontrol_new aw8896_volume = { |
| .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
| .name = "aw8896_rx_volume", |
| .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
| | SNDRV_CTL_ELEM_ACCESS_READWRITE, |
| .tlv.p = (digital_gain), |
| .info = aw8896_volume_info, |
| .get = aw8896_volume_get, |
| .put = aw8896_volume_put, |
| .private_value = (unsigned long)&aw8896_mixer, |
| }; |
| |
| static int aw8896_spk_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| pr_debug("%s: aw8896_spk_control=%d\n", __func__, aw8896_spk_control); |
| ucontrol->value.integer.value[0] = aw8896_spk_control; |
| return 0; |
| } |
| |
| static int aw8896_spk_set(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", |
| __func__, ucontrol->value.integer.value[0]); |
| if (ucontrol->value.integer.value[0] == aw8896_spk_control) |
| return 1; |
| |
| aw8896_spk_control = ucontrol->value.integer.value[0]; |
| aw8896->spk_rcv_mode = AW8896_SPEAKER_MODE; |
| |
| return 0; |
| } |
| |
| static int aw8896_rcv_get(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| pr_debug("%s: aw8896_rcv_control=%d\n", __func__, aw8896_rcv_control); |
| ucontrol->value.integer.value[0] = aw8896_rcv_control; |
| return 0; |
| } |
| |
| static int aw8896_rcv_set(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", |
| __func__, ucontrol->value.integer.value[0]); |
| if (ucontrol->value.integer.value[0] == aw8896_rcv_control) |
| return 1; |
| |
| aw8896_rcv_control = ucontrol->value.integer.value[0]; |
| |
| aw8896->spk_rcv_mode = AW8896_RECEIVER_MODE; |
| |
| return 0; |
| } |
| |
| static const struct soc_enum aw8896_snd_enum[] = { |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), |
| SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rcv_function), rcv_function), |
| }; |
| |
| static struct snd_kcontrol_new aw8896_controls[] = { |
| SOC_ENUM_EXT("aw8896_speaker_switch", aw8896_snd_enum[0], |
| aw8896_spk_get, aw8896_spk_set), |
| SOC_ENUM_EXT("aw8896_receiver_switch", aw8896_snd_enum[1], |
| aw8896_rcv_get, aw8896_rcv_set), |
| }; |
| |
| static void aw8896_add_codec_controls(struct aw8896 *aw8896) |
| { |
| snd_soc_add_codec_controls(aw8896->codec, aw8896_controls, |
| ARRAY_SIZE(aw8896_controls)); |
| |
| snd_soc_add_codec_controls(aw8896->codec, &aw8896_volume, 1); |
| } |
| |
| /* |
| * Digital Audio Interface |
| */ |
| static int aw8896_startup(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| pr_debug("%s: enter\n", __func__); |
| mutex_lock(&aw8896->lock); |
| aw8896_run_pwd(aw8896, false); |
| mutex_unlock(&aw8896->lock); |
| |
| return 0; |
| } |
| |
| static int aw8896_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| |
| pr_debug("%s: fmt=0x%x\n", __func__, fmt); |
| |
| /* Supported mode: regular I2S, slave, or PDM */ |
| switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| case SND_SOC_DAIFMT_I2S: |
| if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) |
| != SND_SOC_DAIFMT_CBS_CFS) { |
| dev_err(codec->dev, "%s: invalid codec master mode\n", |
| __func__); |
| return -EINVAL; |
| } |
| break; |
| default: |
| dev_err(codec->dev, "%s: unsupported DAI format %d\n", __func__, |
| fmt & SND_SOC_DAIFMT_FORMAT_MASK); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int aw8896_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
| int clk_id, unsigned int freq, int dir) |
| { |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec_dai->codec); |
| |
| pr_debug("%s: freq=%d\n", __func__, freq); |
| |
| aw8896->sysclk = freq; |
| return 0; |
| } |
| |
| static int aw8896_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params, |
| struct snd_soc_dai *dai) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| int reg_value = 0; |
| int tmp_value = 0; |
| int width = 0; |
| |
| mutex_lock(&aw8896->lock); |
| /* get rate param */ |
| aw8896->rate = params_rate(params); |
| pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, |
| aw8896->rate, |
| snd_pcm_format_width(params_format(params))); |
| |
| switch (aw8896->rate) { |
| case 8000: |
| reg_value = AW8896_BIT_I2SCTRL_SR_8K; |
| break; |
| case 16000: |
| reg_value = AW8896_BIT_I2SCTRL_SR_16K; |
| break; |
| case 32000: |
| reg_value = AW8896_BIT_I2SCTRL_SR_32K; |
| break; |
| case 44100: |
| reg_value = AW8896_BIT_I2SCTRL_SR_44K; |
| break; |
| case 48000: |
| reg_value = AW8896_BIT_I2SCTRL_SR_48K; |
| break; |
| default: |
| reg_value = AW8896_BIT_I2SCTRL_SR_48K; |
| pr_err("%s: rate can not support, set to default\n", __func__); |
| break; |
| } |
| /* set chip rate */ |
| if (reg_value != -1) { |
| aw8896_i2c_read(aw8896, AW8896_REG_I2SCTRL, &tmp_value); |
| reg_value |= (tmp_value & (~AW8896_BIT_I2SCTRL_SR_MASK)); |
| aw8896_i2c_write(aw8896, AW8896_REG_I2SCTRL, reg_value); |
| } |
| |
| /* get bit width */ |
| width = params_width(params); |
| pr_debug("%s: width = %d\n", __func__, width); |
| switch (width) { |
| case 16: |
| reg_value = AW8896_BIT_I2SCTRL_FMS_16BIT; |
| break; |
| case 20: |
| reg_value = AW8896_BIT_I2SCTRL_FMS_20BIT; |
| break; |
| case 24: |
| reg_value = AW8896_BIT_I2SCTRL_FMS_24BIT; |
| break; |
| case 32: |
| reg_value = AW8896_BIT_I2SCTRL_FMS_32BIT; |
| break; |
| default: |
| reg_value = AW8896_BIT_I2SCTRL_FMS_16BIT; |
| pr_err("%s: width can not support\n", __func__); |
| break; |
| } |
| /* set width */ |
| if (reg_value != -1) { |
| aw8896_i2c_read(aw8896, AW8896_REG_I2SCTRL, &tmp_value); |
| reg_value |= (tmp_value & (~AW8896_BIT_I2SCTRL_FMS_MASK)); |
| aw8896_i2c_write(aw8896, AW8896_REG_I2SCTRL, reg_value); |
| } |
| mutex_unlock(&aw8896->lock); |
| |
| return 0; |
| } |
| |
| static int aw8896_mute(struct snd_soc_dai *dai, int mute, int stream) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| pr_debug("%s: mute state=%d\n", __func__, mute); |
| |
| if (!(aw8896->flags & AW8896_FLAG_DSP_START_ON_MUTE)) |
| return 0; |
| |
| if (mute) { |
| /* stop DSP only when both playback and capture streams |
| * are deactivated |
| */ |
| if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
| aw8896->pstream = 0; |
| else |
| aw8896->cstream = 0; |
| if (aw8896->pstream != 0 || aw8896->cstream != 0) |
| return 0; |
| |
| /* Stop DSP */ |
| mutex_lock(&aw8896->lock); |
| aw8896_smartpa_cfg(aw8896, false); |
| mutex_unlock(&aw8896->lock); |
| } else { |
| if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
| aw8896->pstream = 1; |
| else |
| aw8896->cstream = 1; |
| |
| /* Start DSP */ |
| mutex_lock(&aw8896->lock); |
| aw8896_smartpa_cfg(aw8896, true); |
| mutex_unlock(&aw8896->lock); |
| } |
| |
| return 0; |
| } |
| |
| static void aw8896_shutdown(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| aw8896->rate = 0; |
| aw8896_run_pwd(aw8896, true); |
| } |
| |
| static const struct snd_soc_dai_ops aw8896_dai_ops = { |
| .startup = aw8896_startup, |
| .set_fmt = aw8896_set_fmt, |
| .set_sysclk = aw8896_set_dai_sysclk, |
| .hw_params = aw8896_hw_params, |
| .mute_stream = aw8896_mute, |
| .shutdown = aw8896_shutdown, |
| }; |
| |
| static struct snd_soc_dai_driver aw8896_dai[] = { |
| { |
| .name = "aw8896-aif", |
| .id = 1, |
| .playback = { |
| .stream_name = "Speaker_Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = AW8896_RATES, |
| .formats = AW8896_FORMATS, |
| }, |
| .capture = { |
| .stream_name = "Speaker_Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = AW8896_RATES, |
| .formats = AW8896_FORMATS, |
| }, |
| .ops = &aw8896_dai_ops, |
| .symmetric_rates = 1, |
| .symmetric_channels = 1, |
| .symmetric_samplebits = 1, |
| }, |
| }; |
| |
| static int aw8896_probe(struct snd_soc_codec *codec) |
| { |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| int ret = 0; |
| |
| aw8896->codec = codec; |
| aw8896_add_codec_controls(aw8896); |
| |
| return ret; |
| } |
| |
| static int aw8896_remove(struct snd_soc_codec *codec) |
| { |
| return 0; |
| } |
| |
| struct regmap *aw8896_get_regmap(struct device *dev) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| |
| return aw8896->regmap; |
| } |
| |
| static unsigned int aw8896_codec_read(struct snd_soc_codec *codec, |
| unsigned int reg) |
| { |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| unsigned int value = 0; |
| int ret = 0; |
| |
| pr_debug("%s:enter\n", __func__); |
| |
| if (aw8896_reg_access[reg] & REG_RD_ACCESS) { |
| ret = aw8896_i2c_read(aw8896, reg, &value); |
| if (ret < 0) { |
| pr_debug("%s: read register failed\n", __func__); |
| return ret; |
| } |
| } else { |
| pr_debug("%s:Register 0x%x NO read access\n", __func__, reg); |
| return -EINVAL; |
| } |
| return value; |
| } |
| |
| static int aw8896_codec_write(struct snd_soc_codec *codec, unsigned int reg, |
| unsigned int value) |
| { |
| int ret = 0; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| pr_debug("%s:enter ,reg is 0x%x value is 0x%x\n", __func__, reg, value); |
| |
| if (aw8896_reg_access[reg] & REG_WR_ACCESS) { |
| ret = aw8896_i2c_write(aw8896, reg, value); |
| return ret; |
| } |
| pr_debug("%s: Register 0x%x NO write access\n", __func__, reg); |
| |
| return -EINVAL; |
| } |
| |
| #ifdef CONFIG_PM |
| static int aw8896_suspend(struct snd_soc_codec *codec) |
| { |
| int ret = 0; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| /* clear FW/DSP status flags so the DSP can get into |
| * correct status after resume |
| */ |
| aw8896->init = 0; |
| aw8896->dsp_fw_state = AW8896_DSP_FW_FAIL; |
| aw8896->dsp_cfg_state = AW8896_DSP_CFG_FAIL; |
| |
| ret = regulator_disable(aw8896->supply.regulator); |
| if (ret < 0) |
| dev_err(codec->dev, "%s: fail to disable regulator, err:%d\n", |
| __func__, ret); |
| |
| return 0; |
| } |
| |
| static int aw8896_resume(struct snd_soc_codec *codec) |
| { |
| int ret = 0; |
| struct aw8896 *aw8896 = snd_soc_codec_get_drvdata(codec); |
| |
| ret = regulator_enable(aw8896->supply.regulator); |
| if (ret < 0) |
| dev_err(codec->dev, "%s: fail to enable regulator, err:%d\n", |
| __func__, ret); |
| |
| return 0; |
| } |
| #else |
| #define aw8896_suspend NULL |
| #define aw8896_resume NULL |
| #endif |
| |
| static struct snd_soc_codec_driver soc_codec_dev_aw8896 = { |
| .probe = aw8896_probe, |
| .remove = aw8896_remove, |
| .suspend = aw8896_suspend, |
| .resume = aw8896_resume, |
| .read = aw8896_codec_read, |
| .write = aw8896_codec_write, |
| .reg_cache_size = AW8896_REG_MAX, |
| .reg_word_size = 2, |
| }; |
| |
| bool aw8896_writeable_register(struct device *dev, unsigned int reg) |
| { |
| /* enable write access for all registers */ |
| return 1; |
| } |
| |
| bool aw8896_readable_register(struct device *dev, unsigned int reg) |
| { |
| /* enable read access for all registers */ |
| return 1; |
| } |
| |
| bool aw8896_volatile_register(struct device *dev, unsigned int reg) |
| { |
| return 1; |
| } |
| |
| static const struct regmap_config aw8896_regmap = { |
| .reg_bits = 8, |
| .val_bits = 16, |
| |
| .max_register = AW8896_MAX_REGISTER, |
| .writeable_reg = aw8896_writeable_register, |
| .readable_reg = aw8896_readable_register, |
| .volatile_reg = aw8896_volatile_register, |
| .cache_type = REGCACHE_RBTREE, |
| }; |
| |
| static void aw8896_interrupt_setup(struct aw8896 *aw8896) |
| { |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSINTM, ®_val); |
| |
| /* i2s status interrupt unmask */ |
| reg_val &= (~AW8896_BIT_SYSINTM_PLLM); |
| reg_val &= (~AW8896_BIT_SYSINTM_CLKM); |
| reg_val &= (~AW8896_BIT_SYSINTM_NOCLKM); |
| |
| /* uvlo interrupt unmask */ |
| reg_val &= (~AW8896_BIT_SYSINTM_UVLOM); |
| |
| /* over temperature interrupt unmask */ |
| reg_val &= (~AW8896_BIT_SYSINTM_OTHM); |
| |
| /* dsp watchdog status interrupt unmask */ |
| reg_val &= (~AW8896_BIT_SYSINTM_WDM); |
| |
| aw8896_i2c_write(aw8896, AW8896_REG_SYSINTM, reg_val); |
| } |
| |
| static void aw8896_interrupt_clear(struct aw8896 *aw8896) |
| { |
| unsigned int reg_val; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val); |
| pr_debug("%s: reg SYSST=0x%x\n", __func__, reg_val); |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSINT, ®_val); |
| pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSINTM, ®_val); |
| pr_debug("%s: reg SYSINTM=0x%x\n", __func__, reg_val); |
| } |
| |
| static irqreturn_t aw8896_irq(int irq, void *data) |
| { |
| struct aw8896 *aw8896 = data; |
| |
| aw8896_interrupt_clear(aw8896); |
| |
| pr_debug("%s exit\n", __func__); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int aw8896_parse_dt(struct device *dev, struct aw8896 *aw8896, |
| struct device_node *np) { |
| int prop_val = 0; |
| int ret = 0; |
| int len = 0; |
| const __be32 *prop = NULL; |
| struct device_node *regnode = NULL; |
| char *dvdd_supply = "dvdd"; |
| char prop_name[DT_MAX_PROP_SIZE] = {0}; |
| |
| aw8896->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); |
| if (aw8896->reset_gpio < 0) { |
| dev_err(dev, |
| "%s: no reset gpio provided, will not HW reset device\n", |
| __func__); |
| return -EINVAL; |
| } |
| |
| dev_dbg(dev, "%s: reset gpio provided ok\n", __func__); |
| |
| aw8896->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); |
| if (aw8896->irq_gpio < 0) |
| dev_info(dev, "%s: no irq gpio provided.\n", __func__); |
| |
| snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-supply", dvdd_supply); |
| regnode = of_parse_phandle(np, prop_name, 0); |
| if (!regnode) { |
| dev_err(dev, "%s: no %s provided\n", __func__, prop_name); |
| goto err_get_regulator; |
| } |
| |
| aw8896->supply.regulator = devm_regulator_get(dev, dvdd_supply); |
| if (IS_ERR(aw8896->supply.regulator)) { |
| dev_err(dev, "%s: failed to get supply for %s\n", __func__, |
| dvdd_supply); |
| goto err_get_regulator; |
| } |
| |
| snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-voltage", dvdd_supply); |
| prop = of_get_property(np, prop_name, &len); |
| if (!prop || (len != (2 * sizeof(__be32)))) { |
| dev_err(dev, "%s: no %s provided or format invalid\n", |
| __func__, prop_name); |
| goto err_get_voltage; |
| } |
| |
| aw8896->supply.min_uv = be32_to_cpup(&prop[0]); |
| aw8896->supply.max_uv = be32_to_cpup(&prop[1]); |
| |
| snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-current", dvdd_supply); |
| ret = of_property_read_u32(np, prop_name, &prop_val); |
| if (ret) { |
| dev_err(dev, "%s: no %s provided\n", __func__, prop_name); |
| goto err_get_current; |
| } |
| aw8896->supply.ua = prop_val; |
| |
| return 0; |
| |
| err_get_current: |
| err_get_voltage: |
| devm_regulator_put(aw8896->supply.regulator); |
| aw8896->supply.regulator = NULL; |
| err_get_regulator: |
| return -EINVAL; |
| } |
| |
| int aw8896_hw_reset(struct aw8896 *aw8896) |
| { |
| if (aw8896 && gpio_is_valid(aw8896->reset_gpio)) { |
| gpio_set_value_cansleep(aw8896->reset_gpio, 1); |
| udelay(1000); /* delay 1ms for aw8896 hardware reset */ |
| gpio_set_value_cansleep(aw8896->reset_gpio, 0); |
| udelay(1000); /* delay 1ms for aw8896 hardware reset */ |
| } else { |
| dev_err(aw8896->dev, "%s: failed\n", __func__); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int aw8896_read_chipid(struct aw8896 *aw8896) |
| { |
| int ret = -1; |
| unsigned int cnt = 0; |
| unsigned int reg = 0; |
| |
| while (cnt < AW_READ_CHIPID_RETRIES) { |
| ret = aw8896_i2c_read(aw8896, AW8896_REG_ID, ®); |
| if (ret < 0) { |
| dev_err(aw8896->dev, |
| "%s: failed to read register AW8896_REG_ID: %d\n", |
| __func__, ret); |
| return -EIO; |
| } |
| switch (reg) { |
| case 0x0310: |
| pr_debug("%s aw8896 detected\n", __func__); |
| aw8896->flags |= AW8896_FLAG_SKIP_INTERRUPTS; |
| aw8896->flags |= AW8896_FLAG_DSP_START_ON_MUTE; |
| aw8896->flags |= AW8896_FLAG_DSP_START_ON; |
| aw8896->chipid = AW8990_ID; |
| pr_debug("%s aw8896->flags=0x%x\n", __func__, |
| aw8896->flags); |
| return 0; |
| default: |
| pr_err("%s unsupported device revision (0x%x)\n", |
| __func__, reg); |
| break; |
| } |
| cnt++; |
| |
| msleep(AW_READ_CHIPID_RETRY_DELAY); |
| } |
| |
| return -EINVAL; |
| } |
| |
| static ssize_t aw8896_reg_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| unsigned int databuf[2] = {0}; |
| |
| if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) |
| aw8896_i2c_write(aw8896, databuf[0], databuf[1]); |
| |
| return count; |
| } |
| |
| static ssize_t aw8896_reg_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| ssize_t len = 0; |
| unsigned char i = 0; |
| unsigned int reg_val = 0; |
| |
| for (i = 0; i < AW8896_REG_MAX; i++) { |
| if (!(aw8896_reg_access[i] & REG_RD_ACCESS)) |
| continue; |
| aw8896_i2c_read(aw8896, i, ®_val); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "reg:0x%02x=0x%04x\n", i, reg_val); |
| } |
| return len; |
| } |
| |
| static ssize_t aw8896_dsp_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| |
| unsigned int databuf[16] = {0}; |
| unsigned int reg_val = 0; |
| |
| if (kstrtouint(buf, 10, &databuf[0]) == 0) { |
| if (databuf[0] == 1) { |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val); |
| if (reg_val & AW8896_BIT_SYSST_PLLS) { |
| aw8896->init = 0; |
| aw8896->dsp_fw_state = AW8896_DSP_FW_FAIL; |
| aw8896->dsp_cfg_state = AW8896_DSP_CFG_FAIL; |
| aw8896_smartpa_cfg(aw8896, false); |
| aw8896_smartpa_cfg(aw8896, true); |
| } else { |
| count += snprintf((char *)(buf + count), |
| PAGE_SIZE - count, |
| "aw8896 plls=%d, no iis signal\n", |
| reg_val & AW8896_BIT_SYSST_PLLS); |
| } |
| } else { |
| aw8896_dsp_enable(aw8896, false); |
| } |
| } |
| |
| return count; |
| } |
| |
| static ssize_t aw8896_dsp_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| ssize_t len = 0; |
| unsigned int i = 0; |
| unsigned int addr = 0; |
| unsigned int reg_val = 0; |
| |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSCTRL, ®_val); |
| |
| if (reg_val & AW8896_BIT_SYSCTRL_DSPBY) { |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "aw8896 dsp bypass\n"); |
| } else { |
| aw8896_i2c_read(aw8896, AW8896_REG_SYSST, ®_val); |
| if (reg_val & AW8896_BIT_SYSST_PLLS) { |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "aw8896 dsp working\n"); |
| |
| aw8896_dsp_enable(aw8896, false); |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "aw8896 dsp firmware:\n"); |
| addr = 0; |
| for (i = 0; i < aw8896->dsp_fw_len; i += 2) { |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, |
| AW8896_DSP_FW_BASE + addr); |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT, |
| ®_val); |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "0x%02x,0x%02x,", reg_val & 0x00FF, |
| (reg_val >> 8)); |
| |
| if ((i / 2 + 1) % 4 == 0) |
| len += snprintf(buf + len, |
| PAGE_SIZE - len, "\n"); |
| addr++; |
| } |
| len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "aw8896 dsp config:\n"); |
| addr = 0; |
| for (i = 0; i < aw8896->dsp_cfg_len; i += 2) { |
| aw8896_i2c_write(aw8896, AW8896_REG_DSPMADD, |
| AW8896_DSP_CFG_BASE + addr); |
| aw8896_i2c_read(aw8896, AW8896_REG_DSPMDAT, |
| ®_val); |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "0x%02x,0x%02x,", reg_val & 0x00FF, |
| (reg_val >> 8)); |
| |
| if ((i / 2 + 1) % 4 == 0) |
| len += snprintf(buf + len, |
| PAGE_SIZE - len, "\n"); |
| addr++; |
| } |
| len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| |
| aw8896_dsp_enable(aw8896, true); |
| } else { |
| len += snprintf((char *)(buf+len), PAGE_SIZE - len, |
| "aw8896 plls=%d, no iis signal\n", |
| reg_val & AW8896_BIT_SYSST_PLLS); |
| } |
| } |
| |
| return len; |
| } |
| |
| static ssize_t aw8896_spk_rcv_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| unsigned int databuf[2] = {0}; |
| |
| if (kstrtouint(buf, 10, &databuf[0]) == 0) |
| aw8896->spk_rcv_mode = databuf[0]; |
| |
| return count; |
| } |
| |
| static ssize_t aw8896_spk_rcv_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aw8896 *aw8896 = dev_get_drvdata(dev); |
| ssize_t len = 0; |
| |
| if (aw8896->spk_rcv_mode == AW8896_SPEAKER_MODE) |
| len += snprintf(buf+len, PAGE_SIZE-len, |
| "aw8896 spk_rcv: %d, speaker mode\n", |
| aw8896->spk_rcv_mode); |
| else if (aw8896->spk_rcv_mode == AW8896_RECEIVER_MODE) |
| len += snprintf(buf+len, PAGE_SIZE-len, |
| "aw8896 spk_rcv: %d, receiver mode\n", |
| aw8896->spk_rcv_mode); |
| else |
| len += snprintf(buf+len, PAGE_SIZE-len, |
| "aw8896 spk_rcv: %d, unknown mode\n", |
| aw8896->spk_rcv_mode); |
| |
| return len; |
| } |
| |
| static DEVICE_ATTR(reg, 0644, aw8896_reg_show, aw8896_reg_store); |
| static DEVICE_ATTR(dsp, 0644, aw8896_dsp_show, aw8896_dsp_store); |
| static DEVICE_ATTR(spk_rcv, 0644, aw8896_spk_rcv_show, aw8896_spk_rcv_store); |
| |
| static struct attribute *aw8896_attributes[] = { |
| &dev_attr_reg.attr, |
| &dev_attr_dsp.attr, |
| &dev_attr_spk_rcv.attr, |
| NULL |
| }; |
| |
| static struct attribute_group aw8896_attribute_group = { |
| .attrs = aw8896_attributes |
| }; |
| |
| static int aw8896_i2c_probe(struct i2c_client *i2c, |
| const struct i2c_device_id *id) |
| { |
| struct snd_soc_dai_driver *dai; |
| struct aw8896 *aw8896; |
| struct device_node *np = i2c->dev.of_node; |
| int irq_flags = 0; |
| int ret = -1; |
| |
| if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { |
| dev_err(&i2c->dev, "check_functionality failed\n"); |
| return -EIO; |
| } |
| |
| aw8896 = devm_kzalloc(&i2c->dev, sizeof(struct aw8896), GFP_KERNEL); |
| if (aw8896 == NULL) |
| return -ENOMEM; |
| |
| aw8896->dev = &i2c->dev; |
| aw8896->i2c = i2c; |
| |
| aw8896->regmap = devm_regmap_init_i2c(i2c, &aw8896_regmap); |
| if (IS_ERR(aw8896->regmap)) { |
| ret = PTR_ERR(aw8896->regmap); |
| dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n", |
| __func__, ret); |
| goto err_regmap; |
| } |
| |
| i2c_set_clientdata(i2c, aw8896); |
| mutex_init(&aw8896->lock); |
| |
| if (np) { |
| ret = aw8896_parse_dt(&i2c->dev, aw8896, np); |
| if (ret) { |
| dev_err(&i2c->dev, |
| "%s: failed to parse device tree node\n", |
| __func__); |
| goto err_parse_dt; |
| } |
| } else { |
| aw8896->reset_gpio = -1; |
| aw8896->irq_gpio = -1; |
| } |
| |
| if (gpio_is_valid(aw8896->reset_gpio)) { |
| ret = devm_gpio_request_one(&i2c->dev, aw8896->reset_gpio, |
| GPIOF_OUT_INIT_LOW, "aw8896_rst"); |
| if (ret) { |
| dev_err(&i2c->dev, "%s: rst request failed\n", |
| __func__); |
| goto err_reset_gpio_request; |
| } |
| } |
| |
| if (gpio_is_valid(aw8896->irq_gpio)) { |
| ret = devm_gpio_request_one(&i2c->dev, aw8896->irq_gpio, |
| GPIOF_DIR_IN, "aw8896_int"); |
| if (ret) { |
| dev_err(&i2c->dev, "%s: int request failed\n", |
| __func__); |
| goto err_irq_gpio_request; |
| } |
| } |
| |
| ret = regulator_set_voltage(aw8896->supply.regulator, |
| aw8896->supply.max_uv, |
| aw8896->supply.min_uv); |
| if (ret) { |
| dev_err(&i2c->dev, "%s: set voltage %d ~ %d failed\n", |
| __func__, |
| aw8896->supply.min_uv, |
| aw8896->supply.max_uv); |
| goto err_supply_set; |
| } |
| |
| ret = regulator_set_load(aw8896->supply.regulator, aw8896->supply.ua); |
| if (ret < 0) { |
| dev_err(&i2c->dev, "%s: set current %d failed\n", __func__, |
| aw8896->supply.ua); |
| goto err_supply_set; |
| } |
| |
| ret = regulator_enable(aw8896->supply.regulator); |
| if (ret < 0) { |
| dev_err(&i2c->dev, "%s: regulator enable failed\n", __func__); |
| goto err_supply_set; |
| } |
| |
| ret = aw8896_hw_reset(aw8896); |
| if (ret < 0) { |
| dev_err(&i2c->dev, "%s: aw8896_hw_reset failed\n", __func__); |
| goto err_hw_rst; |
| } |
| |
| ret = aw8896_read_chipid(aw8896); |
| if (ret < 0) { |
| dev_err(&i2c->dev, "%s: aw8896_read_chipid failed ret=%d\n", |
| __func__, ret); |
| goto err_id; |
| } |
| |
| /* register codec */ |
| dai = devm_kzalloc(&i2c->dev, sizeof(aw8896_dai), GFP_KERNEL); |
| if (!dai) |
| goto err_dai_kzalloc; |
| |
| memcpy(dai, aw8896_dai, sizeof(aw8896_dai)); |
| pr_debug("%s dai->name(%s)\n", __func__, dai->name); |
| |
| ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aw8896, |
| dai, ARRAY_SIZE(aw8896_dai)); |
| if (ret < 0) { |
| dev_err(&i2c->dev, "%s failed to register aw8896: %d\n", |
| __func__, ret); |
| goto err_register_codec; |
| } |
| |
| /* aw8896 irq */ |
| if (gpio_is_valid(aw8896->irq_gpio) && |
| !(aw8896->flags & AW8896_FLAG_SKIP_INTERRUPTS)) { |
| |
| irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; |
| ret = devm_request_threaded_irq(&i2c->dev, |
| gpio_to_irq(aw8896->irq_gpio), |
| NULL, aw8896_irq, irq_flags, |
| "aw8896", aw8896); |
| if (ret != 0) { |
| dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", |
| gpio_to_irq(aw8896->irq_gpio), ret); |
| goto err_irq; |
| } |
| } else { |
| dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); |
| /* disable feature support if gpio was invalid */ |
| aw8896->flags |= AW8896_FLAG_SKIP_INTERRUPTS; |
| } |
| |
| dev_set_drvdata(&i2c->dev, aw8896); |
| ret = sysfs_create_group(&i2c->dev.kobj, &aw8896_attribute_group); |
| if (ret < 0) { |
| dev_info(&i2c->dev, "%s error creating sysfs attr files\n", |
| __func__); |
| goto err_sysfs; |
| } |
| |
| pr_debug("%s probe completed successfully!\n", __func__); |
| |
| return 0; |
| |
| err_sysfs: |
| devm_free_irq(&i2c->dev, gpio_to_irq(aw8896->irq_gpio), aw8896); |
| err_irq: |
| snd_soc_unregister_codec(&i2c->dev); |
| err_register_codec: |
| devm_kfree(&i2c->dev, dai); |
| dai = NULL; |
| err_dai_kzalloc: |
| err_id: |
| if (gpio_is_valid(aw8896->irq_gpio)) |
| devm_gpio_free(&i2c->dev, aw8896->irq_gpio); |
| err_hw_rst: |
| if (aw8896->supply.regulator) |
| regulator_disable(aw8896->supply.regulator); |
| err_supply_set: |
| if (aw8896->supply.regulator) |
| devm_regulator_put(aw8896->supply.regulator); |
| err_irq_gpio_request: |
| if (gpio_is_valid(aw8896->reset_gpio)) |
| devm_gpio_free(&i2c->dev, aw8896->reset_gpio); |
| err_reset_gpio_request: |
| err_parse_dt: |
| err_regmap: |
| devm_kfree(&i2c->dev, aw8896); |
| aw8896 = NULL; |
| return ret; |
| } |
| |
| static int aw8896_i2c_remove(struct i2c_client *i2c) |
| { |
| struct aw8896 *aw8896 = i2c_get_clientdata(i2c); |
| |
| if (gpio_to_irq(aw8896->irq_gpio)) |
| devm_free_irq(&i2c->dev, gpio_to_irq(aw8896->irq_gpio), aw8896); |
| |
| snd_soc_unregister_codec(&i2c->dev); |
| |
| if (gpio_is_valid(aw8896->irq_gpio)) |
| devm_gpio_free(&i2c->dev, aw8896->irq_gpio); |
| if (gpio_is_valid(aw8896->reset_gpio)) |
| devm_gpio_free(&i2c->dev, aw8896->reset_gpio); |
| |
| if (aw8896->supply.regulator) { |
| regulator_disable(aw8896->supply.regulator); |
| devm_regulator_put(aw8896->supply.regulator); |
| } |
| |
| devm_kfree(&i2c->dev, aw8896); |
| aw8896 = NULL; |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id aw8896_i2c_id[] = { |
| { AW8896_I2C_NAME, 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, aw8896_i2c_id); |
| |
| static const struct of_device_id aw8896_dt_match[] = { |
| { .compatible = "awinic,aw8896_smartpa" }, |
| { }, |
| }; |
| |
| static struct i2c_driver aw8896_i2c_driver = { |
| .driver = { |
| .name = AW8896_I2C_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(aw8896_dt_match), |
| }, |
| .probe = aw8896_i2c_probe, |
| .remove = aw8896_i2c_remove, |
| .id_table = aw8896_i2c_id, |
| }; |
| |
| static int __init aw8896_i2c_init(void) |
| { |
| int ret = 0; |
| |
| pr_info("%s: aw8896 driver version %s\n", __func__, AW8896_VERSION); |
| |
| ret = i2c_add_driver(&aw8896_i2c_driver); |
| if (ret) { |
| pr_err("%s: fail to add aw8896 device into i2c\n", __func__); |
| return ret; |
| } |
| |
| return 0; |
| } |
| module_init(aw8896_i2c_init); |
| |
| static void __exit aw8896_i2c_exit(void) |
| { |
| i2c_del_driver(&aw8896_i2c_driver); |
| } |
| module_exit(aw8896_i2c_exit); |
| |
| MODULE_DESCRIPTION("ASoC AW8896 Smart PA Driver"); |
| MODULE_LICENSE("GPL v2"); |