| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 Amlogic, Inc. All rights reserved. |
| * |
| */ |
| |
| #define DEBUG |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/platform_device.h> |
| #include <linux/clk.h> |
| #include <linux/clk-provider.h> |
| #include <linux/amlogic/iomap.h> |
| #include <linux/timer.h> |
| |
| #include <sound/soc.h> |
| #include <sound/tlv.h> |
| |
| #include "regs.h" |
| #include "iomap.h" |
| #include "resample.h" |
| #include "resample_hw.h" |
| #include "frhdmirx_hw.h" |
| #include "loopback.h" |
| |
| #define DRV_NAME "audioresample" |
| |
| #define CLK_RATIO 256 |
| |
| struct resample_chipinfo { |
| int num; /* support resample a/b */ |
| enum resample_idx id; |
| |
| bool dividor_fn; |
| int resample_version; |
| |
| bool chnum_sync; |
| bool watchdog; |
| }; |
| |
| struct audioresample { |
| struct device *dev; |
| |
| /* mpll0~3, hifi pll, div3~4, gp0 */ |
| struct clk *pll; |
| /* mst_mclk_a~f, slv_sclk_a~j */ |
| struct clk *sclk; |
| /* resample clk */ |
| struct clk *clk; |
| |
| struct resample_chipinfo *chipinfo; |
| |
| enum resample_idx id; |
| |
| /* which module should be resampled */ |
| enum toddr_src resample_module; |
| /* resample to the rate */ |
| int out_rate; |
| |
| /* sync with auge_resample_texts */ |
| enum samplerate_index asrc_in_sr_idx; |
| |
| int capture_sample_rate; |
| |
| bool enable; |
| |
| struct timer_list timer; |
| bool timer_running; |
| }; |
| |
| struct audioresample *s_resample_a; |
| |
| struct audioresample *s_resample_b; |
| |
| struct audioresample *get_audioresample(enum resample_idx id) |
| { |
| struct audioresample *p_resample; |
| |
| p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b); |
| |
| if (!p_resample) |
| return NULL; |
| |
| return p_resample; |
| } |
| |
| int get_resample_module_num(void) |
| { |
| struct audioresample *p_resample = get_audioresample(RESAMPLE_A); |
| |
| if (p_resample && p_resample->chipinfo) |
| return p_resample->chipinfo->num; |
| |
| return 1; |
| } |
| |
| int get_resample_version_id(enum resample_idx id) |
| { |
| struct audioresample *p_resample; |
| |
| p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b); |
| |
| if (!p_resample || !p_resample->chipinfo) { |
| pr_debug("Not init audio resample\n"); |
| return -1; |
| } |
| |
| return p_resample->chipinfo->resample_version; |
| } |
| |
| bool get_resample_enable(enum resample_idx id) |
| { |
| struct audioresample *p_resample; |
| |
| p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b); |
| |
| if (!p_resample) { |
| pr_debug("Not init audio resample\n"); |
| return 0; |
| } |
| |
| return p_resample->enable; |
| } |
| |
| bool get_resample_enable_chnum_sync(enum resample_idx id) |
| { |
| struct audioresample *p_resample; |
| |
| p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b); |
| |
| if (!p_resample || !p_resample->chipinfo) { |
| pr_debug("Not init audio resample\n"); |
| return false; |
| } |
| |
| return p_resample->chipinfo->chnum_sync; |
| } |
| |
| int get_resample_source(enum resample_idx id) |
| { |
| struct audioresample *p_resample; |
| |
| p_resample = ((id == RESAMPLE_A) ? s_resample_a : s_resample_b); |
| |
| if (!p_resample) { |
| pr_debug("%s Not init audio resample\n", __func__); |
| return TODDR_INVAL; |
| } |
| |
| return p_resample->resample_module; |
| } |
| |
| int set_resample_source(enum resample_idx id, enum toddr_src src) |
| { |
| struct audioresample *p_resample = get_audioresample(id); |
| |
| (void)p_resample; |
| (void)src; |
| //p_resample->resample_module = src; |
| return 0; |
| } |
| |
| int get_resample_version(void) |
| { |
| if (!s_resample_a) |
| return 0; |
| return s_resample_a->chipinfo->resample_version; |
| } |
| |
| static int resample_clk_set(struct audioresample *p_resample, int output_sr) |
| { |
| int ret = 0; |
| char *clk_name; |
| |
| clk_name = (char *)__clk_get_name(p_resample->pll); |
| if (!strcmp(clk_name, "hifipll") || !strcmp(clk_name, "t5_hifi_pll")) { |
| pr_info("%s:set hifi pll\n", __func__); |
| clk_set_rate(p_resample->pll, 1806336 * 1000); |
| } else { |
| /* defaule tdm out mclk to resample clk */ |
| /* for same with earctx, so need *5 */ |
| clk_set_rate(p_resample->pll, output_sr * CLK_RATIO * 2 * 20); |
| } |
| |
| clk_set_rate(p_resample->sclk, output_sr * CLK_RATIO); |
| clk_set_rate(p_resample->clk, output_sr * CLK_RATIO); |
| |
| pr_info("%s, resample_pll:%lu, sclk:%lu, clk:%lu\n", |
| __func__, |
| clk_get_rate(p_resample->pll), |
| clk_get_rate(p_resample->sclk), |
| clk_get_rate(p_resample->clk)); |
| |
| return ret; |
| } |
| |
| static const char *const auge_resample_texts[] = { |
| "Disable", |
| "Enable:32K", |
| "Enable:44K", |
| "Enable:48K", |
| "Enable:88K", |
| "Enable:96K", |
| "Enable:176K", |
| "Enable:192K", |
| "Enable:16K", |
| }; |
| |
| static int resample_idx2rate(enum samplerate_index index) |
| { |
| int rate = 0; |
| |
| if (index == RATE_OFF) |
| rate = 0; |
| else if (index == RATE_32K) |
| rate = 32000; |
| else if (index == RATE_44K) |
| rate = 44100; |
| else if (index == RATE_48K) |
| rate = 48000; |
| else if (index == RATE_88K) |
| rate = 88200; |
| else if (index == RATE_96K) |
| rate = 96000; |
| else if (index == RATE_176K) |
| rate = 176400; |
| else if (index == RATE_192K) |
| rate = 192000; |
| else if (index == RATE_16K) |
| rate = 16000; |
| |
| return rate; |
| } |
| |
| static const struct soc_enum auge_resample_enum = |
| SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(auge_resample_texts), |
| auge_resample_texts); |
| |
| static int resample_get_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct audioresample *p_resample = snd_kcontrol_chip(kcontrol); |
| int resample_enable = 0; |
| |
| if (!p_resample || !p_resample->chipinfo) { |
| pr_debug("audio resample is not init\n"); |
| return 0; |
| } |
| |
| if (p_resample->chipinfo->resample_version >= SM1_RESAMPLE) |
| resample_enable = new_resample_get_status(p_resample->id); |
| else if (p_resample->chipinfo->resample_version == AXG_RESAMPLE) |
| resample_enable = resample_get_status(p_resample->id); |
| |
| if (resample_enable) |
| ucontrol->value.enumerated.item[0] = p_resample->asrc_in_sr_idx; |
| else |
| ucontrol->value.enumerated.item[0] = RATE_OFF; |
| |
| return 0; |
| } |
| |
| static void new_resample_timer_callback(struct timer_list *t) |
| { |
| struct audioresample *p_resample = from_timer(p_resample, t, timer); |
| int ADJ_diff = (int)new_resample_read(p_resample->id, |
| AUDIO_RSAMP_RO_ADJ_DIFF_BAK); |
| |
| if (!p_resample->timer_running) |
| return; |
| |
| if (ADJ_diff > 100 || ADJ_diff < -100) { |
| pr_debug("reset resample ADJ: [%d]\n", ADJ_diff); |
| new_resample_update_bits(p_resample->id, |
| AUDIO_RSAMP_ADJ_CTRL0, 0x1, 0); |
| new_resample_update_bits(p_resample->id, |
| AUDIO_RSAMP_CTRL0, 0x1 << 2, 0x1 << 2); |
| new_resample_update_bits(p_resample->id, |
| AUDIO_RSAMP_CTRL0, 0x1 << 2, 0x0 << 2); |
| new_resample_update_bits(p_resample->id, |
| AUDIO_RSAMP_ADJ_CTRL0, 0x1, 1); |
| } |
| |
| mod_timer(&p_resample->timer, jiffies + msecs_to_jiffies(500)); |
| } |
| |
| /* force set to new rate index whatever the resampler holds */ |
| int resample_set(enum resample_idx id, enum samplerate_index index) |
| { |
| int resample_rate = 0; |
| struct audioresample *p_resample = get_audioresample(id); |
| |
| if (!p_resample || !p_resample->chipinfo) |
| return 0; |
| |
| if (index < RATE_OFF || index >= RATE_MAX) { |
| pr_err("%s(), invalid index %d\n", __func__, index); |
| return 0; |
| } |
| |
| |
| pr_debug("%s resample_%c to %s, last %s\n", |
| __func__, |
| (id == RESAMPLE_A) ? 'a' : 'b', |
| auge_resample_texts[index], |
| auge_resample_texts[p_resample->asrc_in_sr_idx]); |
| |
| resample_rate = resample_idx2rate(index); |
| |
| p_resample->asrc_in_sr_idx = index; |
| p_resample->enable = index > 0 ? 1 : 0; |
| p_resample->out_rate = resample_rate; |
| |
| if (index == RATE_OFF) { |
| if (p_resample->chipinfo->resample_version >= SM1_RESAMPLE) { |
| /* delete the timer when resample is disable */ |
| if (p_resample->timer_running && |
| p_resample->chipinfo && |
| !p_resample->chipinfo->watchdog) { |
| p_resample->timer_running = false; |
| del_timer(&p_resample->timer); |
| } |
| } else if (p_resample->chipinfo->resample_version == AXG_RESAMPLE) { |
| frhdmirx_afifo_reset(); |
| } |
| } else { |
| if (p_resample->chipinfo->resample_version >= SM1_RESAMPLE) { |
| new_resample_set_ratio(id, resample_rate, |
| DEFAULT_SPK_SAMPLERATE); |
| |
| /* start a timer to check resample RO status */ |
| /* TM2 revb, these is hw watchdog to reset itself */ |
| if (!p_resample->timer_running && |
| p_resample->chipinfo && |
| !p_resample->chipinfo->watchdog) { |
| timer_setup(&p_resample->timer, |
| new_resample_timer_callback, |
| 0); |
| mod_timer(&p_resample->timer, |
| jiffies + msecs_to_jiffies(500)); |
| p_resample->timer_running = true; |
| } |
| } else if (p_resample->chipinfo->resample_version == AXG_RESAMPLE) { |
| resample_init(p_resample->id, resample_rate); |
| resample_set_hw_param(p_resample->id, index); |
| } |
| } |
| |
| aml_set_resample(p_resample->id, p_resample->enable, |
| p_resample->resample_module); |
| |
| return 0; |
| } |
| |
| static int resample_set_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct audioresample *p_resample = snd_kcontrol_chip(kcontrol); |
| int index = ucontrol->value.enumerated.item[0]; |
| |
| if (!p_resample) { |
| pr_debug("audio resample is not init\n"); |
| return 0; |
| } |
| |
| if (index == p_resample->asrc_in_sr_idx) |
| return 0; |
| |
| resample_set(p_resample->id, index); |
| |
| return 0; |
| } |
| |
| static int mixer_audiobus_read(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| unsigned int max = mc->max; |
| unsigned int invert = mc->invert; |
| unsigned int value = |
| (((unsigned int)audiobus_read(reg)) >> shift) & max; |
| |
| if (invert) |
| value = (~value) & max; |
| ucontrol->value.integer.value[0] = value; |
| |
| return 0; |
| } |
| |
| static int mixer_audiobus_write(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct soc_mixer_control *mc = |
| (struct soc_mixer_control *)kcontrol->private_value; |
| unsigned int reg = mc->reg; |
| unsigned int shift = mc->shift; |
| unsigned int max = mc->max; |
| unsigned int invert = mc->invert; |
| unsigned int value = ucontrol->value.integer.value[0]; |
| unsigned int reg_value = (unsigned int)audiobus_read(reg); |
| |
| if (invert) |
| value = (~value) & max; |
| max = ~(max << shift); |
| reg_value &= max; |
| reg_value |= (value << shift); |
| audiobus_write(reg, reg_value); |
| |
| return 0; |
| } |
| |
| /* resample module |
| * keep sync with enum toddr_src in ddr_mngr.h |
| */ |
| static const char *const auge_resample_module_texts[] = { |
| "TDMIN_A", |
| "TDMIN_B", |
| "TDMIN_C", |
| "SPDIFIN", |
| "PDMIN", |
| "FRATV", /* NONE for axg, g12a, g12b */ |
| "TDMIN_LB", |
| "LOOPBACK_A", |
| "FRHDMIRX", /* from tl1 chipset*/ |
| "LOOPBACK_B", |
| "SPDIFIN_LB", |
| "EARCRX_DMAC", |
| "FRHDMIRX_PAO", |
| "RESERVED_0", |
| "RESERVED_1", |
| "VAD", |
| }; |
| |
| static const struct soc_enum auge_resample_module_enum = |
| SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(auge_resample_module_texts), |
| auge_resample_module_texts); |
| |
| static int resample_module_get_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct audioresample *p_resample = snd_kcontrol_chip(kcontrol); |
| |
| if (!p_resample) { |
| pr_debug("audio resample is not init\n"); |
| return 0; |
| } |
| |
| ucontrol->value.enumerated.item[0] = p_resample->resample_module; |
| |
| return 0; |
| } |
| |
| static int resample_module_set_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct audioresample *p_resample = snd_kcontrol_chip(kcontrol); |
| |
| if (!p_resample) { |
| pr_debug("audio resample is not init\n"); |
| return 0; |
| } |
| |
| p_resample->resample_module = ucontrol->value.enumerated.item[0]; |
| |
| /* update info to ddr */ |
| aml_set_resample(p_resample->id, |
| p_resample->enable, |
| p_resample->resample_module); |
| |
| return 0; |
| } |
| |
| static const struct snd_kcontrol_new asrc_a_controls[] = { |
| SOC_ENUM_EXT("Hardware resample enable", |
| auge_resample_enum, |
| resample_get_enum, |
| resample_set_enum), |
| SOC_SINGLE_EXT_TLV("Hw resample pause enable", |
| EE_AUDIO_RESAMPLEA_CTRL2, 24, 0x1, 0, |
| mixer_audiobus_read, mixer_audiobus_write, |
| NULL), |
| SOC_SINGLE_EXT_TLV("Hw resample pause thd", |
| EE_AUDIO_RESAMPLEA_CTRL2, 11, 0x1fff, 0, |
| mixer_audiobus_read, mixer_audiobus_write, |
| NULL), |
| SOC_ENUM_EXT("Hw resample module", |
| auge_resample_module_enum, |
| resample_module_get_enum, |
| resample_module_set_enum), |
| }; |
| |
| static const struct snd_kcontrol_new asrc_b_controls[] = { |
| SOC_ENUM_EXT("Hardware resample b enable", |
| auge_resample_enum, |
| resample_get_enum, |
| resample_set_enum), |
| SOC_SINGLE_EXT_TLV("Hw resample b pause enable", |
| EE_AUDIO_RESAMPLEB_CTRL2, 24, 0x1, 0, |
| mixer_audiobus_read, mixer_audiobus_write, |
| NULL), |
| SOC_SINGLE_EXT_TLV("Hw resample b pause thd", |
| EE_AUDIO_RESAMPLEB_CTRL2, 11, 0x1fff, 0, |
| mixer_audiobus_read, mixer_audiobus_write, |
| NULL), |
| SOC_ENUM_EXT("Hw resample b module", |
| auge_resample_module_enum, |
| resample_module_get_enum, |
| resample_module_set_enum), |
| }; |
| |
| static const struct snd_kcontrol_new rsamp_a_controls[] = { |
| SOC_ENUM_EXT("Hardware resample enable", |
| auge_resample_enum, |
| resample_get_enum, |
| resample_set_enum), |
| SOC_ENUM_EXT("Hw resample module", |
| auge_resample_module_enum, |
| resample_module_get_enum, |
| resample_module_set_enum), |
| }; |
| |
| int card_add_resample_kcontrols(struct snd_soc_card *card) |
| { |
| unsigned int idx; |
| int err = 0; |
| |
| if (s_resample_a && s_resample_a->chipinfo) { |
| if (s_resample_a->chipinfo->resample_version >= SM1_RESAMPLE) { |
| for (idx = 0; idx < ARRAY_SIZE(rsamp_a_controls); |
| idx++) { |
| err = snd_ctl_add(card->snd_card, |
| snd_ctl_new1(&rsamp_a_controls[idx], s_resample_a)); |
| } |
| } else if (s_resample_a->chipinfo->resample_version == AXG_RESAMPLE) { |
| for (idx = 0; idx < ARRAY_SIZE(asrc_a_controls); |
| idx++) { |
| err = snd_ctl_add(card->snd_card, |
| snd_ctl_new1(&asrc_a_controls[idx], s_resample_a)); |
| } |
| } |
| if (err < 0) |
| return err; |
| } |
| |
| if (s_resample_b && s_resample_b->chipinfo) { |
| if (s_resample_b->chipinfo->resample_version == 0) { |
| for (idx = 0; idx < ARRAY_SIZE(asrc_b_controls); |
| idx++) { |
| err = snd_ctl_add(card->snd_card, |
| snd_ctl_new1(&asrc_b_controls[idx], s_resample_b)); |
| } |
| if (err < 0) |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int new_resample_init(struct audioresample *p_resample) |
| { |
| if (!p_resample) |
| return -ENOMEM; |
| |
| pr_info("%s: Start init new resample %s parameters!\n", |
| __func__, (p_resample->id == RESAMPLE_A) ? "A" : "B"); |
| |
| p_resample->enable = 1; |
| new_resample_init_param(p_resample->id); |
| resample_clk_set(p_resample, DEFAULT_SPK_SAMPLERATE); |
| |
| if (p_resample && p_resample->chipinfo && |
| p_resample->chipinfo->watchdog) { |
| new_resample_enable_watchdog(p_resample->id, true); |
| } |
| |
| if (p_resample->id == RESAMPLE_A) { |
| /* default resample A for tv input source */ |
| new_resample_set_ratio(p_resample->id, |
| DEFAULT_SPK_SAMPLERATE, |
| DEFAULT_SPK_SAMPLERATE); |
| } else if (p_resample->id == RESAMPLE_B) { |
| unsigned int channel = loopback_get_lb_channel(0); |
| |
| /* default resample B for loopback downsample */ |
| new_resample_set_ratio(p_resample->id, |
| DEFAULT_SPK_SAMPLERATE, |
| p_resample->capture_sample_rate); |
| new_resampleB_set_format(p_resample->id, |
| p_resample->capture_sample_rate, channel); |
| } |
| |
| return 0; |
| } |
| |
| static struct resample_chipinfo axg_resample_chipinfo = { |
| .num = 1, |
| .id = RESAMPLE_A, |
| .resample_version = AXG_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo g12a_resample_chipinfo = { |
| .num = 1, |
| .id = RESAMPLE_A, |
| .dividor_fn = true, |
| .resample_version = AXG_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo tl1_resample_a_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_A, |
| .dividor_fn = true, |
| .resample_version = AXG_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo tl1_resample_b_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_B, |
| .dividor_fn = true, |
| .resample_version = AXG_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo sm1_resample_a_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_A, |
| .dividor_fn = true, |
| .resample_version = SM1_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo sm1_resample_b_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_B, |
| .dividor_fn = true, |
| .resample_version = SM1_RESAMPLE, |
| }; |
| |
| static struct resample_chipinfo tm2_revb_resample_a_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_A, |
| .dividor_fn = true, |
| .resample_version = SM1_RESAMPLE, |
| .chnum_sync = true, |
| .watchdog = true, |
| }; |
| |
| static struct resample_chipinfo tm2_revb_resample_b_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_B, |
| .dividor_fn = true, |
| .resample_version = SM1_RESAMPLE, |
| .chnum_sync = true, |
| .watchdog = true, |
| }; |
| |
| static struct resample_chipinfo t5_resample_a_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_A, |
| .dividor_fn = true, |
| .resample_version = T5_RESAMPLE, |
| .chnum_sync = true, |
| .watchdog = true, |
| //.src_conf = &resample_srcs_v2[0], |
| }; |
| |
| static struct resample_chipinfo t5_resample_b_chipinfo = { |
| .num = 2, |
| .id = RESAMPLE_B, |
| .dividor_fn = true, |
| .resample_version = T5_RESAMPLE, |
| .chnum_sync = true, |
| .watchdog = true, |
| //.src_conf = &resample_srcs_v2[0], |
| }; |
| |
| static const struct of_device_id resample_device_id[] = { |
| { |
| .compatible = "amlogic, axg-resample", |
| .data = &axg_resample_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, g12a-resample", |
| .data = &g12a_resample_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, tl1-resample-a", |
| .data = &tl1_resample_a_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, tl1-resample-b", |
| .data = &tl1_resample_b_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, sm1-resample-a", |
| .data = &sm1_resample_a_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, sm1-resample-b", |
| .data = &sm1_resample_b_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, tm2-revb-resample-a", |
| .data = &tm2_revb_resample_a_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, tm2-revb-resample-b", |
| .data = &tm2_revb_resample_b_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, t5-resample-a", |
| .data = &t5_resample_a_chipinfo, |
| }, |
| { |
| .compatible = "amlogic, t5-resample-b", |
| .data = &t5_resample_b_chipinfo, |
| }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, resample_device_id); |
| |
| static int resample_platform_probe(struct platform_device *pdev) |
| { |
| struct audioresample *p_resample; |
| struct device *dev = &pdev->dev; |
| struct resample_chipinfo *p_chipinfo; |
| unsigned int resample_module; |
| int ret; |
| |
| pr_info("%s\n", __func__); |
| |
| p_resample = devm_kzalloc(&pdev->dev, |
| sizeof(struct audioresample), |
| GFP_KERNEL); |
| if (!p_resample) { |
| /*dev_err(&pdev->dev, "Can't allocate pcm_p\n");*/ |
| return -ENOMEM; |
| } |
| |
| /* match data */ |
| p_chipinfo = (struct resample_chipinfo *) |
| of_device_get_match_data(dev); |
| if (!p_chipinfo) { |
| dev_warn_once(dev, "check whether to update resample chipinfo\n"); |
| return -EINVAL; |
| } |
| |
| p_resample->id = p_chipinfo->id; |
| p_resample->chipinfo = p_chipinfo; |
| |
| if (p_chipinfo->id == 0) { |
| ret = of_property_read_u32(pdev->dev.of_node, "resample_module", |
| &resample_module); |
| if (ret < 0) { |
| dev_err(&pdev->dev, "Can't retrieve resample_module\n"); |
| return -EINVAL; |
| } |
| } else { |
| resample_module = LOOPBACK_A; |
| ret = of_property_read_u32(pdev->dev.of_node, |
| "capture_sample_rate", |
| &p_resample->capture_sample_rate); |
| if (ret < 0 || |
| p_resample->capture_sample_rate != DEFAULT_SPK_SAMPLERATE) { |
| p_resample->capture_sample_rate = |
| DEFAULT_MIC_SAMPLERATE; |
| } |
| } |
| |
| /* config from dts */ |
| p_resample->resample_module = resample_module; |
| |
| p_resample->pll = devm_clk_get(&pdev->dev, "resample_pll"); |
| if (IS_ERR(p_resample->pll)) { |
| dev_err(&pdev->dev, |
| "Can't retrieve resample_pll clock\n"); |
| ret = PTR_ERR(p_resample->pll); |
| return ret; |
| } |
| p_resample->sclk = devm_clk_get(&pdev->dev, "resample_src"); |
| if (IS_ERR(p_resample->sclk)) { |
| dev_err(&pdev->dev, |
| "Can't retrieve resample_src clock\n"); |
| ret = PTR_ERR(p_resample->sclk); |
| return ret; |
| } |
| p_resample->clk = devm_clk_get(&pdev->dev, "resample_clk"); |
| if (IS_ERR(p_resample->clk)) { |
| dev_err(&pdev->dev, |
| "Can't retrieve resample_clk clock\n"); |
| ret = PTR_ERR(p_resample->clk); |
| return ret; |
| } |
| |
| ret = clk_set_parent(p_resample->sclk, p_resample->pll); |
| if (ret) { |
| dev_err(&pdev->dev, |
| "Can't set resample_src parent clock\n"); |
| ret = PTR_ERR(p_resample->sclk); |
| return ret; |
| } |
| ret = clk_set_parent(p_resample->clk, p_resample->sclk); |
| if (ret) { |
| dev_err(&pdev->dev, |
| "Can't set resample_clk parent clock\n"); |
| ret = PTR_ERR(p_resample->clk); |
| return ret; |
| } |
| |
| ret = clk_prepare_enable(p_resample->clk); |
| if (ret) { |
| pr_err("Can't enable resample_clk clock: %d\n", |
| ret); |
| return ret; |
| } |
| |
| p_resample->dev = dev; |
| |
| if (p_chipinfo && p_chipinfo->id == 1) |
| s_resample_b = p_resample; |
| else |
| s_resample_a = p_resample; |
| |
| if (p_chipinfo && p_chipinfo->resample_version >= SM1_RESAMPLE) |
| new_resample_init(p_resample); |
| else if (p_chipinfo && p_chipinfo->resample_version == AXG_RESAMPLE) |
| resample_clk_set(p_resample, DEFAULT_SPK_SAMPLERATE); |
| |
| aml_set_resample(p_resample->id, p_resample->enable, |
| p_resample->resample_module); |
| |
| pr_info("resample id = %d, new resample = %d, resample_module = %d\n", |
| p_chipinfo->id, p_chipinfo->resample_version, |
| p_resample->resample_module); |
| |
| return 0; |
| } |
| |
| static struct platform_driver resample_platform_driver = { |
| .driver = { |
| .name = DRV_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(resample_device_id), |
| }, |
| .probe = resample_platform_probe, |
| }; |
| |
| int __init resample_drv_init(void) |
| { |
| return platform_driver_register(&resample_platform_driver); |
| } |
| |
| void __exit resample_drv_exit(void) |
| { |
| platform_driver_unregister(&resample_platform_driver); |
| } |
| |
| #ifndef MODULE |
| module_init(resample_drv_init); |
| module_exit(resample_drv_exit); |
| #endif |