diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-1012-Allo-boss2-driver.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-1012-Allo-boss2-driver.patch | 1185 |
1 files changed, 1185 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-1012-Allo-boss2-driver.patch b/target/linux/bcm27xx/patches-5.4/950-1012-Allo-boss2-driver.patch new file mode 100644 index 0000000000..aacbc7dcbe --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-1012-Allo-boss2-driver.patch @@ -0,0 +1,1185 @@ +From 51ce8712f4633484615b9982dc48038b3b86e235 Mon Sep 17 00:00:00 2001 +From: Sudeep <sudeepkumar@cem-solutions.net> +Date: Fri, 23 Oct 2020 15:47:17 +0530 +Subject: [PATCH] Allo boss2 driver + +Signed-off-by: Sudeep <sudeepkumar@cem-solutions.net> +--- + sound/soc/bcm/Kconfig | 9 + + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/allo-boss2-dac.c | 1133 ++++++++++++++++++++++++++++++++ + 3 files changed, 1144 insertions(+) + create mode 100644 sound/soc/bcm/allo-boss2-dac.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -258,6 +258,15 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC + help + Say Y or M if you want to add support for Allo Boss DAC. + ++config SND_BCM2708_SOC_ALLO_BOSS2_DAC ++ tristate "Support for Allo Boss2 DAC" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ depends on I2C ++ select REGMAP_I2C ++ select SND_AUDIO_GRAPH_CARD ++ help ++ Say Y or M if you want to add support for Allo Boss2 DAC. ++ + config SND_BCM2708_SOC_ALLO_DIGIONE + tristate "Support for Allo DigiOne" + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -33,6 +33,7 @@ snd-soc-digidac1-soundcard-objs := digid + snd-soc-dionaudio-loco-objs := dionaudio_loco.o + snd-soc-dionaudio-loco-v2-objs := dionaudio_loco-v2.o + snd-soc-allo-boss-dac-objs := allo-boss-dac.o ++snd-soc-allo-boss2-dac-objs := allo-boss2-dac.o + snd-soc-allo-piano-dac-objs := allo-piano-dac.o + snd-soc-allo-piano-dac-plus-objs := allo-piano-dac-plus.o + snd-soc-allo-katana-codec-objs := allo-katana-codec.o +@@ -63,6 +64,7 @@ obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += + obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o + obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2) += snd-soc-dionaudio-loco-v2.o + obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC) += snd-soc-allo-boss-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC) += snd-soc-allo-boss2-dac.o + obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o + obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS) += snd-soc-allo-piano-dac-plus.o + obj-$(CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC) += snd-soc-allo-katana-codec.o +--- /dev/null ++++ b/sound/soc/bcm/allo-boss2-dac.c +@@ -0,0 +1,1133 @@ ++/* ++ * Driver for the ALLO KATANA CODEC ++ * ++ * Author: Jaikumar <sudeepkumar@cem-solutions.net> ++ * Copyright 2018 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This 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/moduleparam.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/gpio/consumer.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/i2c.h> ++#include <linux/of_device.h> ++#include <linux/regmap.h> ++#include <linux/slab.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <sound/soc.h> ++#include <sound/soc-dapm.h> ++#include <sound/initval.h> ++#include <sound/tlv.h> ++#include <linux/of_gpio.h> ++#include <linux/regulator/consumer.h> ++#include <linux/pm_runtime.h> ++#include <linux/of_irq.h> ++#include <linux/completion.h> ++#include <linux/mutex.h> ++#include <linux/workqueue.h> ++#include <sound/jack.h> ++ ++#include "../codecs/cs43130.h" ++ ++#include <linux/clk.h> ++#include <linux/gcd.h> ++#define DEBUG ++ ++#define CS43130_DSD_EN_MASK 0x10 ++#define CS43130_PDN_DONE_INT_MASK 0x00 ++ ++static struct gpio_desc *snd_allo_clk44gpio; ++static struct gpio_desc *snd_allo_clk48gpio; ++ ++struct cs43130_priv { ++ struct snd_soc_component *component; ++ struct regmap *regmap; ++ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES]; ++ struct gpio_desc *reset_gpio; ++ unsigned int dev_id; /* codec device ID */ ++ int xtal_ibias; ++ /* shared by both DAIs */ ++ struct mutex clk_mutex; ++ int clk_req; ++ bool pll_bypass; ++ struct completion xtal_rdy; ++ struct completion pll_rdy; ++ unsigned int mclk; ++ unsigned int mclk_int; ++ int mclk_int_src; ++ ++ /* DAI specific */ ++ struct cs43130_dai dais[CS43130_DAI_ID_MAX]; ++ ++ /* HP load specific */ ++ bool dc_meas; ++ bool ac_meas; ++ bool hpload_done; ++ struct completion hpload_evt; ++ unsigned int hpload_stat; ++ u16 hpload_dc[2]; ++ u16 dc_threshold[CS43130_DC_THRESHOLD]; ++ u16 ac_freq[CS43130_AC_FREQ]; ++ u16 hpload_ac[CS43130_AC_FREQ][2]; ++ struct workqueue_struct *wq; ++ struct work_struct work; ++ struct snd_soc_jack jack; ++}; ++ ++static const struct reg_default cs43130_reg_defaults[] = { ++ {CS43130_SYS_CLK_CTL_1, 0x06}, ++ {CS43130_SP_SRATE, 0x01}, ++ {CS43130_SP_BITSIZE, 0x05}, ++ {CS43130_PAD_INT_CFG, 0x03}, ++ {CS43130_PWDN_CTL, 0xFE}, ++ {CS43130_CRYSTAL_SET, 0x04}, ++ {CS43130_PLL_SET_1, 0x00}, ++ {CS43130_PLL_SET_2, 0x00}, ++ {CS43130_PLL_SET_3, 0x00}, ++ {CS43130_PLL_SET_4, 0x00}, ++ {CS43130_PLL_SET_5, 0x40}, ++ {CS43130_PLL_SET_6, 0x10}, ++ {CS43130_PLL_SET_7, 0x80}, ++ {CS43130_PLL_SET_8, 0x03}, ++ {CS43130_PLL_SET_9, 0x02}, ++ {CS43130_PLL_SET_10, 0x02}, ++ {CS43130_CLKOUT_CTL, 0x00}, ++ {CS43130_ASP_NUM_1, 0x01}, ++ {CS43130_ASP_NUM_2, 0x00}, ++ {CS43130_ASP_DEN_1, 0x08}, ++ {CS43130_ASP_DEN_2, 0x00}, ++ {CS43130_ASP_LRCK_HI_TIME_1, 0x1F}, ++ {CS43130_ASP_LRCK_HI_TIME_2, 0x00}, ++ {CS43130_ASP_LRCK_PERIOD_1, 0x3F}, ++ {CS43130_ASP_LRCK_PERIOD_2, 0x00}, ++ {CS43130_ASP_CLOCK_CONF, 0x0C}, ++ {CS43130_ASP_FRAME_CONF, 0x0A}, ++ {CS43130_XSP_NUM_1, 0x01}, ++ {CS43130_XSP_NUM_2, 0x00}, ++ {CS43130_XSP_DEN_1, 0x02}, ++ {CS43130_XSP_DEN_2, 0x00}, ++ {CS43130_XSP_LRCK_HI_TIME_1, 0x1F}, ++ {CS43130_XSP_LRCK_HI_TIME_2, 0x00}, ++ {CS43130_XSP_LRCK_PERIOD_1, 0x3F}, ++ {CS43130_XSP_LRCK_PERIOD_2, 0x00}, ++ {CS43130_XSP_CLOCK_CONF, 0x0C}, ++ {CS43130_XSP_FRAME_CONF, 0x0A}, ++ {CS43130_ASP_CH_1_LOC, 0x00}, ++ {CS43130_ASP_CH_2_LOC, 0x00}, ++ {CS43130_ASP_CH_1_SZ_EN, 0x06}, ++ {CS43130_ASP_CH_2_SZ_EN, 0x0E}, ++ {CS43130_XSP_CH_1_LOC, 0x00}, ++ {CS43130_XSP_CH_2_LOC, 0x00}, ++ {CS43130_XSP_CH_1_SZ_EN, 0x06}, ++ {CS43130_XSP_CH_2_SZ_EN, 0x0E}, ++ {CS43130_DSD_VOL_B, 0x78}, ++ {CS43130_DSD_VOL_A, 0x78}, ++ {CS43130_DSD_PATH_CTL_1, 0xA8}, ++ {CS43130_DSD_INT_CFG, 0x00}, ++ {CS43130_DSD_PATH_CTL_2, 0x02}, ++ {CS43130_DSD_PCM_MIX_CTL, 0x00}, ++ {CS43130_DSD_PATH_CTL_3, 0x40}, ++ {CS43130_HP_OUT_CTL_1, 0x30}, ++ {CS43130_PCM_FILT_OPT, 0x02}, ++ {CS43130_PCM_VOL_B, 0x78}, ++ {CS43130_PCM_VOL_A, 0x78}, ++ {CS43130_PCM_PATH_CTL_1, 0xA8}, ++ {CS43130_PCM_PATH_CTL_2, 0x00}, ++ {CS43130_CLASS_H_CTL, 0x1E}, ++ {CS43130_HP_DETECT, 0x04}, ++ {CS43130_HP_LOAD_1, 0x00}, ++ {CS43130_HP_MEAS_LOAD_1, 0x00}, ++ {CS43130_HP_MEAS_LOAD_2, 0x00}, ++ {CS43130_INT_MASK_1, 0xFF}, ++ {CS43130_INT_MASK_2, 0xFF}, ++ {CS43130_INT_MASK_3, 0xFF}, ++ {CS43130_INT_MASK_4, 0xFF}, ++ {CS43130_INT_MASK_5, 0xFF}, ++}; ++static bool cs43130_volatile_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: ++ case CS43130_HP_DC_STAT_1 ... CS43130_HP_DC_STAT_2: ++ case CS43130_HP_AC_STAT_1 ... CS43130_HP_AC_STAT_2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const char * const pcm_spd_texts[] = { ++ "Fast", ++ "Slow", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(pcm_spd_enum, CS43130_PCM_FILT_OPT, 7, ++ pcm_spd_texts); ++ ++static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(master_tlv, -12750, 0); ++ ++static const struct snd_kcontrol_new cs43130_controls[] = { ++ SOC_DOUBLE_R_TLV("Master Playback Volume", CS43130_PCM_VOL_B, ++ CS43130_PCM_VOL_A, 0, 255, 1, master_tlv), ++ SOC_DOUBLE("Master Playback Switch", CS43130_PCM_PATH_CTL_1, ++ 0, 1, 1, 1), ++ SOC_DOUBLE_R_TLV("Digital Playback Volume", CS43130_DSD_VOL_B, ++ CS43130_DSD_VOL_A, 0, 255, 1, master_tlv), ++ SOC_DOUBLE("Digital Playback Switch", CS43130_DSD_PATH_CTL_1, ++ 0, 1, 1, 1), ++ SOC_SINGLE("HV_Enable", CS43130_HP_OUT_CTL_1, 0, 1, 0), ++ SOC_ENUM("PCM Filter Speed", pcm_spd_enum), ++ SOC_SINGLE("PCM Phase Compensation", CS43130_PCM_FILT_OPT, 6, 1, 0), ++ SOC_SINGLE("PCM Nonoversample Emulate", CS43130_PCM_FILT_OPT, 5, 1, 0), ++ SOC_SINGLE("PCM High-pass Filter", CS43130_PCM_FILT_OPT, 1, 1, 0), ++ SOC_SINGLE("PCM De-emphasis Filter", CS43130_PCM_FILT_OPT, 0, 1, 0), ++}; ++ ++static bool cs43130_readable_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1: ++ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG: ++ case CS43130_PWDN_CTL: ++ case CS43130_CRYSTAL_SET: ++ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5: ++ case CS43130_PLL_SET_6: ++ case CS43130_PLL_SET_7: ++ case CS43130_PLL_SET_8: ++ case CS43130_PLL_SET_9: ++ case CS43130_PLL_SET_10: ++ case CS43130_CLKOUT_CTL: ++ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF: ++ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF: ++ case CS43130_ASP_CH_1_LOC: ++ case CS43130_ASP_CH_2_LOC: ++ case CS43130_ASP_CH_1_SZ_EN: ++ case CS43130_ASP_CH_2_SZ_EN: ++ case CS43130_XSP_CH_1_LOC: ++ case CS43130_XSP_CH_2_LOC: ++ case CS43130_XSP_CH_1_SZ_EN: ++ case CS43130_XSP_CH_2_SZ_EN: ++ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3: ++ case CS43130_HP_OUT_CTL_1: ++ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2: ++ case CS43130_CLASS_H_CTL: ++ case CS43130_HP_DETECT: ++ case CS43130_HP_STATUS: ++ case CS43130_HP_LOAD_1: ++ case CS43130_HP_MEAS_LOAD_1: ++ case CS43130_HP_MEAS_LOAD_2: ++ case CS43130_HP_DC_STAT_1: ++ case CS43130_HP_DC_STAT_2: ++ case CS43130_HP_AC_STAT_1: ++ case CS43130_HP_AC_STAT_2: ++ case CS43130_HP_LOAD_STAT: ++ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: ++ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5: ++ return true; ++ default: ++ return false; ++ } ++} ++static bool cs43130_precious_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: ++ return true; ++ default: ++ return false; ++ } ++} ++static int cs43130_pcm_pdn(struct snd_soc_component *component) ++{ ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ int ret; ++ unsigned int reg, pdn_int; ++ ++ regmap_write(cs43130->regmap, CS43130_DSD_PATH_CTL_2, 0x02); ++ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, ++ CS43130_PDN_DONE_INT_MASK, 0); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_HP_MASK, 1 << CS43130_PDN_HP_SHIFT); ++ usleep_range(10, 50); ++ ret = regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, ®); ++ pdn_int = reg & 0xFE; ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_ASP_MASK, 1 << CS43130_PDN_ASP_SHIFT); ++ return 0; ++ ++} ++static int cs43130_pwr_up_asp_dac(struct snd_soc_component *component) ++{ ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ ++ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG, ++ CS43130_ASP_3ST_MASK, 0); ++ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99); ++ regmap_write(cs43130->regmap, CS43130_DXD13, 0x20); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_ASP_MASK, 0); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_HP_MASK, 0); ++ usleep_range(10000, 12000); ++ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00); ++ regmap_write(cs43130->regmap, CS43130_DXD13, 0x00); ++ return 0; ++} ++static int cs43130_change_clksrc(struct snd_soc_component *component, ++ enum cs43130_mclk_src_sel src) ++{ ++ int ret; ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ int mclk_int_decoded; ++ ++ if (src == cs43130->mclk_int_src) { ++ /* clk source has not changed */ ++ return 0; ++ } ++ switch (cs43130->mclk_int) { ++ case CS43130_MCLK_22M: ++ mclk_int_decoded = CS43130_MCLK_22P5; ++ break; ++ case CS43130_MCLK_24M: ++ mclk_int_decoded = CS43130_MCLK_24P5; ++ break; ++ default: ++ dev_err(component->dev, "Invalid MCLK INT freq: %u\n", ++ cs43130->mclk_int); ++ return -EINVAL; ++ } ++ ++ switch (src) { ++ case CS43130_MCLK_SRC_EXT: ++ cs43130->pll_bypass = true; ++ cs43130->mclk_int_src = CS43130_MCLK_SRC_EXT; ++ if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) { ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_XTAL_MASK, ++ 1 << CS43130_PDN_XTAL_SHIFT); ++ } else { ++ reinit_completion(&cs43130->xtal_rdy); ++ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, ++ CS43130_XTAL_RDY_INT_MASK, 0); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_XTAL_MASK, 0); ++ ret = wait_for_completion_timeout(&cs43130->xtal_rdy, ++ msecs_to_jiffies(100)); ++ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, ++ CS43130_XTAL_RDY_INT_MASK, ++ 1 << CS43130_XTAL_RDY_INT_SHIFT); ++ if (ret == 0) { ++ dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n"); ++ return -ETIMEDOUT; ++ } ++ } ++ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, ++ CS43130_MCLK_SRC_SEL_MASK, ++ src << CS43130_MCLK_SRC_SEL_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, ++ CS43130_MCLK_INT_MASK, ++ mclk_int_decoded << CS43130_MCLK_INT_SHIFT); ++ usleep_range(150, 200); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_PLL_MASK, ++ 1 << CS43130_PDN_PLL_SHIFT); ++ break; ++ case CS43130_MCLK_SRC_RCO: ++ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO; ++ ++ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, ++ CS43130_MCLK_SRC_SEL_MASK, ++ src << CS43130_MCLK_SRC_SEL_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, ++ CS43130_MCLK_INT_MASK, ++ CS43130_MCLK_22P5 << CS43130_MCLK_INT_SHIFT); ++ usleep_range(150, 200); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_XTAL_MASK, ++ 1 << CS43130_PDN_XTAL_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_PLL_MASK, ++ 1 << CS43130_PDN_PLL_SHIFT); ++ break; ++ default: ++ dev_err(component->dev, "Invalid MCLK source value\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++static const struct cs43130_bitwidth_map cs43130_bitwidth_table[] = { ++ {8, CS43130_SP_BIT_SIZE_8, CS43130_CH_BIT_SIZE_8}, ++ {16, CS43130_SP_BIT_SIZE_16, CS43130_CH_BIT_SIZE_16}, ++ {24, CS43130_SP_BIT_SIZE_24, CS43130_CH_BIT_SIZE_24}, ++ {32, CS43130_SP_BIT_SIZE_32, CS43130_CH_BIT_SIZE_32}, ++}; ++ ++static const struct cs43130_bitwidth_map *cs43130_get_bitwidth_table( ++ unsigned int bitwidth) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(cs43130_bitwidth_table); i++) { ++ if (cs43130_bitwidth_table[i].bitwidth == bitwidth) ++ return &cs43130_bitwidth_table[i]; ++ } ++ ++ return NULL; ++} ++static int cs43130_set_bitwidth(int dai_id, unsigned int bitwidth_dai, ++ struct regmap *regmap) ++{ ++ const struct cs43130_bitwidth_map *bw_map; ++ ++ bw_map = cs43130_get_bitwidth_table(bitwidth_dai); ++ if (!bw_map) ++ return -EINVAL; ++ ++ switch (dai_id) { ++ case CS43130_ASP_PCM_DAI: ++ case CS43130_ASP_DOP_DAI: ++ regmap_update_bits(regmap, CS43130_ASP_CH_1_SZ_EN, ++ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); ++ regmap_update_bits(regmap, CS43130_ASP_CH_2_SZ_EN, ++ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); ++ regmap_update_bits(regmap, CS43130_SP_BITSIZE, ++ CS43130_ASP_BITSIZE_MASK, bw_map->sp_bit); ++ break; ++ case CS43130_XSP_DOP_DAI: ++ regmap_update_bits(regmap, CS43130_XSP_CH_1_SZ_EN, ++ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); ++ regmap_update_bits(regmap, CS43130_XSP_CH_2_SZ_EN, ++ CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); ++ regmap_update_bits(regmap, CS43130_SP_BITSIZE, ++ CS43130_XSP_BITSIZE_MASK, bw_map->sp_bit << ++ CS43130_XSP_BITSIZE_SHIFT); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++static const struct cs43130_rate_map cs43130_rate_table[] = { ++ {32000, CS43130_ASP_SPRATE_32K}, ++ {44100, CS43130_ASP_SPRATE_44_1K}, ++ {48000, CS43130_ASP_SPRATE_48K}, ++ {88200, CS43130_ASP_SPRATE_88_2K}, ++ {96000, CS43130_ASP_SPRATE_96K}, ++ {176400, CS43130_ASP_SPRATE_176_4K}, ++ {192000, CS43130_ASP_SPRATE_192K}, ++ {352800, CS43130_ASP_SPRATE_352_8K}, ++ {384000, CS43130_ASP_SPRATE_384K}, ++}; ++ ++static const struct cs43130_rate_map *cs43130_get_rate_table(int fs) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(cs43130_rate_table); i++) { ++ if (cs43130_rate_table[i].fs == fs) ++ return &cs43130_rate_table[i]; ++ } ++ ++ return NULL; ++} ++ ++static const struct cs43130_clk_gen *cs43130_get_clk_gen(int mclk_int, int fs, ++ const struct cs43130_clk_gen *clk_gen_table, int len_clk_gen_table) ++{ ++ int i; ++ ++ for (i = 0; i < len_clk_gen_table; i++) { ++ if (clk_gen_table[i].mclk_int == mclk_int && ++ clk_gen_table[i].fs == fs) ++ return &clk_gen_table[i]; ++ } ++ return NULL; ++} ++ ++static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, ++ struct snd_pcm_hw_params *params, ++ struct cs43130_priv *cs43130) ++{ ++ u16 frm_size; ++ u16 hi_size; ++ u8 frm_delay; ++ u8 frm_phase; ++ u8 frm_data; ++ u8 sclk_edge; ++ u8 lrck_edge; ++ u8 clk_data; ++ u8 loc_ch1; ++ u8 loc_ch2; ++ u8 dai_mode_val; ++ const struct cs43130_clk_gen *clk_gen; ++ ++ switch (cs43130->dais[dai_id].dai_format) { ++ case SND_SOC_DAIFMT_I2S: ++ hi_size = bitwidth_sclk; ++ frm_delay = 2; ++ frm_phase = 0; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ hi_size = bitwidth_sclk; ++ frm_delay = 2; ++ frm_phase = 1; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ hi_size = 1; ++ frm_delay = 2; ++ frm_phase = 1; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ hi_size = 1; ++ frm_delay = 0; ++ frm_phase = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ switch (cs43130->dais[dai_id].dai_mode) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ dai_mode_val = 0; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ dai_mode_val = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ frm_size = bitwidth_sclk * params_channels(params); ++ sclk_edge = 1; ++ lrck_edge = 0; ++ loc_ch1 = 0; ++ loc_ch2 = bitwidth_sclk * (params_channels(params) - 1); ++ ++ frm_data = frm_delay & CS43130_SP_FSD_MASK; ++ frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK; ++ ++ clk_data = lrck_edge & CS43130_SP_LCPOL_IN_MASK; ++ clk_data |= (lrck_edge << CS43130_SP_LCPOL_OUT_SHIFT) & ++ CS43130_SP_LCPOL_OUT_MASK; ++ clk_data |= (sclk_edge << CS43130_SP_SCPOL_IN_SHIFT) & ++ CS43130_SP_SCPOL_IN_MASK; ++ clk_data |= (sclk_edge << CS43130_SP_SCPOL_OUT_SHIFT) & ++ CS43130_SP_SCPOL_OUT_MASK; ++ clk_data |= (dai_mode_val << CS43130_SP_MODE_SHIFT) & ++ CS43130_SP_MODE_MASK; ++ switch (dai_id) { ++ case CS43130_ASP_PCM_DAI: ++ case CS43130_ASP_DOP_DAI: ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1, ++ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> ++ CS43130_SP_LCPR_LSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2, ++ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> ++ CS43130_SP_LCPR_MSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1, ++ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> ++ CS43130_SP_LCHI_LSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2, ++ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> ++ CS43130_SP_LCHI_MSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_ASP_FRAME_CONF, frm_data); ++ regmap_write(cs43130->regmap, CS43130_ASP_CH_1_LOC, loc_ch1); ++ regmap_write(cs43130->regmap, CS43130_ASP_CH_2_LOC, loc_ch2); ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN, ++ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN, ++ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_ASP_CLOCK_CONF, clk_data); ++ break; ++ case CS43130_XSP_DOP_DAI: ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_1, ++ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> ++ CS43130_SP_LCPR_LSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_2, ++ CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> ++ CS43130_SP_LCPR_MSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_1, ++ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> ++ CS43130_SP_LCHI_LSB_DATA_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_2, ++ CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> ++ CS43130_SP_LCHI_MSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_XSP_FRAME_CONF, frm_data); ++ regmap_write(cs43130->regmap, CS43130_XSP_CH_1_LOC, loc_ch1); ++ regmap_write(cs43130->regmap, CS43130_XSP_CH_2_LOC, loc_ch2); ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_1_SZ_EN, ++ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_2_SZ_EN, ++ CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_XSP_CLOCK_CONF, clk_data); ++ break; ++ default: ++ return -EINVAL; ++ } ++ switch (frm_size) { ++ case 16: ++ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, ++ params_rate(params), ++ cs43130_16_clk_gen, ++ ARRAY_SIZE(cs43130_16_clk_gen)); ++ break; ++ case 32: ++ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, ++ params_rate(params), ++ cs43130_32_clk_gen, ++ ARRAY_SIZE(cs43130_32_clk_gen)); ++ break; ++ case 48: ++ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, ++ params_rate(params), ++ cs43130_48_clk_gen, ++ ARRAY_SIZE(cs43130_48_clk_gen)); ++ break; ++ case 64: ++ clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, ++ params_rate(params), ++ cs43130_64_clk_gen, ++ ARRAY_SIZE(cs43130_64_clk_gen)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (!clk_gen) ++ return -EINVAL; ++ switch (dai_id) { ++ case CS43130_ASP_PCM_DAI: ++ case CS43130_ASP_DOP_DAI: ++ regmap_write(cs43130->regmap, CS43130_ASP_DEN_1, ++ (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >> ++ CS43130_SP_M_LSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_ASP_DEN_2, ++ (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >> ++ CS43130_SP_M_MSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, ++ (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >> ++ CS43130_SP_N_LSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, ++ (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >> ++ CS43130_SP_N_MSB_DATA_SHIFT); ++ break; ++ case CS43130_XSP_DOP_DAI: ++ regmap_write(cs43130->regmap, CS43130_XSP_DEN_1, ++ (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >> ++ CS43130_SP_M_LSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_XSP_DEN_2, ++ (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >> ++ CS43130_SP_M_MSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_XSP_NUM_1, ++ (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >> ++ CS43130_SP_N_LSB_DATA_SHIFT); ++ regmap_write(cs43130->regmap, CS43130_XSP_NUM_2, ++ (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >> ++ CS43130_SP_N_MSB_DATA_SHIFT); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int cs43130_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ const struct cs43130_rate_map *rate_map; ++ unsigned int sclk = cs43130->dais[dai->id].sclk; ++ unsigned int bitwidth_sclk; ++ unsigned int bitwidth_dai = (unsigned int)(params_width(params)); ++ unsigned int dop_rate = (unsigned int)(params_rate(params)); ++ unsigned int required_clk, ret; ++ u8 dsd_speed; ++ ++ cs43130->pll_bypass = true; ++ cs43130_pcm_pdn(component); ++ mutex_lock(&cs43130->clk_mutex); ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ if (!(CS43130_MCLK_22M % params_rate(params))) { ++ required_clk = CS43130_MCLK_22M; ++ cs43130->mclk_int = CS43130_MCLK_22M; ++ gpiod_set_value_cansleep(snd_allo_clk44gpio, 1); ++ gpiod_set_value_cansleep(snd_allo_clk48gpio, 0); ++ usleep_range(13500, 14000); ++ } else { ++ required_clk = CS43130_MCLK_24M; ++ cs43130->mclk_int = CS43130_MCLK_24M; ++ gpiod_set_value_cansleep(snd_allo_clk48gpio, 1); ++ gpiod_set_value_cansleep(snd_allo_clk44gpio, 0); ++ usleep_range(13500, 14000); ++ } ++ if (cs43130->pll_bypass) ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); ++ else ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ } ++ ++ cs43130->clk_req++; ++ mutex_unlock(&cs43130->clk_mutex); ++ ++ switch (dai->id) { ++ case CS43130_ASP_DOP_DAI: ++ case CS43130_XSP_DOP_DAI: ++ /* DoP bitwidth is always 24-bit */ ++ bitwidth_dai = 24; ++ sclk = params_rate(params) * bitwidth_dai * ++ params_channels(params); ++ ++ switch (params_rate(params)) { ++ case 176400: ++ dsd_speed = 0; ++ break; ++ case 352800: ++ dsd_speed = 1; ++ break; ++ default: ++ dev_err(component->dev, "Rate(%u) not supported\n", ++ params_rate(params)); ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_SPEED_MASK, ++ dsd_speed << CS43130_DSD_SPEED_SHIFT); ++ break; ++ case CS43130_ASP_PCM_DAI: ++ rate_map = cs43130_get_rate_table(params_rate(params)); ++ if (!rate_map) ++ return -EINVAL; ++ ++ regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val); ++ if ((dop_rate == 176400) && (bitwidth_dai == 24)) { ++ dsd_speed = 0; ++ regmap_update_bits(cs43130->regmap, ++ CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_SPEED_MASK, ++ dsd_speed << CS43130_DSD_SPEED_SHIFT); ++ regmap_update_bits(cs43130->regmap, ++ CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_SRC_MASK, ++ CS43130_DSD_SRC_ASP << ++ CS43130_DSD_SRC_SHIFT); ++ regmap_update_bits(cs43130->regmap, ++ CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_EN_MASK, 0x01 << ++ CS43130_DSD_EN_SHIFT); ++ } ++ break; ++ default: ++ dev_err(component->dev, "Invalid DAI (%d)\n", dai->id); ++ return -EINVAL; ++ } ++ ++ switch (dai->id) { ++ case CS43130_ASP_DOP_DAI: ++ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_ASP << ++ CS43130_DSD_SRC_SHIFT); ++ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_EN_MASK, 0x01 << ++ CS43130_DSD_EN_SHIFT); ++ break; ++ case CS43130_XSP_DOP_DAI: ++ regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, ++ CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << ++ CS43130_DSD_SRC_SHIFT); ++ break; ++ } ++ if (!sclk && cs43130->dais[dai->id].dai_mode == ++ SND_SOC_DAIFMT_CBM_CFM) { ++ /* Calculate SCLK in master mode if unassigned */ ++ sclk = params_rate(params) * bitwidth_dai * ++ params_channels(params); ++ } ++ if (!sclk) { ++ /* at this point, SCLK must be set */ ++ dev_err(component->dev, "SCLK freq is not set\n"); ++ return -EINVAL; ++ } ++ ++ bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params); ++ if (bitwidth_sclk < bitwidth_dai) { ++ dev_err(component->dev, "Format not supported: SCLK freq is too low\n"); ++ return -EINVAL; ++ } ++ ++ dev_dbg(component->dev, ++ "sclk = %u, fs = %d, bitwidth_dai = %u\n", ++ sclk, params_rate(params), bitwidth_dai); ++ ++ dev_dbg(component->dev, ++ "bitwidth_sclk = %u, num_ch = %u\n", ++ bitwidth_sclk, params_channels(params)); ++ ++ cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap); ++ cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130); ++ ret = cs43130_pwr_up_asp_dac(component); ++ return 0; ++} ++ ++static int cs43130_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ ++ mutex_lock(&cs43130->clk_mutex); ++ cs43130->clk_req--; ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); ++ cs43130_pcm_pdn(component); ++ } ++ mutex_unlock(&cs43130->clk_mutex); ++ ++ return 0; ++} ++ ++static const unsigned int cs43130_asp_src_rates[] = { ++ 32000, 44100, 48000, 88200, 96000, 176400, 192000 ++}; ++ ++static const struct snd_pcm_hw_constraint_list cs43130_asp_constraints = { ++ .count = ARRAY_SIZE(cs43130_asp_src_rates), ++ .list = cs43130_asp_src_rates, ++}; ++ ++static int cs43130_pcm_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ return snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &cs43130_asp_constraints); ++} ++ ++static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ++{ ++ struct snd_soc_component *component = codec_dai->component; ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; ++ break; ++ default: ++ dev_err(component->dev, "unsupported mode\n"); ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_I2S; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_LEFT_J; ++ break; ++ default: ++ dev_err(component->dev, ++ "unsupported audio format\n"); ++ return -EINVAL; ++ } ++ ++ dev_dbg(component->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n", ++ codec_dai->id, ++ cs43130->dais[codec_dai->id].dai_mode, ++ cs43130->dais[codec_dai->id].dai_format); ++ ++ return 0; ++} ++ ++static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ struct snd_soc_component *component = codec_dai->component; ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ ++ cs43130->dais[codec_dai->id].sclk = freq; ++ dev_dbg(component->dev, "dai_id = %d, sclk = %u\n", codec_dai->id, ++ cs43130->dais[codec_dai->id].sclk); ++ ++ return 0; ++} ++ ++static int cs43130_component_set_sysclk(struct snd_soc_component *component, ++ int clk_id, int source, ++ unsigned int freq, int dir) ++{ ++ struct cs43130_priv *cs43130 = ++ snd_soc_component_get_drvdata(component); ++ ++ dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n", ++ clk_id, source, freq, dir); ++ ++ switch (freq) { ++ case CS43130_MCLK_22M: ++ case CS43130_MCLK_24M: ++ cs43130->mclk = freq; ++ break; ++ default: ++ dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq); ++ return -EINVAL; ++ } ++ ++ if (source == CS43130_MCLK_SRC_EXT) { ++ cs43130->pll_bypass = true; ++ } else { ++ dev_err(component->dev, "Invalid MCLK source\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++static u16 const cs43130_ac_freq[CS43130_AC_FREQ] = { ++ 24, ++ 43, ++ 93, ++ 200, ++ 431, ++ 928, ++ 2000, ++ 4309, ++ 9283, ++ 20000, ++}; ++static const struct snd_soc_dai_ops cs43130_dai_ops = { ++ .startup = cs43130_pcm_startup, ++ .hw_params = cs43130_hw_params, ++ .hw_free = cs43130_hw_free, ++ .set_sysclk = cs43130_set_sysclk, ++ .set_fmt = cs43130_pcm_set_fmt, ++}; ++ ++static struct snd_soc_dai_driver cs43130_codec_dai = { ++ .name = "allo-cs43130", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .rate_min = 44100, ++ .rate_max = 192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE ++ ++ }, ++ .ops = &cs43130_dai_ops, ++}; ++ ++static struct snd_soc_component_driver cs43130_component_driver = { ++ .idle_bias_on = true, ++ .controls = cs43130_controls, ++ .num_controls = ARRAY_SIZE(cs43130_controls), ++ .set_sysclk = cs43130_component_set_sysclk, ++ .idle_bias_on = 1, ++ .use_pmdown_time = 1, ++ .endianness = 1, ++ .non_legacy_dai_naming = 1, ++}; ++ ++static const struct regmap_config cs43130_regmap = { ++ .reg_bits = 24, ++ .pad_bits = 8, ++ .val_bits = 8, ++ ++ .max_register = CS43130_LASTREG, ++ .reg_defaults = cs43130_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults), ++ .readable_reg = cs43130_readable_register, ++ .precious_reg = cs43130_precious_register, ++ .volatile_reg = cs43130_volatile_register, ++ .cache_type = REGCACHE_RBTREE, ++ /* needed for regcache_sync */ ++ .use_single_read = true, ++ .use_single_write = true, ++}; ++ ++static u16 const cs43130_dc_threshold[CS43130_DC_THRESHOLD] = { ++ 50, ++ 120, ++}; ++ ++static int cs43130_handle_device_data(struct i2c_client *i2c_client, ++ struct cs43130_priv *cs43130) ++{ ++ struct device_node *np = i2c_client->dev.of_node; ++ unsigned int val; ++ int i; ++ ++ if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) { ++ /* Crystal is unused. System clock is used for external MCLK */ ++ cs43130->xtal_ibias = CS43130_XTAL_UNUSED; ++ return 0; ++ } ++ ++ switch (val) { ++ case 1: ++ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA; ++ break; ++ case 2: ++ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA; ++ break; ++ case 3: ++ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA; ++ break; ++ default: ++ dev_err(&i2c_client->dev, ++ "Invalid cirrus,xtal-ibias value: %d\n", val); ++ return -EINVAL; ++ } ++ ++ cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure"); ++ cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure"); ++ ++ if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq, ++ CS43130_AC_FREQ) < 0) { ++ for (i = 0; i < CS43130_AC_FREQ; i++) ++ cs43130->ac_freq[i] = cs43130_ac_freq[i]; ++ } ++ ++ if (of_property_read_u16_array(np, "cirrus,dc-threshold", ++ cs43130->dc_threshold, ++ CS43130_DC_THRESHOLD) < 0) { ++ for (i = 0; i < CS43130_DC_THRESHOLD; i++) ++ cs43130->dc_threshold[i] = cs43130_dc_threshold[i]; ++ } ++ ++ return 0; ++} ++ ++ ++static int allo_cs43130_component_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct regmap *regmap; ++ struct regmap_config config = cs43130_regmap; ++ struct device *dev = &i2c->dev; ++ struct cs43130_priv *cs43130; ++ unsigned int devid = 0; ++ unsigned int reg; ++ int ret; ++ ++ regmap = devm_regmap_init_i2c(i2c, &config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ cs43130 = devm_kzalloc(dev, sizeof(struct cs43130_priv), ++ GFP_KERNEL); ++ if (!cs43130) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, cs43130); ++ cs43130->regmap = regmap; ++ ++ if (i2c->dev.of_node) { ++ ret = cs43130_handle_device_data(i2c, cs43130); ++ if (ret != 0) ++ return ret; ++ } ++ usleep_range(2000, 2050); ++ ++ ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, ®); ++ devid = (reg & 0xFF) << 12; ++ ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, ®); ++ devid |= (reg & 0xFF) << 4; ++ ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, ®); ++ devid |= (reg & 0xF0) >> 4; ++ if (devid != CS43198_CHIP_ID) { ++ dev_err(dev, "Failed to read Chip or wrong Chip id: %d\n", ret); ++ return ret; ++ } ++ ++ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO; ++ msleep(20); ++ ++ ret = snd_soc_register_component(dev, &cs43130_component_driver, ++ &cs43130_codec_dai, 1); ++ if (ret != 0) { ++ dev_err(dev, "failed to register codec: %d\n", ret); ++ return ret; ++ } ++ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG, ++ CS43130_ASP_3ST_MASK, 0); ++ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG, ++ CS43130_XSP_3ST_MASK, 1); ++ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, ++ CS43130_PDN_HP_MASK, 1 << CS43130_PDN_HP_SHIFT); ++ msleep(20); ++ regmap_write(cs43130->regmap, CS43130_CLASS_H_CTL, 0x06); ++ snd_allo_clk44gpio = devm_gpiod_get(dev, "clock44", GPIOD_OUT_HIGH); ++ if (IS_ERR(snd_allo_clk44gpio)) ++ dev_err(dev, "devm_gpiod_get() failed\n"); ++ ++ snd_allo_clk48gpio = devm_gpiod_get(dev, "clock48", GPIOD_OUT_LOW); ++ if (IS_ERR(snd_allo_clk48gpio)) ++ dev_err(dev, "devm_gpiod_get() failed\n"); ++ ++ return 0; ++} ++ ++static int allo_cs43130_component_remove(struct i2c_client *i2c) ++{ ++ snd_soc_unregister_component(&i2c->dev); ++ return 0; ++} ++ ++static const struct i2c_device_id allo_cs43130_component_id[] = { ++ { "allo-cs43198", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, allo_cs43130_component_id); ++ ++static const struct of_device_id allo_cs43130_codec_of_match[] = { ++ { .compatible = "allo,allo-cs43198", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, allo_cs43130_codec_of_match); ++ ++static struct i2c_driver allo_cs43130_component_driver = { ++ .probe = allo_cs43130_component_probe, ++ .remove = allo_cs43130_component_remove, ++ .id_table = allo_cs43130_component_id, ++ .driver = { ++ .name = "allo-cs43198", ++ .of_match_table = allo_cs43130_codec_of_match, ++ }, ++}; ++ ++module_i2c_driver(allo_cs43130_component_driver); ++ ++MODULE_DESCRIPTION("ASoC Allo Boss2 Codec Driver"); ++MODULE_AUTHOR("Sudeepkumar <sudeepkumar@cem-solutions.net>"); ++MODULE_LICENSE("GPL v2"); |