/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snddev_icodec.h" #define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */ #define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ #define SNDDEV_ICODEC_CLK_RATE(freq) \ (((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR)) #define SNDDEV_LOW_POWER_MODE 0 #define SNDDEV_HIGH_POWER_MODE 1 /* Voltage required for S4 in microVolts, 2.2V or 2200000microvolts */ #define SNDDEV_VREG_8058_S4_VOLTAGE (2200000) /* Load Current required for S4 in microAmps, 36mA - 56mA */ #define SNDDEV_VREG_LOW_POWER_LOAD (36000) #define SNDDEV_VREG_HIGH_POWER_LOAD (56000) bool msm_codec_i2s_slave_mode; /* Context for each internal codec sound device */ struct snddev_icodec_state { struct snddev_icodec_data *data; struct adie_codec_path *adie_path; u32 sample_rate; u32 enabled; }; /* Global state for the driver */ struct snddev_icodec_drv_state { struct mutex rx_lock; struct mutex lb_lock; struct mutex tx_lock; u32 rx_active; /* ensure one rx device at a time */ u32 tx_active; /* ensure one tx device at a time */ struct clk *rx_osrclk; struct clk *rx_bitclk; struct clk *tx_osrclk; struct clk *tx_bitclk; struct pm_qos_request rx_pm_qos_req; struct pm_qos_request tx_pm_qos_req; /* handle to pmic8058 regulator smps4 */ struct regulator *snddev_vreg; }; static struct snddev_icodec_drv_state snddev_icodec_drv; struct regulator *vreg_init(void) { int rc; struct regulator *vreg_ptr; vreg_ptr = regulator_get(NULL, "8058_s4"); if (IS_ERR(vreg_ptr)) { pr_err("%s: regulator_get 8058_s4 failed\n", __func__); return NULL; } rc = regulator_set_voltage(vreg_ptr, SNDDEV_VREG_8058_S4_VOLTAGE, SNDDEV_VREG_8058_S4_VOLTAGE); if (rc == 0) return vreg_ptr; else return NULL; } static void vreg_deinit(struct regulator *vreg) { regulator_put(vreg); } static void vreg_mode_vote(struct regulator *vreg, int enable, int mode) { int rc; if (enable) { rc = regulator_enable(vreg); if (rc != 0) pr_err("%s:Enabling regulator failed\n", __func__); else { if (mode) regulator_set_optimum_mode(vreg, SNDDEV_VREG_HIGH_POWER_LOAD); else regulator_set_optimum_mode(vreg, SNDDEV_VREG_LOW_POWER_LOAD); } } else { rc = regulator_disable(vreg); if (rc != 0) pr_err("%s:Disabling regulator failed\n", __func__); } } struct msm_cdcclk_ctl_state { unsigned int rx_mclk; unsigned int rx_mclk_requested; unsigned int tx_mclk; unsigned int tx_mclk_requested; }; static struct msm_cdcclk_ctl_state the_msm_cdcclk_ctl_state; static int msm_snddev_rx_mclk_request(void) { int rc = 0; rc = gpio_request(the_msm_cdcclk_ctl_state.rx_mclk, "MSM_SNDDEV_RX_MCLK"); if (rc < 0) { pr_err("%s: GPIO request for MSM SNDDEV RX failed\n", __func__); return rc; } the_msm_cdcclk_ctl_state.rx_mclk_requested = 1; return rc; } static int msm_snddev_tx_mclk_request(void) { int rc = 0; rc = gpio_request(the_msm_cdcclk_ctl_state.tx_mclk, "MSM_SNDDEV_TX_MCLK"); if (rc < 0) { pr_err("%s: GPIO request for MSM SNDDEV TX failed\n", __func__); return rc; } the_msm_cdcclk_ctl_state.tx_mclk_requested = 1; return rc; } static void msm_snddev_rx_mclk_free(void) { if (the_msm_cdcclk_ctl_state.rx_mclk_requested) { gpio_free(the_msm_cdcclk_ctl_state.rx_mclk); the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; } } static void msm_snddev_tx_mclk_free(void) { if (the_msm_cdcclk_ctl_state.tx_mclk_requested) { gpio_free(the_msm_cdcclk_ctl_state.tx_mclk); the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; } } static int get_msm_cdcclk_ctl_gpios(struct platform_device *pdev) { int rc = 0; struct resource *res; /* Claim all of the GPIOs. */ res = platform_get_resource_byname(pdev, IORESOURCE_IO, "msm_snddev_rx_mclk"); if (!res) { pr_err("%s: failed to get gpio MSM SNDDEV RX\n", __func__); return -ENODEV; } the_msm_cdcclk_ctl_state.rx_mclk = res->start; the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; res = platform_get_resource_byname(pdev, IORESOURCE_IO, "msm_snddev_tx_mclk"); if (!res) { pr_err("%s: failed to get gpio MSM SNDDEV TX\n", __func__); return -ENODEV; } the_msm_cdcclk_ctl_state.tx_mclk = res->start; the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; return rc; } static int msm_cdcclk_ctl_probe(struct platform_device *pdev) { int rc = 0; rc = get_msm_cdcclk_ctl_gpios(pdev); if (rc < 0) { pr_err("%s: GPIO configuration failed\n", __func__); return -ENODEV; } return rc; } static struct platform_driver msm_cdcclk_ctl_driver = { .probe = msm_cdcclk_ctl_probe, .driver = { .name = "msm_cdcclk_ctl"} }; static int snddev_icodec_open_lb(struct snddev_icodec_state *icodec) { int trc; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; /* Voting for low power is ok here as all use cases are * supported in low power mode. */ if (drv->snddev_vreg) vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_LOW_POWER_MODE); if (icodec->data->voltage_on) icodec->data->voltage_on(); trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); if (IS_ERR_VALUE(trc)) pr_err("%s: adie codec open failed\n", __func__); else adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256); if (icodec->adie_path) adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_ANALOG_READY); if (icodec->data->pamp_on) icodec->data->pamp_on(); icodec->enabled = 1; return 0; } static int initialize_msm_icodec_gpios(struct platform_device *pdev) { int rc = 0; struct resource *res; int i = 0; int *reg_defaults = pdev->dev.platform_data; while ((res = platform_get_resource(pdev, IORESOURCE_IO, i))) { rc = gpio_request(res->start, res->name); if (rc) { pr_err("%s: icodec gpio %d request failed\n", __func__, res->start); goto err; } else { /* This platform data structure only works if all gpio * resources are to be used only in output mode. * If gpio resources are added which are to be used in * input mode, then the platform data structure will * have to be changed. */ gpio_direction_output(res->start, reg_defaults[i]); gpio_free(res->start); } i++; } err: return rc; } static int msm_icodec_gpio_probe(struct platform_device *pdev) { int rc = 0; rc = initialize_msm_icodec_gpios(pdev); if (rc < 0) { pr_err("%s: GPIO configuration failed\n", __func__); return -ENODEV; } return rc; } static struct platform_driver msm_icodec_gpio_driver = { .probe = msm_icodec_gpio_probe, .driver = { .name = "msm_icodec_gpio"} }; static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec) { int trc; int afe_channel_mode; union afe_port_config afe_config; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; pm_qos_update_request(&drv->rx_pm_qos_req, msm_cpuidle_get_deep_idle_latency()); if (drv->snddev_vreg) { if (!strcmp(icodec->data->name, "headset_stereo_rx")) vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_LOW_POWER_MODE); else vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_HIGH_POWER_MODE); } msm_snddev_rx_mclk_request(); drv->rx_osrclk = clk_get_sys(NULL, "i2s_spkr_osr_clk"); if (IS_ERR(drv->rx_osrclk)) pr_err("%s master clock Error\n", __func__); trc = clk_set_rate(drv->rx_osrclk, SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); if (IS_ERR_VALUE(trc)) { pr_err("ERROR setting m clock1\n"); goto error_invalid_freq; } clk_prepare_enable(drv->rx_osrclk); drv->rx_bitclk = clk_get_sys(NULL, "i2s_spkr_bit_clk"); if (IS_ERR(drv->rx_bitclk)) pr_err("%s clock Error\n", __func__); /* Master clock = Sample Rate * OSR rate bit clock * OSR Rate bit clock = bit/sample * channel master * clock / bit clock = divider value = 8 */ if (msm_codec_i2s_slave_mode) { pr_info("%s: configuring bit clock for slave mode\n", __func__); trc = clk_set_rate(drv->rx_bitclk, 0); } else trc = clk_set_rate(drv->rx_bitclk, 8); if (IS_ERR_VALUE(trc)) { pr_err("ERROR setting m clock1\n"); goto error_adie; } clk_prepare_enable(drv->rx_bitclk); if (icodec->data->voltage_on) icodec->data->voltage_on(); /* Configure ADIE */ trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); if (IS_ERR_VALUE(trc)) pr_err("%s: adie codec open failed\n", __func__); else adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256); /* OSR default to 256, can be changed for power optimization * If OSR is to be changed, need clock API for setting the divider */ switch (icodec->data->channel_mode) { case 2: afe_channel_mode = MSM_AFE_STEREO; break; case 1: default: afe_channel_mode = MSM_AFE_MONO; break; } afe_config.mi2s.channel = afe_channel_mode; afe_config.mi2s.bitwidth = 16; afe_config.mi2s.line = 1; afe_config.mi2s.format = MSM_AFE_I2S_FORMAT_LPCM; if (msm_codec_i2s_slave_mode) afe_config.mi2s.ws = 0; else afe_config.mi2s.ws = 1; trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); if (trc < 0) pr_err("%s: afe open failed, trc = %d\n", __func__, trc); /* Enable ADIE */ if (icodec->adie_path) { adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY); adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_ANALOG_READY); } if (msm_codec_i2s_slave_mode) adie_codec_set_master_mode(icodec->adie_path, 1); else adie_codec_set_master_mode(icodec->adie_path, 0); /* Enable power amplifier */ if (icodec->data->pamp_on) { if (icodec->data->pamp_on()) { pr_err("%s: Error turning on rx power\n", __func__); goto error_pamp; } } icodec->enabled = 1; pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return 0; error_pamp: error_adie: clk_disable_unprepare(drv->rx_osrclk); error_invalid_freq: pr_err("%s: encounter error\n", __func__); pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return -ENODEV; } static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec) { int trc; int afe_channel_mode; union afe_port_config afe_config; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;; pm_qos_update_request(&drv->tx_pm_qos_req, msm_cpuidle_get_deep_idle_latency()); if (drv->snddev_vreg) vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_HIGH_POWER_MODE); /* Reuse pamp_on for TX platform-specific setup */ if (icodec->data->pamp_on) { if (icodec->data->pamp_on()) { pr_err("%s: Error turning on tx power\n", __func__); goto error_pamp; } } msm_snddev_tx_mclk_request(); drv->tx_osrclk = clk_get_sys(NULL, "i2s_mic_osr_clk"); if (IS_ERR(drv->tx_osrclk)) pr_err("%s master clock Error\n", __func__); trc = clk_set_rate(drv->tx_osrclk, SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); if (IS_ERR_VALUE(trc)) { pr_err("ERROR setting m clock1\n"); goto error_invalid_freq; } clk_prepare_enable(drv->tx_osrclk); drv->tx_bitclk = clk_get_sys(NULL, "i2s_mic_bit_clk"); if (IS_ERR(drv->tx_bitclk)) pr_err("%s clock Error\n", __func__); /* Master clock = Sample Rate * OSR rate bit clock * OSR Rate bit clock = bit/sample * channel master * clock / bit clock = divider value = 8 */ if (msm_codec_i2s_slave_mode) { pr_info("%s: configuring bit clock for slave mode\n", __func__); trc = clk_set_rate(drv->tx_bitclk, 0); } else trc = clk_set_rate(drv->tx_bitclk, 8); clk_prepare_enable(drv->tx_bitclk); /* Enable ADIE */ trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); if (IS_ERR_VALUE(trc)) pr_err("%s: adie codec open failed\n", __func__); else adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256); switch (icodec->data->channel_mode) { case 2: afe_channel_mode = MSM_AFE_STEREO; break; case 1: default: afe_channel_mode = MSM_AFE_MONO; break; } afe_config.mi2s.channel = afe_channel_mode; afe_config.mi2s.bitwidth = 16; afe_config.mi2s.line = 1; afe_config.mi2s.format = MSM_AFE_I2S_FORMAT_LPCM; if (msm_codec_i2s_slave_mode) afe_config.mi2s.ws = 0; else afe_config.mi2s.ws = 1; trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); if (icodec->adie_path) { adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY); adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_ANALOG_READY); } if (msm_codec_i2s_slave_mode) adie_codec_set_master_mode(icodec->adie_path, 1); else adie_codec_set_master_mode(icodec->adie_path, 0); icodec->enabled = 1; pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return 0; error_invalid_freq: if (icodec->data->pamp_off) icodec->data->pamp_off(); pr_err("%s: encounter error\n", __func__); error_pamp: pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return -ENODEV; } static int snddev_icodec_close_lb(struct snddev_icodec_state *icodec) { struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; /* Disable power amplifier */ if (icodec->data->pamp_off) icodec->data->pamp_off(); if (drv->snddev_vreg) vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_LOW_POWER_MODE); if (icodec->adie_path) { adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF); adie_codec_close(icodec->adie_path); icodec->adie_path = NULL; } if (icodec->data->voltage_off) icodec->data->voltage_off(); return 0; } static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec) { struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; pm_qos_update_request(&drv->rx_pm_qos_req, msm_cpuidle_get_deep_idle_latency()); if (drv->snddev_vreg) vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); /* Disable power amplifier */ if (icodec->data->pamp_off) icodec->data->pamp_off(); /* Disable ADIE */ if (icodec->adie_path) { adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF); adie_codec_close(icodec->adie_path); icodec->adie_path = NULL; } afe_close(icodec->data->copp_id); if (icodec->data->voltage_off) icodec->data->voltage_off(); clk_disable_unprepare(drv->rx_bitclk); clk_disable_unprepare(drv->rx_osrclk); msm_snddev_rx_mclk_free(); icodec->enabled = 0; pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return 0; } static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec) { struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; pm_qos_update_request(&drv->tx_pm_qos_req, msm_cpuidle_get_deep_idle_latency()); if (drv->snddev_vreg) vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); /* Disable ADIE */ if (icodec->adie_path) { adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF); adie_codec_close(icodec->adie_path); icodec->adie_path = NULL; } afe_close(icodec->data->copp_id); clk_disable_unprepare(drv->tx_bitclk); clk_disable_unprepare(drv->tx_osrclk); msm_snddev_tx_mclk_free(); /* Reuse pamp_off for TX platform-specific setup */ if (icodec->data->pamp_off) icodec->data->pamp_off(); icodec->enabled = 0; pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); return 0; } static int snddev_icodec_set_device_volume_impl( struct msm_snddev_info *dev_info, u32 volume) { struct snddev_icodec_state *icodec; int rc = 0; icodec = dev_info->private_data; if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) { rc = adie_codec_set_device_digital_volume(icodec->adie_path, icodec->data->channel_mode, volume); if (rc < 0) { pr_err("%s: unable to set_device_digital_volume for" "%s volume in percentage = %u\n", __func__, dev_info->name, volume); return rc; } } else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) { rc = adie_codec_set_device_analog_volume(icodec->adie_path, icodec->data->channel_mode, volume); if (rc < 0) { pr_err("%s: unable to set_device_analog_volume for" "%s volume in percentage = %u\n", __func__, dev_info->name, volume); return rc; } } else { pr_err("%s: Invalid device volume control\n", __func__); return -EPERM; } return rc; } static int snddev_icodec_open(struct msm_snddev_info *dev_info) { int rc = 0; struct snddev_icodec_state *icodec; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; if (!dev_info) { rc = -EINVAL; goto error; } icodec = dev_info->private_data; if (icodec->data->capability & SNDDEV_CAP_RX) { mutex_lock(&drv->rx_lock); if (drv->rx_active) { mutex_unlock(&drv->rx_lock); pr_err("%s: rx_active is set, return EBUSY\n", __func__); rc = -EBUSY; goto error; } rc = snddev_icodec_open_rx(icodec); if (!IS_ERR_VALUE(rc)) { if ((icodec->data->dev_vol_type & ( SNDDEV_DEV_VOL_DIGITAL | SNDDEV_DEV_VOL_ANALOG))) rc = snddev_icodec_set_device_volume_impl( dev_info, dev_info->dev_volume); if (!IS_ERR_VALUE(rc)) drv->rx_active = 1; else pr_err("%s: set_device_volume_impl" " error(rx) = %d\n", __func__, rc); } mutex_unlock(&drv->rx_lock); } else if (icodec->data->capability & SNDDEV_CAP_LB) { mutex_lock(&drv->lb_lock); rc = snddev_icodec_open_lb(icodec); if (!IS_ERR_VALUE(rc)) { if ((icodec->data->dev_vol_type & ( SNDDEV_DEV_VOL_DIGITAL | SNDDEV_DEV_VOL_ANALOG))) rc = snddev_icodec_set_device_volume_impl( dev_info, dev_info->dev_volume); } mutex_unlock(&drv->lb_lock); } else { mutex_lock(&drv->tx_lock); if (drv->tx_active) { mutex_unlock(&drv->tx_lock); pr_err("%s: tx_active is set, return EBUSY\n", __func__); rc = -EBUSY; goto error; } rc = snddev_icodec_open_tx(icodec); if (!IS_ERR_VALUE(rc)) { if ((icodec->data->dev_vol_type & ( SNDDEV_DEV_VOL_DIGITAL | SNDDEV_DEV_VOL_ANALOG))) rc = snddev_icodec_set_device_volume_impl( dev_info, dev_info->dev_volume); if (!IS_ERR_VALUE(rc)) drv->tx_active = 1; else pr_err("%s: set_device_volume_impl" " error(tx) = %d\n", __func__, rc); } mutex_unlock(&drv->tx_lock); } error: return rc; } static int snddev_icodec_close(struct msm_snddev_info *dev_info) { int rc = 0; struct snddev_icodec_state *icodec; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; if (!dev_info) { rc = -EINVAL; goto error; } icodec = dev_info->private_data; if (icodec->data->capability & SNDDEV_CAP_RX) { mutex_lock(&drv->rx_lock); if (!drv->rx_active) { mutex_unlock(&drv->rx_lock); pr_err("%s: rx_active not set, return\n", __func__); rc = -EPERM; goto error; } rc = snddev_icodec_close_rx(icodec); if (!IS_ERR_VALUE(rc)) drv->rx_active = 0; else pr_err("%s: close rx failed, rc = %d\n", __func__, rc); mutex_unlock(&drv->rx_lock); } else if (icodec->data->capability & SNDDEV_CAP_LB) { mutex_lock(&drv->lb_lock); rc = snddev_icodec_close_lb(icodec); mutex_unlock(&drv->lb_lock); } else { mutex_lock(&drv->tx_lock); if (!drv->tx_active) { mutex_unlock(&drv->tx_lock); pr_err("%s: tx_active not set, return\n", __func__); rc = -EPERM; goto error; } rc = snddev_icodec_close_tx(icodec); if (!IS_ERR_VALUE(rc)) drv->tx_active = 0; else pr_err("%s: close tx failed, rc = %d\n", __func__, rc); mutex_unlock(&drv->tx_lock); } error: return rc; } static int snddev_icodec_check_freq(u32 req_freq) { int rc = -EINVAL; if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) { if ((req_freq == 8000) || (req_freq == 11025) || (req_freq == 12000) || (req_freq == 16000) || (req_freq == 22050) || (req_freq == 24000) || (req_freq == 32000) || (req_freq == 44100) || (req_freq == 48000)) { rc = 0; } else pr_info("%s: Unsupported Frequency:%d\n", __func__, req_freq); } return rc; } static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) { int rc; struct snddev_icodec_state *icodec; if (!dev_info) { rc = -EINVAL; goto error; } icodec = dev_info->private_data; if (adie_codec_freq_supported(icodec->data->profile, rate) != 0) { pr_err("%s: adie_codec_freq_supported() failed\n", __func__); rc = -EINVAL; goto error; } else { if (snddev_icodec_check_freq(rate) != 0) { pr_err("%s: check_freq failed\n", __func__); rc = -EINVAL; goto error; } else icodec->sample_rate = rate; } if (icodec->enabled) { snddev_icodec_close(dev_info); snddev_icodec_open(dev_info); } return icodec->sample_rate; error: return rc; } static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info, u32 enable, uint16_t gain) { int rc = 0; struct snddev_icodec_state *icodec; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; if (!dev_info) { pr_err("invalid dev_info\n"); rc = -EINVAL; goto error; } icodec = dev_info->private_data; if (icodec->data->capability & SNDDEV_CAP_RX) { mutex_lock(&drv->rx_lock); if (!drv->rx_active || !dev_info->opened) { pr_err("dev not active\n"); rc = -EPERM; mutex_unlock(&drv->rx_lock); goto error; } rc = afe_sidetone(PRIMARY_I2S_TX, PRIMARY_I2S_RX, enable, gain); if (rc < 0) pr_err("%s: AFE command sidetone failed\n", __func__); mutex_unlock(&drv->rx_lock); } else { rc = -EINVAL; pr_err("rx device only\n"); } error: return rc; } static int snddev_icodec_enable_anc(struct msm_snddev_info *dev_info, u32 enable) { int rc = 0; struct adie_codec_anc_data *reg_writes; struct acdb_cal_block cal_block; struct snddev_icodec_state *icodec; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; pr_info("%s: enable=%d\n", __func__, enable); if (!dev_info) { pr_err("invalid dev_info\n"); rc = -EINVAL; goto error; } icodec = dev_info->private_data; if ((icodec->data->capability & SNDDEV_CAP_RX) && (icodec->data->capability & SNDDEV_CAP_ANC)) { mutex_lock(&drv->rx_lock); if (!drv->rx_active || !dev_info->opened) { pr_err("dev not active\n"); rc = -EPERM; mutex_unlock(&drv->rx_lock); goto error; } if (enable) { get_anc_cal(&cal_block); reg_writes = (struct adie_codec_anc_data *) cal_block.cal_kvaddr; if (reg_writes == NULL) { pr_err("error, no calibration data\n"); rc = -1; mutex_unlock(&drv->rx_lock); goto error; } rc = adie_codec_enable_anc(icodec->adie_path, 1, reg_writes); } else { rc = adie_codec_enable_anc(icodec->adie_path, 0, NULL); } mutex_unlock(&drv->rx_lock); } else { rc = -EINVAL; pr_err("rx and ANC device only\n"); } error: return rc; } int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info, u32 volume) { struct snddev_icodec_state *icodec; struct mutex *lock; struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; int rc = -EPERM; if (!dev_info) { pr_info("%s : device not intilized.\n", __func__); return -EINVAL; } icodec = dev_info->private_data; if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL | SNDDEV_DEV_VOL_ANALOG))) { pr_info("%s : device %s does not support device volume " "control.", __func__, dev_info->name); return -EPERM; } dev_info->dev_volume = volume; if (icodec->data->capability & SNDDEV_CAP_RX) lock = &drv->rx_lock; else if (icodec->data->capability & SNDDEV_CAP_LB) lock = &drv->lb_lock; else lock = &drv->tx_lock; mutex_lock(lock); rc = snddev_icodec_set_device_volume_impl(dev_info, dev_info->dev_volume); mutex_unlock(lock); return rc; } static int snddev_icodec_probe(struct platform_device *pdev) { int rc = 0; struct snddev_icodec_data *pdata; struct msm_snddev_info *dev_info; struct snddev_icodec_state *icodec; if (!pdev || !pdev->dev.platform_data) { printk(KERN_ALERT "Invalid caller\n"); rc = -1; goto error; } pdata = pdev->dev.platform_data; if ((pdata->capability & SNDDEV_CAP_RX) && (pdata->capability & SNDDEV_CAP_TX)) { pr_err("%s: invalid device data either RX or TX\n", __func__); goto error; } icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL); if (!icodec) { rc = -ENOMEM; goto error; } dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); if (!dev_info) { kfree(icodec); rc = -ENOMEM; goto error; } dev_info->name = pdata->name; dev_info->copp_id = pdata->copp_id; dev_info->private_data = (void *) icodec; dev_info->dev_ops.open = snddev_icodec_open; dev_info->dev_ops.close = snddev_icodec_close; dev_info->dev_ops.set_freq = snddev_icodec_set_freq; dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume; dev_info->capability = pdata->capability; dev_info->opened = 0; msm_snddev_register(dev_info); icodec->data = pdata; icodec->sample_rate = pdata->default_sample_rate; dev_info->sample_rate = pdata->default_sample_rate; dev_info->channel_mode = pdata->channel_mode; if (pdata->capability & SNDDEV_CAP_RX) dev_info->dev_ops.enable_sidetone = snddev_icodec_enable_sidetone; else dev_info->dev_ops.enable_sidetone = NULL; if (pdata->capability & SNDDEV_CAP_ANC) { dev_info->dev_ops.enable_anc = snddev_icodec_enable_anc; } else { dev_info->dev_ops.enable_anc = NULL; } error: return rc; } static int snddev_icodec_remove(struct platform_device *pdev) { return 0; } static struct platform_driver snddev_icodec_driver = { .probe = snddev_icodec_probe, .remove = snddev_icodec_remove, .driver = { .name = "snddev_icodec" } }; module_param(msm_codec_i2s_slave_mode, bool, 0); MODULE_PARM_DESC(msm_codec_i2s_slave_mode, "Set MSM to I2S slave clock mode"); static int __init snddev_icodec_init(void) { s32 rc; struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; rc = platform_driver_register(&snddev_icodec_driver); if (IS_ERR_VALUE(rc)) { pr_err("%s: platform_driver_register for snddev icodec failed\n", __func__); goto error_snddev_icodec_driver; } rc = platform_driver_register(&msm_cdcclk_ctl_driver); if (IS_ERR_VALUE(rc)) { pr_err("%s: platform_driver_register for msm snddev failed\n", __func__); goto error_msm_cdcclk_ctl_driver; } rc = platform_driver_register(&msm_icodec_gpio_driver); if (IS_ERR_VALUE(rc)) { pr_err("%s: platform_driver_register for msm snddev gpio failed\n", __func__); goto error_msm_icodec_gpio_driver; } mutex_init(&icodec_drv->rx_lock); mutex_init(&icodec_drv->lb_lock); mutex_init(&icodec_drv->tx_lock); icodec_drv->rx_active = 0; icodec_drv->tx_active = 0; icodec_drv->snddev_vreg = vreg_init(); pm_qos_add_request(&icodec_drv->tx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); pm_qos_add_request(&icodec_drv->rx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); return 0; error_msm_icodec_gpio_driver: platform_driver_unregister(&msm_cdcclk_ctl_driver); error_msm_cdcclk_ctl_driver: platform_driver_unregister(&snddev_icodec_driver); error_snddev_icodec_driver: return -ENODEV; } static void __exit snddev_icodec_exit(void) { struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; platform_driver_unregister(&snddev_icodec_driver); platform_driver_unregister(&msm_cdcclk_ctl_driver); platform_driver_unregister(&msm_icodec_gpio_driver); clk_put(icodec_drv->rx_osrclk); clk_put(icodec_drv->tx_osrclk); if (icodec_drv->snddev_vreg) { vreg_deinit(icodec_drv->snddev_vreg); icodec_drv->snddev_vreg = NULL; } return; } module_init(snddev_icodec_init); module_exit(snddev_icodec_exit); MODULE_DESCRIPTION("ICodec Sound Device driver"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2");