Merge "ASoc : use hwdep node to get calibration"
This commit is contained in:
commit
16942c11bf
|
@ -11,3 +11,4 @@ header-y += compress_offload.h
|
|||
header-y += tlv.h
|
||||
header-y += compress_params.h
|
||||
header-y += compress_offload.h
|
||||
header-y += msmcal-hwdep.h
|
||||
|
|
|
@ -95,9 +95,10 @@ enum {
|
|||
SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */
|
||||
SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
|
||||
SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
|
||||
SNDRV_HWDEP_IFACE_AUDIO_CODEC, /* codec Audio Control */
|
||||
|
||||
/* Don't forget to change the following: */
|
||||
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
|
||||
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_AUDIO_CODEC
|
||||
};
|
||||
|
||||
struct snd_hwdep_info {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.
|
||||
*/
|
||||
#ifndef _CALIB_HWDEP_H
|
||||
#define _CALIB_HWDEP_H
|
||||
|
||||
#define WCD9XXX_CODEC_HWDEP_NODE 1000
|
||||
enum wcd_cal_type {
|
||||
WCD9XXX_MIN_CAL,
|
||||
WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL,
|
||||
WCD9XXX_MBHC_CAL,
|
||||
WCD9XXX_MAX_CAL,
|
||||
};
|
||||
|
||||
struct wcdcal_ioctl_buffer {
|
||||
__u32 size;
|
||||
__u8 __user *buffer;
|
||||
enum wcd_cal_type cal_type;
|
||||
};
|
||||
|
||||
#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \
|
||||
_IOW('U', 0x1, struct wcdcal_ioctl_buffer)
|
||||
|
||||
#endif /*_CALIB_HWDEP_H*/
|
|
@ -49,7 +49,7 @@ snd-soc-twl6040-objs := twl6040.o
|
|||
snd-soc-uda134x-objs := uda134x.o
|
||||
snd-soc-uda1380-objs := uda1380.o
|
||||
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
|
||||
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
|
||||
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o wcdcal-hwdep.o
|
||||
snd-soc-cs8427-objs := cs8427.o
|
||||
snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
|
||||
snd-soc-wl1273-objs := wl1273.o
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2012-2013,2015 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
|
||||
|
@ -38,6 +38,7 @@
|
|||
#include <linux/wakelock.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "wcd9304.h"
|
||||
#include "wcdcal-hwdep.h"
|
||||
|
||||
#define WCD9304_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
|
||||
SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_48000)
|
||||
|
@ -341,6 +342,10 @@ struct sitar_priv {
|
|||
|
||||
bool gpio_irq_resend;
|
||||
struct wake_lock irq_resend_wlock;
|
||||
|
||||
/* cal info for codec */
|
||||
struct fw_info *fw_data;
|
||||
struct firmware_cal *mbhc_cal;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -1567,7 +1572,7 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
const char *filename;
|
||||
const struct firmware *fw;
|
||||
int i;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int num_anc_slots;
|
||||
struct anc_header *anc_head;
|
||||
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -1576,6 +1581,9 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
u32 *anc_ptr;
|
||||
u16 reg;
|
||||
u8 mask, val, old_val;
|
||||
size_t cal_size;
|
||||
const void *data;
|
||||
struct firmware_cal *hwdep_cal = NULL;
|
||||
|
||||
pr_debug("%s %d\n", __func__, event);
|
||||
switch (event) {
|
||||
|
@ -1586,38 +1594,54 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
* WCD9310 and WCD9304
|
||||
*/
|
||||
filename = "wcd9310/wcd9310_anc.bin";
|
||||
hwdep_cal = wcdcal_get_fw_cal(sitar ->fw_data, WCD9XXX_ANC_CAL);
|
||||
if (hwdep_cal) {
|
||||
data = hwdep_cal->data;
|
||||
cal_size = hwdep_cal->size;
|
||||
dev_dbg(codec->dev, "%s: using hwdep calibration\n",
|
||||
__func__);
|
||||
} else {
|
||||
ret = request_firmware(&fw, filename, codec->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!fw) {
|
||||
dev_err(codec->dev, "failed to get anc fw");
|
||||
return -ENODEV;
|
||||
}
|
||||
data = fw->data;
|
||||
cal_size = fw->size;
|
||||
dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
|
||||
__func__);
|
||||
|
||||
ret = request_firmware(&fw, filename, codec->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (fw->size < sizeof(struct anc_header)) {
|
||||
if (cal_size < sizeof(struct anc_header)) {
|
||||
dev_err(codec->dev, "Not enough data\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* First number is the number of register writes */
|
||||
anc_head = (struct anc_header *)(fw->data);
|
||||
anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
|
||||
anc_size_remaining = fw->size - sizeof(struct anc_header);
|
||||
anc_head = (struct anc_header *)(data);
|
||||
anc_ptr = (u32 *)((u32)data + sizeof(struct anc_header));
|
||||
anc_size_remaining = cal_size - sizeof(struct anc_header);
|
||||
num_anc_slots = anc_head->num_anc_slots;
|
||||
|
||||
if (sitar->anc_slot >= num_anc_slots) {
|
||||
dev_err(codec->dev, "Invalid ANC slot selected\n");
|
||||
release_firmware(fw);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_anc_slots; i++) {
|
||||
|
||||
if (anc_size_remaining < SITAR_PACKED_REG_SIZE) {
|
||||
dev_err(codec->dev, "Invalid register format\n");
|
||||
release_firmware(fw);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
anc_writes_size = (u32)(*anc_ptr);
|
||||
anc_size_remaining -= sizeof(u32);
|
||||
|
@ -1626,8 +1650,8 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
if (anc_writes_size * SITAR_PACKED_REG_SIZE
|
||||
> anc_size_remaining) {
|
||||
dev_err(codec->dev, "Invalid register format\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sitar->anc_slot == i)
|
||||
|
@ -1639,8 +1663,8 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
if (i == num_anc_slots) {
|
||||
dev_err(codec->dev, "Selected ANC slot not present\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < anc_writes_size; i++) {
|
||||
|
@ -1651,7 +1675,8 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
(val & mask));
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
if (!hwdep_cal)
|
||||
release_firmware(fw);
|
||||
|
||||
/* For Sitar, it is required to enable both Feed-forward
|
||||
* and Feed back clocks to enable ANC
|
||||
|
@ -1666,6 +1691,11 @@ static int sitar_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
break;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
if (!hwdep_cal)
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -4363,39 +4393,63 @@ void sitar_mbhc_init(struct snd_soc_codec *codec)
|
|||
|
||||
}
|
||||
|
||||
static bool sitar_mbhc_fw_validate(const struct firmware *fw)
|
||||
static bool sitar_mbhc_fw_validate(const void *data, size_t size)
|
||||
{
|
||||
u32 cfg_offset;
|
||||
struct sitar_mbhc_imped_detect_cfg *imped_cfg;
|
||||
struct sitar_mbhc_btn_detect_cfg *btn_cfg;
|
||||
struct firmware_cal fw;
|
||||
|
||||
if (fw->size < SITAR_MBHC_CAL_MIN_SIZE)
|
||||
fw.data = (void *)data;
|
||||
fw.size = size;
|
||||
if (fw.size < SITAR_MBHC_CAL_MIN_SIZE)
|
||||
return false;
|
||||
|
||||
/* previous check guarantees that there is enough fw data up
|
||||
* to num_btn
|
||||
*/
|
||||
btn_cfg = SITAR_MBHC_CAL_BTN_DET_PTR(fw->data);
|
||||
cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
|
||||
if (fw->size < (cfg_offset + SITAR_MBHC_CAL_BTN_SZ(btn_cfg)))
|
||||
btn_cfg = SITAR_MBHC_CAL_BTN_DET_PTR(fw.data);
|
||||
cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
|
||||
if (fw.size < (cfg_offset + SITAR_MBHC_CAL_BTN_SZ(btn_cfg)))
|
||||
return false;
|
||||
|
||||
/* previous check guarantees that there is enough fw data up
|
||||
* to start of impedance detection configuration
|
||||
*/
|
||||
imped_cfg = SITAR_MBHC_CAL_IMPED_DET_PTR(fw->data);
|
||||
cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
|
||||
imped_cfg = SITAR_MBHC_CAL_IMPED_DET_PTR(fw.data);
|
||||
cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
|
||||
|
||||
if (fw->size < (cfg_offset + SITAR_MBHC_CAL_IMPED_MIN_SZ))
|
||||
if (fw.size < (cfg_offset + SITAR_MBHC_CAL_IMPED_MIN_SZ))
|
||||
return false;
|
||||
|
||||
if (fw->size < (cfg_offset + SITAR_MBHC_CAL_IMPED_SZ(imped_cfg)))
|
||||
if (fw.size < (cfg_offset + SITAR_MBHC_CAL_IMPED_SZ(imped_cfg)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
struct firmware_cal *get_hwdep_fw_cal(struct snd_soc_codec *codec,
|
||||
enum wcd_cal_type type)
|
||||
{
|
||||
struct sitar_priv *sitar;
|
||||
struct firmware_cal *hwdep_cal;
|
||||
|
||||
if (!codec) {
|
||||
pr_err("%s: NULL codec pointer\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
sitar = snd_soc_codec_get_drvdata(codec);
|
||||
hwdep_cal = wcdcal_get_fw_cal(sitar->fw_data, type);
|
||||
if (!hwdep_cal) {
|
||||
dev_err(codec->dev, "%s: cal not sent by %d\n",
|
||||
__func__, type);
|
||||
return NULL;
|
||||
}
|
||||
return hwdep_cal;
|
||||
}
|
||||
|
||||
static void sitar_turn_onoff_override(struct snd_soc_codec *codec, bool on)
|
||||
{
|
||||
snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
|
||||
|
@ -5053,6 +5107,8 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
struct snd_soc_codec *codec;
|
||||
const struct firmware *fw;
|
||||
int ret = -1, retry = 0;
|
||||
struct firmware_cal *fw_data = NULL;
|
||||
bool use_default_cal = false;
|
||||
|
||||
dwork = to_delayed_work(work);
|
||||
sitar = container_of(dwork, struct sitar_priv,
|
||||
|
@ -5061,12 +5117,19 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
|
||||
while (retry < MBHC_FW_READ_ATTEMPTS) {
|
||||
retry++;
|
||||
pr_info("%s:Attempt %d to request MBHC firmware\n",
|
||||
__func__, retry);
|
||||
ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
|
||||
pr_err("%s:Attempt %d to request MBHC firmware\n",
|
||||
__func__, retry);
|
||||
fw_data = get_hwdep_fw_cal(codec,
|
||||
WCD9XXX_MBHC_CAL);
|
||||
if (!fw_data)
|
||||
ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
|
||||
codec->dev);
|
||||
|
||||
if (ret != 0) {
|
||||
/*
|
||||
* if request_firmware and hwdep cal both fail then
|
||||
* retry for few times before bailing out
|
||||
*/
|
||||
if ((ret != 0) && !fw_data) {
|
||||
usleep_range(MBHC_FW_READ_TIMEOUT,
|
||||
MBHC_FW_READ_TIMEOUT);
|
||||
} else {
|
||||
|
@ -5075,18 +5138,43 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
if (!fw_data)
|
||||
pr_debug("%s: using request_firmware\n", __func__);
|
||||
else
|
||||
pr_debug("%s: using hwdep cal\n", __func__);
|
||||
if (ret != 0 && !fw_data) {
|
||||
pr_err("%s: Cannot load MBHC firmware use default cal\n",
|
||||
__func__);
|
||||
} else if (sitar_mbhc_fw_validate(fw) == false) {
|
||||
pr_err("%s: Invalid MBHC cal data size use default cal\n",
|
||||
__func__);
|
||||
release_firmware(fw);
|
||||
} else {
|
||||
sitar->calibration = (void *)fw->data;
|
||||
sitar->mbhc_fw = fw;
|
||||
use_default_cal = true;
|
||||
}
|
||||
if (!use_default_cal) {
|
||||
const void *data;
|
||||
size_t size;
|
||||
if (fw_data) {
|
||||
data = fw_data->data;
|
||||
size = fw_data->size;
|
||||
|
||||
} else {
|
||||
data = fw->data;
|
||||
size = fw->size;
|
||||
}
|
||||
if (sitar_mbhc_fw_validate(data, size) == false) {
|
||||
pr_err("%s: Invalid MBHC cal data size use default cal\n",
|
||||
__func__);
|
||||
if (!fw_data)
|
||||
release_firmware(fw);
|
||||
} else {
|
||||
if (fw_data) {
|
||||
sitar->mbhc_cfg.calibration =
|
||||
(void *)fw_data->data;
|
||||
sitar->mbhc_cal = fw_data;
|
||||
} else {
|
||||
sitar->mbhc_cfg.calibration =
|
||||
(void *)fw->data;
|
||||
sitar->mbhc_fw = fw;
|
||||
}
|
||||
}
|
||||
}
|
||||
sitar_mbhc_init_and_calibrate(codec);
|
||||
}
|
||||
|
||||
|
@ -5129,13 +5217,21 @@ int sitar_hs_detect(struct snd_soc_codec *codec,
|
|||
INIT_WORK(&sitar->hs_correct_plug_work,
|
||||
sitar_hs_correct_gpio_plug);
|
||||
|
||||
if (!sitar->mbhc_cfg.read_fw_bin) {
|
||||
if ((!sitar->mbhc_cfg.read_fw_bin) ||
|
||||
(sitar->mbhc_cfg.read_fw_bin && sitar->mbhc_fw) ||
|
||||
(sitar->mbhc_cfg.read_fw_bin && sitar->mbhc_cal)) {
|
||||
rc = sitar_mbhc_init_and_calibrate(codec);
|
||||
} else {
|
||||
schedule_delayed_work(&sitar->mbhc_firmware_dwork,
|
||||
usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
|
||||
if (!sitar->mbhc_fw || sitar->mbhc_cal)
|
||||
schedule_delayed_work(&sitar ->mbhc_firmware_dwork,
|
||||
usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
|
||||
else
|
||||
pr_err("%s: Skipping to read mbhc fw, 0x%p 0x%p\n",
|
||||
__func__, sitar ->mbhc_fw, sitar->mbhc_cal);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sitar_hs_detect);
|
||||
|
@ -5936,7 +6032,8 @@ static int sitar_codec_probe(struct snd_soc_codec *codec)
|
|||
sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
|
||||
if (!sitar) {
|
||||
dev_err(codec->dev, "Failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
goto err_nomem_slimch;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_DECIMATORS; i++) {
|
||||
|
@ -5989,6 +6086,20 @@ static int sitar_codec_probe(struct snd_soc_codec *codec)
|
|||
goto err_pdata;
|
||||
}
|
||||
|
||||
sitar->fw_data = kzalloc(sizeof(*(sitar ->fw_data)), GFP_KERNEL);
|
||||
if (!sitar ->fw_data) {
|
||||
dev_err(codec->dev, "Failed to allocate fw_data\n");
|
||||
goto err_nomem_slimch;
|
||||
}
|
||||
set_bit(WCD9XXX_ANC_CAL, sitar->fw_data->cal_bit);
|
||||
set_bit(WCD9XXX_MBHC_CAL, sitar->fw_data->cal_bit);
|
||||
ret = wcd_cal_create_hwdep(sitar->fw_data,
|
||||
WCD9XXX_CODEC_HWDEP_NODE, codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
|
||||
goto err_hwdep;
|
||||
}
|
||||
|
||||
snd_soc_add_codec_controls(codec, sitar_snd_controls,
|
||||
ARRAY_SIZE(sitar_snd_controls));
|
||||
snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
|
||||
|
@ -6125,7 +6236,10 @@ err_potential_irq:
|
|||
err_remove_irq:
|
||||
wcd9xxx_free_irq(codec->control_data,
|
||||
SITAR_IRQ_MBHC_INSERTION, sitar);
|
||||
err_hwdep:
|
||||
err_insert_irq:
|
||||
kfree(sitar->fw_data);
|
||||
err_nomem_slimch:
|
||||
err_pdata:
|
||||
mutex_destroy(&sitar->codec_resource_lock);
|
||||
kfree(sitar);
|
||||
|
@ -6147,12 +6261,13 @@ static int sitar_codec_remove(struct snd_soc_codec *codec)
|
|||
sitar_codec_disable_clock_block(codec);
|
||||
SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
|
||||
sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
|
||||
if (sitar->mbhc_fw)
|
||||
if (sitar->mbhc_fw||sitar->mbhc_cal)
|
||||
release_firmware(sitar->mbhc_fw);
|
||||
for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
|
||||
kfree(sitar->dai[i].ch_num);
|
||||
mutex_destroy(&sitar->codec_resource_lock);
|
||||
kfree(sitar);
|
||||
kfree(sitar->fw_data);
|
||||
return 0;
|
||||
}
|
||||
static struct snd_soc_codec_driver soc_codec_dev_sitar = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2011-2013,2015 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
|
||||
|
@ -37,6 +37,7 @@
|
|||
#include <linux/wakelock.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "wcd9310.h"
|
||||
#include "wcdcal-hwdep.h"
|
||||
|
||||
static int cfilt_adjust_ms = 10;
|
||||
module_param(cfilt_adjust_ms, int, 0644);
|
||||
|
@ -345,6 +346,7 @@ struct tabla_priv {
|
|||
/* Work to perform MBHC Firmware Read */
|
||||
struct delayed_work mbhc_firmware_dwork;
|
||||
const struct firmware *mbhc_fw;
|
||||
struct firmware_cal *mbhc_cal;
|
||||
|
||||
/* num of slim ports required */
|
||||
struct tabla_codec_dai_data dai[NUM_CODEC_DAIS];
|
||||
|
@ -388,6 +390,8 @@ struct tabla_priv {
|
|||
struct dentry *debugfs_poke;
|
||||
struct dentry *debugfs_mbhc;
|
||||
#endif
|
||||
/* cal info for codec */
|
||||
struct fw_info *fw_data;
|
||||
};
|
||||
|
||||
static const u32 comp_shift[] = {
|
||||
|
@ -3087,7 +3091,7 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
const char *filename;
|
||||
const struct firmware *fw;
|
||||
int i;
|
||||
int ret;
|
||||
int ret =0;
|
||||
int num_anc_slots;
|
||||
struct anc_header *anc_head;
|
||||
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -3096,6 +3100,9 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
u32 *anc_ptr;
|
||||
u16 reg;
|
||||
u8 mask, val, old_val;
|
||||
size_t cal_size;
|
||||
const void *data;
|
||||
struct firmware_cal *hwdep_cal = NULL;
|
||||
|
||||
pr_debug("%s: DAPM Event %d ANC func is %d\n",
|
||||
__func__, event, tabla->anc_func);
|
||||
|
@ -3107,24 +3114,40 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
case SND_SOC_DAPM_PRE_PMU:
|
||||
|
||||
filename = "wcd9310/wcd9310_anc.bin";
|
||||
hwdep_cal = wcdcal_get_fw_cal(tabla->fw_data, WCD9XXX_ANC_CAL);
|
||||
if (hwdep_cal) {
|
||||
data = hwdep_cal->data;
|
||||
cal_size = hwdep_cal->size;
|
||||
dev_dbg(codec->dev, "%s: using hwdep calibration\n",
|
||||
__func__);
|
||||
} else {
|
||||
ret = request_firmware(&fw, filename, codec->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!fw) {
|
||||
dev_err(codec->dev, "failed to get anc fw");
|
||||
return -ENODEV;
|
||||
}
|
||||
data = fw->data;
|
||||
cal_size = fw->size;
|
||||
dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
|
||||
__func__);
|
||||
|
||||
ret = request_firmware(&fw, filename, codec->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (fw->size < sizeof(struct anc_header)) {
|
||||
if (cal_size < sizeof(struct anc_header)) {
|
||||
dev_err(codec->dev, "Not enough data\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* First number is the number of register writes */
|
||||
anc_head = (struct anc_header *)(fw->data);
|
||||
anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
|
||||
anc_size_remaining = fw->size - sizeof(struct anc_header);
|
||||
anc_head = (struct anc_header *)(data);
|
||||
anc_ptr = (u32 *)((u32)data + sizeof(struct anc_header));
|
||||
anc_size_remaining = cal_size - sizeof(struct anc_header);
|
||||
num_anc_slots = anc_head->num_anc_slots;
|
||||
|
||||
if (tabla->anc_slot >= num_anc_slots) {
|
||||
|
@ -3137,8 +3160,8 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
|
||||
if (anc_size_remaining < TABLA_PACKED_REG_SIZE) {
|
||||
dev_err(codec->dev, "Invalid register format\n");
|
||||
release_firmware(fw);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
anc_writes_size = (u32)(*anc_ptr);
|
||||
anc_size_remaining -= sizeof(u32);
|
||||
|
@ -3147,8 +3170,8 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
if (anc_writes_size * TABLA_PACKED_REG_SIZE
|
||||
> anc_size_remaining) {
|
||||
dev_err(codec->dev, "Invalid register format\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (tabla->anc_slot == i)
|
||||
|
@ -3160,8 +3183,9 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
if (i == num_anc_slots) {
|
||||
dev_err(codec->dev, "Selected ANC slot not present\n");
|
||||
release_firmware(fw);
|
||||
return -ENOMEM;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < anc_writes_size; i++) {
|
||||
|
@ -3171,7 +3195,9 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
snd_soc_write(codec, reg, (old_val & ~mask) |
|
||||
(val & mask));
|
||||
}
|
||||
release_firmware(fw);
|
||||
if (!hwdep_cal)
|
||||
release_firmware(fw);
|
||||
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
snd_soc_update_bits(codec, TABLA_A_CDC_ANC1_CTL, 0x01, 0x00);
|
||||
|
@ -3183,6 +3209,11 @@ static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
|
|||
break;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
if (!hwdep_cal)
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
|
||||
|
@ -6330,33 +6361,37 @@ void tabla_mbhc_init(struct snd_soc_codec *codec)
|
|||
tabla->mbhc_cfg.micbias);
|
||||
}
|
||||
|
||||
static bool tabla_mbhc_fw_validate(const struct firmware *fw)
|
||||
static bool tabla_mbhc_fw_validate(const void *data, size_t size)
|
||||
{
|
||||
u32 cfg_offset;
|
||||
struct tabla_mbhc_imped_detect_cfg *imped_cfg;
|
||||
struct tabla_mbhc_btn_detect_cfg *btn_cfg;
|
||||
struct firmware_cal fw;
|
||||
|
||||
if (fw->size < TABLA_MBHC_CAL_MIN_SIZE)
|
||||
fw.data = (void *)data;
|
||||
fw.size = size;
|
||||
|
||||
if (fw.size < TABLA_MBHC_CAL_MIN_SIZE)
|
||||
return false;
|
||||
|
||||
/* previous check guarantees that there is enough fw data up
|
||||
* to num_btn
|
||||
*/
|
||||
btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(fw->data);
|
||||
cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
|
||||
if (fw->size < (cfg_offset + TABLA_MBHC_CAL_BTN_SZ(btn_cfg)))
|
||||
btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(fw.data);
|
||||
cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
|
||||
if (fw.size < (cfg_offset + TABLA_MBHC_CAL_BTN_SZ(btn_cfg)))
|
||||
return false;
|
||||
|
||||
/* previous check guarantees that there is enough fw data up
|
||||
* to start of impedance detection configuration
|
||||
*/
|
||||
imped_cfg = TABLA_MBHC_CAL_IMPED_DET_PTR(fw->data);
|
||||
cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
|
||||
imped_cfg = TABLA_MBHC_CAL_IMPED_DET_PTR(fw.data);
|
||||
cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
|
||||
|
||||
if (fw->size < (cfg_offset + TABLA_MBHC_CAL_IMPED_MIN_SZ))
|
||||
if (fw.size < (cfg_offset + TABLA_MBHC_CAL_IMPED_MIN_SZ))
|
||||
return false;
|
||||
|
||||
if (fw->size < (cfg_offset + TABLA_MBHC_CAL_IMPED_SZ(imped_cfg)))
|
||||
if (fw.size < (cfg_offset + TABLA_MBHC_CAL_IMPED_SZ(imped_cfg)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -7884,6 +7919,26 @@ static int tabla_mbhc_init_and_calibrate(struct tabla_priv *tabla)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
struct firmware_cal *get_hwdep_fw_cal(struct snd_soc_codec *codec,
|
||||
enum wcd_cal_type type)
|
||||
{
|
||||
struct tabla_priv *tabla;
|
||||
struct firmware_cal *hwdep_cal;
|
||||
|
||||
if (!codec) {
|
||||
pr_err("%s: NULL codec pointer\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
tabla = snd_soc_codec_get_drvdata(codec);
|
||||
hwdep_cal = wcdcal_get_fw_cal(tabla->fw_data, type);
|
||||
if (!hwdep_cal) {
|
||||
dev_err(codec->dev, "%s: cal not sent by %d\n",
|
||||
__func__, type);
|
||||
return NULL;
|
||||
}
|
||||
return hwdep_cal;
|
||||
}
|
||||
static void mbhc_fw_read(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork;
|
||||
|
@ -7891,6 +7946,8 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
struct snd_soc_codec *codec;
|
||||
const struct firmware *fw;
|
||||
int ret = -1, retry = 0;
|
||||
struct firmware_cal *fw_data = NULL;
|
||||
bool use_default_cal = false;
|
||||
|
||||
dwork = to_delayed_work(work);
|
||||
tabla = container_of(dwork, struct tabla_priv, mbhc_firmware_dwork);
|
||||
|
@ -7898,12 +7955,18 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
|
||||
while (retry < MBHC_FW_READ_ATTEMPTS) {
|
||||
retry++;
|
||||
pr_info("%s:Attempt %d to request MBHC firmware\n",
|
||||
__func__, retry);
|
||||
ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
|
||||
pr_err("%s:Attempt %d to request MBHC firmware\n",
|
||||
__func__, retry);
|
||||
fw_data = get_hwdep_fw_cal(codec,
|
||||
WCD9XXX_MBHC_CAL);
|
||||
if (!fw_data)
|
||||
ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
|
||||
codec->dev);
|
||||
|
||||
if (ret != 0) {
|
||||
/*
|
||||
* if request_firmware and hwdep cal both fail then
|
||||
* retry for few times before bailing out
|
||||
*/
|
||||
if ((ret != 0) && !fw_data) {
|
||||
usleep_range(MBHC_FW_READ_TIMEOUT,
|
||||
MBHC_FW_READ_TIMEOUT);
|
||||
} else {
|
||||
|
@ -7912,16 +7975,43 @@ static void mbhc_fw_read(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
if (!fw_data)
|
||||
pr_debug("%s: using request_firmware\n", __func__);
|
||||
else
|
||||
pr_debug("%s: using hwdep cal\n", __func__);
|
||||
if (ret != 0 && !fw_data) {
|
||||
|
||||
pr_err("%s: Cannot load MBHC firmware use default cal\n",
|
||||
__func__);
|
||||
} else if (tabla_mbhc_fw_validate(fw) == false) {
|
||||
pr_err("%s: Invalid MBHC cal data size use default cal\n",
|
||||
__func__);
|
||||
release_firmware(fw);
|
||||
use_default_cal = true;
|
||||
}
|
||||
if (!use_default_cal) {
|
||||
const void *data;
|
||||
size_t size;
|
||||
|
||||
if (fw_data) {
|
||||
data = fw_data->data;
|
||||
size = fw_data->size;
|
||||
} else {
|
||||
tabla->mbhc_cfg.calibration = (void *)fw->data;
|
||||
tabla->mbhc_fw = fw;
|
||||
data = fw->data;
|
||||
size = fw->size;
|
||||
}
|
||||
if (tabla_mbhc_fw_validate(data, size) == false) {
|
||||
pr_err("%s: Invalid MBHC cal data size use default cal\n",
|
||||
__func__);
|
||||
if (!fw_data)
|
||||
release_firmware(fw);
|
||||
} else {
|
||||
if (fw_data) {
|
||||
tabla->mbhc_cfg.calibration =
|
||||
(void *)fw_data->data;
|
||||
tabla->mbhc_cal = fw_data;
|
||||
} else {
|
||||
tabla->mbhc_cfg.calibration =
|
||||
(void *)fw->data;
|
||||
tabla->mbhc_fw = fw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void) tabla_mbhc_init_and_calibrate(tabla);
|
||||
|
@ -7964,12 +8054,20 @@ int tabla_hs_detect(struct snd_soc_codec *codec,
|
|||
INIT_WORK(&tabla->hphrocp_work, hphrocp_off_report);
|
||||
INIT_DELAYED_WORK(&tabla->mbhc_insert_dwork, mbhc_insert_work);
|
||||
|
||||
if (!tabla->mbhc_cfg.read_fw_bin)
|
||||
if ((!tabla->mbhc_cfg.read_fw_bin)||
|
||||
(tabla->mbhc_cfg.read_fw_bin && tabla->mbhc_fw) ||
|
||||
(tabla->mbhc_cfg.read_fw_bin && tabla->mbhc_cal)) {
|
||||
rc = tabla_mbhc_init_and_calibrate(tabla);
|
||||
else
|
||||
schedule_delayed_work(&tabla->mbhc_firmware_dwork,
|
||||
}
|
||||
else {
|
||||
if (!tabla->mbhc_fw || !tabla->mbhc_cal)
|
||||
schedule_delayed_work(&tabla->mbhc_firmware_dwork,
|
||||
usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
|
||||
else
|
||||
pr_err("%s: Skipping to read mbhc fw, 0x%p 0x%p\n",
|
||||
__func__, tabla->mbhc_fw, tabla->mbhc_cal);
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tabla_hs_detect);
|
||||
|
@ -8496,7 +8594,8 @@ static int tabla_codec_probe(struct snd_soc_codec *codec)
|
|||
tabla = kzalloc(sizeof(struct tabla_priv), GFP_KERNEL);
|
||||
if (!tabla) {
|
||||
dev_err(codec->dev, "Failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
goto err_nomem_slimch;
|
||||
|
||||
}
|
||||
for (i = 0 ; i < NUM_DECIMATORS; i++) {
|
||||
tx_hpf_work[i].tabla = tabla;
|
||||
|
@ -8547,6 +8646,19 @@ static int tabla_codec_probe(struct snd_soc_codec *codec)
|
|||
pr_err("%s: bad pdata\n", __func__);
|
||||
goto err_pdata;
|
||||
}
|
||||
tabla->fw_data = kzalloc(sizeof(*(tabla->fw_data)), GFP_KERNEL);
|
||||
if (!tabla->fw_data) {
|
||||
dev_err(codec->dev, "Failed to allocate fw_data\n");
|
||||
goto err_nomem_slimch;
|
||||
}
|
||||
set_bit(WCD9XXX_ANC_CAL, tabla->fw_data->cal_bit);
|
||||
set_bit(WCD9XXX_MBHC_CAL, tabla->fw_data->cal_bit);
|
||||
ret = wcd_cal_create_hwdep(tabla->fw_data,
|
||||
WCD9XXX_CODEC_HWDEP_NODE, codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
|
||||
goto err_hwdep;
|
||||
}
|
||||
|
||||
// snd_soc_add_codec_controls(codec, tabla_snd_controls,
|
||||
// ARRAY_SIZE(tabla_snd_controls));
|
||||
|
@ -8728,7 +8840,10 @@ err_potential_irq:
|
|||
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
|
||||
err_remove_irq:
|
||||
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
|
||||
err_hwdep:
|
||||
err_insert_irq:
|
||||
kfree(tabla->fw_data);
|
||||
err_nomem_slimch:
|
||||
err_pdata:
|
||||
mutex_destroy(&tabla->codec_resource_lock);
|
||||
kfree(tabla);
|
||||
|
@ -8750,7 +8865,7 @@ static int tabla_codec_remove(struct snd_soc_codec *codec)
|
|||
tabla_codec_disable_clock_block(codec);
|
||||
TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
|
||||
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
|
||||
if (tabla->mbhc_fw)
|
||||
if (tabla->mbhc_fw||tabla->mbhc_cal)
|
||||
release_firmware(tabla->mbhc_fw);
|
||||
for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
|
||||
kfree(tabla->dai[i].ch_num);
|
||||
|
@ -8760,6 +8875,7 @@ static int tabla_codec_remove(struct snd_soc_codec *codec)
|
|||
debugfs_remove(tabla->debugfs_mbhc);
|
||||
#endif
|
||||
kfree(tabla);
|
||||
kfree(tabla->fw_data);
|
||||
return 0;
|
||||
}
|
||||
static struct snd_soc_codec_driver soc_codec_dev_tabla = {
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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 <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include <sound/msmcal-hwdep.h>
|
||||
#include <sound/soc.h>
|
||||
#include "wcdcal-hwdep.h"
|
||||
|
||||
const int cal_size_info[WCD9XXX_MAX_CAL] = {
|
||||
[WCD9XXX_ANC_CAL] = 4096,
|
||||
[WCD9XXX_MBHC_CAL] = 4096,
|
||||
};
|
||||
|
||||
const char *cal_name_info[WCD9XXX_MAX_CAL] = {
|
||||
[WCD9XXX_ANC_CAL] = "anc",
|
||||
[WCD9XXX_MBHC_CAL] = "mbhc",
|
||||
};
|
||||
|
||||
struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
|
||||
enum wcd_cal_type type)
|
||||
{
|
||||
if (!fw_data) {
|
||||
pr_err("%s: fw_data is NULL\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
if (type >= WCD9XXX_MAX_CAL ||
|
||||
type < WCD9XXX_MIN_CAL) {
|
||||
pr_err("%s: wrong cal type sent %d\n", __func__, type);
|
||||
return NULL;
|
||||
}
|
||||
mutex_lock(&fw_data->lock);
|
||||
if (!test_bit(WCDCAL_RECIEVED,
|
||||
&fw_data->wcdcal_state[type])) {
|
||||
pr_err("%s: cal not sent by userspace %d\n",
|
||||
__func__, type);
|
||||
mutex_unlock(&fw_data->lock);
|
||||
return NULL;
|
||||
}
|
||||
mutex_unlock(&fw_data->lock);
|
||||
return fw_data->fw[type];
|
||||
}
|
||||
EXPORT_SYMBOL(wcdcal_get_fw_cal);
|
||||
|
||||
static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
|
||||
struct wcdcal_ioctl_buffer fw_user)
|
||||
{
|
||||
struct fw_info *fw_data = hw->private_data;
|
||||
struct firmware_cal **fw = fw_data->fw;
|
||||
void *data;
|
||||
|
||||
if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
|
||||
pr_err("%s: codec didn't set this %d!!\n",
|
||||
__func__, fw_user.cal_type);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
|
||||
fw_user.cal_type < WCD9XXX_MIN_CAL) {
|
||||
pr_err("%s: wrong cal type sent %d\n",
|
||||
__func__, fw_user.cal_type);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (fw_user.size > cal_size_info[fw_user.cal_type] ||
|
||||
fw_user.size <= 0) {
|
||||
pr_err("%s: incorrect firmware size %d for %s\n",
|
||||
__func__, fw_user.size,
|
||||
cal_name_info[fw_user.cal_type]);
|
||||
return -EFAULT;
|
||||
}
|
||||
data = fw[fw_user.cal_type]->data;
|
||||
memcpy(data, fw_user.buffer, fw_user.size);
|
||||
fw[fw_user.cal_type]->size = fw_user.size;
|
||||
mutex_lock(&fw_data->lock);
|
||||
set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
|
||||
mutex_unlock(&fw_data->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct wcdcal_ioctl_buffer32 {
|
||||
u32 size;
|
||||
compat_uptr_t buffer;
|
||||
enum wcd_cal_type cal_type;
|
||||
};
|
||||
|
||||
enum {
|
||||
SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
|
||||
_IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
|
||||
};
|
||||
|
||||
static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
|
||||
struct wcdcal_ioctl_buffer32 fw_user32;
|
||||
struct wcdcal_ioctl_buffer fw_user_compat;
|
||||
|
||||
if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
|
||||
pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
|
||||
pr_err("%s: failed to copy\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
fw_user_compat.size = fw_user32.size;
|
||||
fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
|
||||
fw_user_compat.cal_type = fw_user32.cal_type;
|
||||
return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
|
||||
}
|
||||
#else
|
||||
#define wcdcal_hwdep_ioctl_compat NULL
|
||||
#endif
|
||||
|
||||
static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
|
||||
struct wcdcal_ioctl_buffer fw_user;
|
||||
if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
|
||||
pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
|
||||
pr_err("%s: failed to copy\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
return wcdcal_hwdep_ioctl_shared(hw, fw_user);
|
||||
}
|
||||
|
||||
static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
|
||||
{
|
||||
struct fw_info *fw_data = hw->private_data;
|
||||
mutex_lock(&fw_data->lock);
|
||||
/* clear all the calibrations */
|
||||
memset(fw_data->wcdcal_state, 0,
|
||||
sizeof(fw_data->wcdcal_state));
|
||||
mutex_unlock(&fw_data->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
|
||||
{
|
||||
char hwname[40];
|
||||
struct snd_hwdep *hwdep;
|
||||
struct firmware_cal **fw;
|
||||
struct fw_info *fw_data = data;
|
||||
int err, cal_bit;
|
||||
|
||||
if (!fw_data || !codec) {
|
||||
pr_err("%s: wrong arguments passed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw = fw_data->fw;
|
||||
snprintf(hwname, strlen("Codec %s"), "Codec %s", codec->name);
|
||||
err = snd_hwdep_new(codec->card->snd_card, hwname, node, &hwdep);
|
||||
if (err < 0) {
|
||||
dev_err(codec->dev, "%s: new hwdep failed %d\n",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", codec->name);
|
||||
hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
|
||||
hwdep->private_data = fw_data;
|
||||
hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
|
||||
hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
|
||||
hwdep->ops.release = wcdcal_hwdep_release;
|
||||
mutex_init(&fw_data->lock);
|
||||
|
||||
for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
|
||||
set_bit(WCDCAL_UNINITIALISED,
|
||||
&fw_data->wcdcal_state[cal_bit]);
|
||||
fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
|
||||
if (!fw[cal_bit]) {
|
||||
dev_err(codec->dev, "%s: no memory for %s cal\n",
|
||||
__func__, cal_name_info[cal_bit]);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
|
||||
fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
|
||||
GFP_KERNEL);
|
||||
if (!fw[cal_bit]->data) {
|
||||
dev_err(codec->dev, "%s: no memory for %s cal data\n",
|
||||
__func__, cal_name_info[cal_bit]);
|
||||
goto exit;
|
||||
}
|
||||
set_bit(WCDCAL_INITIALISED,
|
||||
&fw_data->wcdcal_state[cal_bit]);
|
||||
}
|
||||
return 0;
|
||||
exit:
|
||||
for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
|
||||
kfree(fw[cal_bit]->data);
|
||||
fw[cal_bit]->data = NULL;
|
||||
}
|
||||
end:
|
||||
for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
|
||||
kfree(fw[cal_bit]);
|
||||
fw[cal_bit] = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(wcd_cal_create_hwdep);
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 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.
|
||||
*/
|
||||
#ifndef __WCD9XXX_HWDEP_H__
|
||||
#define __WCD9XXX_HWDEP_H__
|
||||
#include <sound/msmcal-hwdep.h>
|
||||
|
||||
enum wcd_cal_states {
|
||||
WCDCAL_UNINITIALISED,
|
||||
WCDCAL_INITIALISED,
|
||||
WCDCAL_RECIEVED
|
||||
};
|
||||
|
||||
struct fw_info {
|
||||
struct firmware_cal *fw[WCD9XXX_MAX_CAL];
|
||||
DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL);
|
||||
/* for calibration tracking */
|
||||
unsigned long wcdcal_state[WCD9XXX_MAX_CAL];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct firmware_cal {
|
||||
u8 *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct snd_soc_codec;
|
||||
int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec);
|
||||
struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
|
||||
enum wcd_cal_type type);
|
||||
#endif /* __WCD9XXX_HWDEP_H__ */
|
|
@ -151,6 +151,7 @@ config SND_SOC_MSM8960
|
|||
select SND_SOC_CS8427 if !MACH_APQ8064_MAKO
|
||||
select SND_SOC_DUAL_AMIC if MACH_APQ8064_MAKO
|
||||
select SND_SOC_TPA2028D if MACH_APQ8064_MAKO
|
||||
select SND_HWDEP
|
||||
default n
|
||||
help
|
||||
To add support for SoC audio on MSM8960 and APQ8064 boards
|
||||
|
|
Loading…
Reference in New Issue