[][Add Proslic SI3218x audio codec driver]
[Description]
Add Proslic SI3218x audio codec driver
[Release-log]
N/A
Change-Id: I30edf714fda413b220b6a0f3e301a7bc3f899c08
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5396999
diff --git a/target/linux/mediatek/files-5.4/sound/soc/codecs/si3218x/si3218x.c b/target/linux/mediatek/files-5.4/sound/soc/codecs/si3218x/si3218x.c
new file mode 100644
index 0000000..efb6818
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/sound/soc/codecs/si3218x/si3218x.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "proslic_sys.h"
+#include "si3218x.h"
+#include "inc/proslic.h"
+#include "timer.h"
+
+#include "spi.h"
+
+#ifdef SI3218X
+#include "inc/si3218x.h"
+#include "inc/si3218x_intf.h"
+#endif
+
+#define NUMBER_OF_DEVICES 1
+#if defined(SI3218X)
+#define CHAN_PER_DEVICE 1
+#define NUMBER_OF_CHAN (NUMBER_OF_DEVICES*CHAN_PER_DEVICE)
+#define NUMBER_OF_PROSLIC (NUMBER_OF_CHAN)
+#define PROSLIC_DEVICE_TYPE SI3218X_TYPE
+#endif
+
+static int slic_init = 0;
+
+ctrl_S spiGciObj; /* User¡¦s control interface object */
+systemTimer_S timerObj; /* User¡¦s timer object */
+chanState ports[NUMBER_OF_CHAN]; /* User¡¦s channel object, which has
+ ** a member defined as
+ ** proslicChanType_ptr ProObj;
+ */
+/* Define ProSLIC control interface object */
+controlInterfaceType *ProHWIntf;
+/* Define array of ProSLIC device objects */
+ProslicDeviceType *ProSLICDevices[NUMBER_OF_PROSLIC];
+/* Define array of ProSLIC channel object pointers */
+proslicChanType_ptr arrayOfProslicChans[NUMBER_OF_CHAN];
+
+static int ProSLIC_HWInit(void)
+{
+ int32 i, result= 0;
+
+ printk ("%s()\n",__FUNCTION__);
+ /*
+ ** Step 1: (required)
+ ** Create ProSLIC Control Interface Object
+ */
+ ProSLIC_createControlInterface(&ProHWIntf);
+
+ /*
+ ** Step 2: (required)
+ ** Create ProSLIC Device Objects
+ */
+ for(i=0;i<NUMBER_OF_PROSLIC;i++)
+ {
+ ProSLIC_createDevice(&(ProSLICDevices[i]));
+ }
+
+ /*
+ ** Step 3: (required)
+ ** Create and initialize ProSLIC channel objects
+ ** Also initialize array pointers to user¡¦s proslic channel object
+ ** members to simplify initialization process.
+ */
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ ProSLIC_createChannel(&(ports[i].ProObj));
+ ProSLIC_SWInitChan(ports[i].ProObj,i,PROSLIC_DEVICE_TYPE,
+ ProSLICDevices[i/CHAN_PER_DEVICE],ProHWIntf);
+ arrayOfProslicChans[i] = ports[i].ProObj;
+ ProSLIC_setSWDebugMode(ports[i].ProObj,TRUE); /* optional */
+ }
+
+ /*
+ ** Step 4: (required)
+ ** Establish linkage between host objects/functions and
+ ** ProSLIC API
+ */
+ ProSLIC_setControlInterfaceCtrlObj (ProHWIntf, &spiGciObj);
+ ProSLIC_setControlInterfaceReset (ProHWIntf, ctrl_ResetWrapper);
+ ProSLIC_setControlInterfaceWriteRegister (ProHWIntf, ctrl_WriteRegisterWrapper);
+ ProSLIC_setControlInterfaceReadRegister (ProHWIntf, ctrl_ReadRegisterWrapper);
+ ProSLIC_setControlInterfaceWriteRAM (ProHWIntf, ctrl_WriteRAMWrapper);
+ ProSLIC_setControlInterfaceReadRAM (ProHWIntf, ctrl_ReadRAMWrapper);
+ ProSLIC_setControlInterfaceTimerObj (ProHWIntf, &timerObj);
+ ProSLIC_setControlInterfaceDelay (ProHWIntf, time_DelayWrapper);
+ ProSLIC_setControlInterfaceTimeElapsed (ProHWIntf, time_TimeElapsedWrapper);
+ ProSLIC_setControlInterfaceGetTime (ProHWIntf, time_GetTimeWrapper);
+ ProSLIC_setControlInterfaceSemaphore (ProHWIntf, NULL);
+
+ /*
+ ** Step 5: (system dependent)
+ ** Assert hardware Reset ¡V ensure VDD, PCLK, and FSYNC are present and stable
+ ** before releasing reset
+ */
+ ProSLIC_Reset(ports[0].ProObj);
+
+ /*
+ ** Step 6: (required)
+ ** Initialize device (loading of general parameters, calibrations,
+ ** dc-dc powerup, etc.)
+ */
+ ProSLIC_Init(arrayOfProslicChans,NUMBER_OF_CHAN);
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ if(arrayOfProslicChans[i]->error!=0)
+ {
+ printk("ProSLIC_Init[%d] ERR=%d\n",i,arrayOfProslicChans[i]->error);
+ return 0;
+ }
+ }
+
+
+ /*
+ ** Step 7: (design dependent)
+ ** Execute longitudinal balance calibration
+ ** or reload coefficients from factory LB cal
+ **
+ ** Note: all batteries should be up and stable prior to
+ ** executing the lb cal
+ */
+ ProSLIC_LBCal(arrayOfProslicChans,NUMBER_OF_CHAN);
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ ProSLIC_GetLBCalResultPacked(arrayOfProslicChans[i], &result);
+ printk("LBCal=0x%08X\n",result);
+ }
+
+ /*
+ ** Step 8: (design dependent)
+ ** Load custom configuration presets(generated using
+ ** ProSLIC API Config Tool)
+ */
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ ProSLIC_PCMTimeSlotSetup(ports[i].ProObj,0,0);
+ ProSLIC_DCFeedSetup(ports[i].ProObj,DCFEED_48V_20MA);
+ ProSLIC_RingSetup(ports[i].ProObj,RING_F20_45VRMS_0VDC_LPR);
+ ProSLIC_PCMSetup(ports[i].ProObj,PCM_16LIN_WB); /* PCM_DEFAULT_CONFIG */
+ ProSLIC_ZsynthSetup(ports[i].ProObj,ZSYN_600_0_0_30_0);
+ ProSLIC_ToneGenSetup(ports[i].ProObj,TONEGEN_FCC_DIAL);
+ }
+
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ ProSLIC_PCMStart(ports[i].ProObj);
+ }
+ for(i=0;i<NUMBER_OF_CHAN;i++)
+ {
+ ProSLIC_SetLinefeedStatus(ports[i].ProObj,LF_FWD_ACTIVE);
+ }
+
+ /*
+ ** Step 9: (required)
+ ** SLIC settings - set TX_START and RX_START to 1 and
+ ** disable Free-Run mode.
+ */
+ ProSLIC_WriteReg(ports[0].ProObj, 12, 1);
+ ProSLIC_WriteReg(ports[0].ProObj, 14, 1);
+ ProSLIC_WriteReg(ports[0].ProObj, 47, 1);
+
+ return 1;
+}
+
+static int si3218x_component_probe(struct snd_soc_component *component)
+{
+ dev_info(component->dev, "%s\n", __func__);
+ return 0;
+}
+
+static void si3218x_component_remove(struct snd_soc_component *component)
+{
+ struct si3218x_chip *chip = snd_soc_component_get_drvdata(component);
+
+ dev_info(component->dev, "%s\n", __func__);
+
+ chip->component = NULL;
+}
+
+static const struct snd_soc_dapm_widget si3218x_component_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINP"),
+ SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route si3218x_component_dapm_routes[] = {
+ { "VOUTP", NULL, "aif_playback"},
+ { "aif_capture", NULL, "VINP"},
+};
+
+static const char * const slic_control_str[] = {
+ "off", "on"
+};
+
+static const struct soc_enum si3218x_slic_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slic_control_str),
+ slic_control_str),
+};
+
+static int si3218x_init_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = slic_init;
+
+ return 0;
+}
+
+static int si3218x_init_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (ucontrol->value.integer.value[0]) {
+ ProSLIC_HWInit();
+ slic_init = 1;
+ }
+
+ return 0;
+}
+
+static int si3218x_ring_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!slic_init)
+ return 0;
+
+ ucontrol->value.integer.value[0] =
+ (ProSLIC_ReadReg(ports[0].ProObj,30) == 4) ? 1 : 0;
+ return 0;
+}
+
+static int si3218x_ring_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!slic_init)
+ return 0;
+
+ if (ucontrol->value.integer.value[0])
+ ProSLIC_WriteReg(ports[0].ProObj, 30, 4);
+ else
+ ProSLIC_WriteReg(ports[0].ProObj, 30, 1);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new si3218x_component_snd_controls[] = {
+ SOC_ENUM_EXT("proslic_init", si3218x_slic_enum[0],
+ si3218x_init_get, si3218x_init_set),
+ SOC_ENUM_EXT("proslic_ring", si3218x_slic_enum[0],
+ si3218x_ring_get, si3218x_ring_set),
+};
+
+static const struct snd_soc_component_driver si3218x_component_driver = {
+ .probe = si3218x_component_probe,
+ .remove = si3218x_component_remove,
+
+ .controls = si3218x_component_snd_controls,
+ .num_controls = ARRAY_SIZE(si3218x_component_snd_controls),
+ .dapm_widgets = si3218x_component_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si3218x_component_dapm_widgets),
+ .dapm_routes = si3218x_component_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si3218x_component_dapm_routes),
+
+ .idle_bias_on = false,
+};
+
+static int si3218x_component_aif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+ int word_len = params_physical_width(hw_params);
+ int aud_bit = params_width(hw_params);
+
+ dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params));
+ dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params));
+ dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit);
+ if (word_len > 32 || word_len < 16) {
+ dev_err(dai->dev, "not supported word length\n");
+ return -ENOTSUPP;
+ }
+
+ dev_dbg(dai->dev, "%s: --\n", __func__);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops si3218x_component_aif_ops = {
+ .hw_params = si3218x_component_aif_hw_params,
+};
+
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver si3218x_codec_dai = {
+ .name = "proslic_spi-aif",
+ .playback = {
+ .stream_name = "aif_playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ .capture = {
+ .stream_name = "aif_capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = STUB_RATES,
+ .formats = STUB_FORMATS,
+ },
+ /* dai properties */
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+ /* dai operations */
+ .ops = &si3218x_component_aif_ops,
+};
+
+int si3218x_spi_probe(struct spi_device *spi, struct spi_driver *spi_drv)
+{
+ int ret;
+
+ printk(KERN_INFO "PROSLIC si3218x_spi_probe\n");
+
+ ret = proslic_spi_probe(spi,spi_drv);
+
+ return snd_soc_register_component(&spi->dev, &si3218x_component_driver,
+ &si3218x_codec_dai, 1);
+}
+EXPORT_SYMBOL(si3218x_spi_probe);
+
+int si3218x_spi_remove(struct spi_device *spi)
+{
+ proslic_spi_remove(spi);
+ snd_soc_unregister_component(&spi->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(si3218x_spi_remove);