audio-lnx: Initial change for techpack of audio drivers.

Add snapshot for audio drivers for SDM targets. The code is
migrated from msm-4.9 kernel at the below cutoff -

(74ff856e8d6: "net: ipc_router: Add dynamic enable/disable
wakeup source feature")

This changes are done for new techpack addition
for audio kernel. Migrate all audio kernel drivers
to this techpack.

Change-Id: I33d580af3ba86a5cb777583efc5d4cdaf2882d93
Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
This commit is contained in:
Asish Bhattacharya
2017-07-20 18:31:55 +05:30
committed by Martin Fick
orang tua 5ff0cfa736
melakukan 8e2277f79f
283 mengubah file dengan 250571 tambahan dan 0 penghapusan

23
Makefile Normal file
Melihat File

@@ -0,0 +1,23 @@
# auto-detect subdirs
ifeq ($(CONFIG_ARCH_SDM845), y)
include $(srctree)/techpack/audio/config/sdm845auto.conf
export
endif
# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE += \
-I$(srctree)/techpack/audio/include/uapi \
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE += \
-I$(srctree)/techpack/audio/include/uapi \
-I$(srctree)/techpack/audio/include
ifeq ($(CONFIG_ARCH_SDM845), y)
LINUXINCLUDE += \
-include $(srctree)/techpack/audio/config/sdm845autoconf.h
endif
obj-y += drivers/
obj-y += sound/

24
NOTICE Normal file
Melihat File

@@ -0,0 +1,24 @@
Copyright (c) 2009-2017, 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.
________________________________________
Copyright (C) 2008 Google, Inc.
Copyright (C) 2008 HTC Corporation
Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
This software is licensed under the terms of the GNU General Public
License version 2, as published by the Free Software Foundation, and
may be copied, distributed, and modified under those terms.
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.

7
drivers/Makefile Normal file
Melihat File

@@ -0,0 +1,7 @@
obj-y += mfd/
obj-y += misc/
obj-y += soc/
obj-y += soundwire/
obj-y += base/
obj-y += pinctrl/

2
drivers/base/Makefile Normal file
Melihat File

@@ -0,0 +1,2 @@
obj-y += regmap/

Melihat File

@@ -0,0 +1,2 @@
obj-$(CONFIG_REGMAP_SWR) += regmap-swr.o

Melihat File

@@ -0,0 +1 @@
../../../../../drivers/base/regmap/internal.h

Melihat File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2015-2017, 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/device.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/soundwire/soundwire.h>
#include <linux/module.h>
#include <linux/init.h>
#include "internal.h"
static int regmap_swr_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_len)
{
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
size_t addr_bytes;
size_t val_bytes;
int i, ret = 0;
u16 reg_addr = 0;
u8 *value;
if (map == NULL) {
dev_err(dev, "%s: regmap is NULL\n", __func__);
return -EINVAL;
}
addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr device is NULL\n", __func__);
return -EINVAL;
}
if (reg_size != addr_bytes) {
dev_err(dev, "%s: reg size %zd bytes not supported\n",
__func__, reg_size);
return -EINVAL;
}
reg_addr = *(u16 *)reg;
val_bytes = map->format.val_bytes;
/* val_len = val_bytes * val_count */
for (i = 0; i < (val_len / val_bytes); i++) {
value = (u8 *)val + (val_bytes * i);
ret = swr_write(swr, swr->dev_num, (reg_addr + i), value);
if (ret < 0) {
dev_err(dev, "%s: write reg 0x%x failed, err %d\n",
__func__, (reg_addr + i), ret);
break;
}
}
return ret;
}
static int regmap_swr_raw_multi_reg_write(void *context, const void *data,
size_t count)
{
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
size_t addr_bytes;
size_t val_bytes;
size_t pad_bytes;
size_t num_regs;
int i = 0;
int ret = 0;
u16 *reg;
u8 *val;
u8 *buf;
if (swr == NULL) {
dev_err(dev, "%s: swr device is NULL\n", __func__);
return -EINVAL;
}
if (map == NULL) {
dev_err(dev, "%s: regmap is NULL\n", __func__);
return -EINVAL;
}
addr_bytes = map->format.reg_bytes;
val_bytes = map->format.val_bytes;
pad_bytes = map->format.pad_bytes;
if (addr_bytes + val_bytes + pad_bytes == 0) {
dev_err(dev, "%s: sum of addr, value and pad is 0\n", __func__);
return -EINVAL;
}
num_regs = count / (addr_bytes + val_bytes + pad_bytes);
reg = kcalloc(num_regs, sizeof(u16), GFP_KERNEL);
if (!reg)
return -ENOMEM;
val = kcalloc(num_regs, sizeof(u8), GFP_KERNEL);
if (!val) {
ret = -ENOMEM;
goto mem_fail;
}
buf = (u8 *)data;
for (i = 0; i < num_regs; i++) {
reg[i] = *(u16 *)buf;
buf += (map->format.reg_bytes + map->format.pad_bytes);
val[i] = *buf;
buf += map->format.val_bytes;
}
ret = swr_bulk_write(swr, swr->dev_num, reg, val, num_regs);
if (ret)
dev_err(dev, "%s: multi reg write failed\n", __func__);
kfree(val);
mem_fail:
kfree(reg);
return ret;
}
static int regmap_swr_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct regmap *map = dev_get_regmap(dev, NULL);
size_t addr_bytes;
size_t val_bytes;
size_t pad_bytes;
if (map == NULL) {
dev_err(dev, "%s: regmap is NULL\n", __func__);
return -EINVAL;
}
addr_bytes = map->format.reg_bytes;
val_bytes = map->format.val_bytes;
pad_bytes = map->format.pad_bytes;
WARN_ON(count < addr_bytes);
if (count > (addr_bytes + val_bytes + pad_bytes))
return regmap_swr_raw_multi_reg_write(context, data, count);
else
return regmap_swr_gather_write(context, data, addr_bytes,
(data + addr_bytes),
(count - addr_bytes));
}
static int regmap_swr_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
size_t addr_bytes;
int ret = 0;
u16 reg_addr = 0;
if (map == NULL) {
dev_err(dev, "%s: regmap is NULL\n", __func__);
return -EINVAL;
}
addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr is NULL\n", __func__);
return -EINVAL;
}
if (reg_size != addr_bytes) {
dev_err(dev, "%s: register size %zd bytes not supported\n",
__func__, reg_size);
return -EINVAL;
}
reg_addr = *(u16 *)reg;
ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size);
if (ret < 0)
dev_err(dev, "%s: codec reg 0x%x read failed %d\n",
__func__, reg_addr, ret);
return ret;
}
static struct regmap_bus regmap_swr = {
.write = regmap_swr_write,
.gather_write = regmap_swr_gather_write,
.read = regmap_swr_read,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
struct regmap *__regmap_init_swr(struct swr_device *swr,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&swr->dev, &regmap_swr, &swr->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL(__regmap_init_swr);
struct regmap *__devm_regmap_init_swr(struct swr_device *swr,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __devm_regmap_init(&swr->dev, &regmap_swr, &swr->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL(__devm_regmap_init_swr);
MODULE_LICENSE("GPL v2");

8
drivers/mfd/Makefile Normal file
Melihat File

@@ -0,0 +1,8 @@
wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \
wcd9xxx-core.o wcd9xxx-irq.o \
wcd9xxx-slimslave.o wcd9xxx-utils.o \
wcd934x-regmap.o wcd934x-tables.o \
wcd9335-regmap.o wcd9335-tables.o \
msm-cdc-pinctrl.o msm-cdc-supply.o
obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o

Melihat File

@@ -0,0 +1,253 @@
/* Copyright (c) 2016-2017, 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/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/mfd/msm-cdc-pinctrl.h>
struct msm_cdc_pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_active;
struct pinctrl_state *pinctrl_sleep;
int gpio;
bool state;
};
static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata(
struct device_node *np)
{
struct platform_device *pdev;
struct msm_cdc_pinctrl_info *gpio_data;
if (!np) {
pr_err("%s: device node is null\n", __func__);
return NULL;
}
pdev = of_find_device_by_node(np);
if (!pdev) {
pr_err("%s: platform device not found!\n", __func__);
return NULL;
}
gpio_data = dev_get_drvdata(&pdev->dev);
if (!gpio_data)
dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n",
__func__);
return gpio_data;
}
/*
* msm_cdc_get_gpio_state: select pinctrl sleep state
* @np: pointer to struct device_node
*
* Returns error code for failure and GPIO value on success
*/
int msm_cdc_get_gpio_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
int value = -EINVAL;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return value;
if (gpio_is_valid(gpio_data->gpio))
value = gpio_get_value_cansleep(gpio_data->gpio);
return value;
}
EXPORT_SYMBOL(msm_cdc_get_gpio_state);
/*
* msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state
* @np: pointer to struct device_node
*
* Returns error code for failure
*/
int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
if (!gpio_data->pinctrl_sleep) {
pr_err("%s: pinctrl sleep state is null\n", __func__);
return -EINVAL;
}
gpio_data->state = false;
return pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_sleep);
}
EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state);
/*
* msm_cdc_pinctrl_select_active_state: select pinctrl active state
* @np: pointer to struct device_node
*
* Returns error code for failure
*/
int msm_cdc_pinctrl_select_active_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
if (!gpio_data->pinctrl_active) {
pr_err("%s: pinctrl active state is null\n", __func__);
return -EINVAL;
}
gpio_data->state = true;
return pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_active);
}
EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state);
/*
* msm_cdc_pinctrl_get_state: get curren pinctrl state
* @np: pointer to struct device_node
*
* Returns 0 for sleep state, 1 for active state
*/
bool msm_cdc_pinctrl_get_state(struct device_node *np)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
if (!gpio_data)
return -EINVAL;
return gpio_data->state;
}
EXPORT_SYMBOL(msm_cdc_pinctrl_get_state);
static int msm_cdc_pinctrl_probe(struct platform_device *pdev)
{
int ret = 0;
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = devm_kzalloc(&pdev->dev,
sizeof(struct msm_cdc_pinctrl_info),
GFP_KERNEL);
if (!gpio_data)
return -ENOMEM;
gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(gpio_data->pinctrl)) {
dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl));
ret = PTR_ERR(gpio_data->pinctrl);
goto err_pctrl_get;
}
gpio_data->pinctrl_active = pinctrl_lookup_state(
gpio_data->pinctrl, "aud_active");
if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) {
dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl_active));
ret = PTR_ERR(gpio_data->pinctrl_active);
goto err_lookup_state;
}
gpio_data->pinctrl_sleep = pinctrl_lookup_state(
gpio_data->pinctrl, "aud_sleep");
if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) {
dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n",
__func__, PTR_ERR(gpio_data->pinctrl_sleep));
ret = PTR_ERR(gpio_data->pinctrl_sleep);
goto err_lookup_state;
}
/* skip setting to sleep state for LPI_TLMM GPIOs */
if (!of_property_read_bool(pdev->dev.of_node, "qcom,lpi-gpios")) {
/* Set pinctrl state to aud_sleep by default */
ret = pinctrl_select_state(gpio_data->pinctrl,
gpio_data->pinctrl_sleep);
if (ret)
dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n",
__func__, ret);
}
gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,cdc-rst-n-gpio", 0);
if (gpio_is_valid(gpio_data->gpio)) {
ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET");
if (ret) {
dev_err(&pdev->dev, "%s: Failed to request gpio %d\n",
__func__, gpio_data->gpio);
goto err_lookup_state;
}
}
dev_set_drvdata(&pdev->dev, gpio_data);
return 0;
err_lookup_state:
devm_pinctrl_put(gpio_data->pinctrl);
err_pctrl_get:
devm_kfree(&pdev->dev, gpio_data);
return ret;
}
static int msm_cdc_pinctrl_remove(struct platform_device *pdev)
{
struct msm_cdc_pinctrl_info *gpio_data;
gpio_data = dev_get_drvdata(&pdev->dev);
if (gpio_data && gpio_data->pinctrl)
devm_pinctrl_put(gpio_data->pinctrl);
devm_kfree(&pdev->dev, gpio_data);
return 0;
}
static const struct of_device_id msm_cdc_pinctrl_match[] = {
{.compatible = "qcom,msm-cdc-pinctrl"},
{}
};
static struct platform_driver msm_cdc_pinctrl_driver = {
.driver = {
.name = "msm-cdc-pinctrl",
.owner = THIS_MODULE,
.of_match_table = msm_cdc_pinctrl_match,
},
.probe = msm_cdc_pinctrl_probe,
.remove = msm_cdc_pinctrl_remove,
};
int msm_cdc_pinctrl_drv_init(void)
{
return platform_driver_register(&msm_cdc_pinctrl_driver);
}
void msm_cdc_pinctrl_drv_exit(void)
{
platform_driver_unregister(&msm_cdc_pinctrl_driver);
}
MODULE_DESCRIPTION("MSM CODEC pin control platform driver");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2016-2017, 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/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/mfd/msm-cdc-supply.h>
#include <linux/regulator/consumer.h>
#define CODEC_DT_MAX_PROP_SIZE 40
static int msm_cdc_dt_parse_vreg_info(struct device *dev,
struct cdc_regulator *cdc_vreg,
const char *name, bool is_ond)
{
char prop_name[CODEC_DT_MAX_PROP_SIZE];
struct device_node *regulator_node = NULL;
const __be32 *prop;
int len, rc;
u32 prop_val;
/* Parse supply name */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name);
regulator_node = of_parse_phandle(dev->of_node, prop_name, 0);
if (!regulator_node) {
dev_err(dev, "%s: Looking up %s property in node %s failed",
__func__, prop_name, dev->of_node->full_name);
rc = -EINVAL;
goto done;
}
cdc_vreg->name = name;
cdc_vreg->ondemand = is_ond;
/* Parse supply - voltage */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name);
prop = of_get_property(dev->of_node, prop_name, &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
dev_err(dev, "%s: %s %s property\n", __func__,
prop ? "invalid format" : "no", prop_name);
rc = -EINVAL;
goto done;
} else {
cdc_vreg->min_uV = be32_to_cpup(&prop[0]);
cdc_vreg->max_uV = be32_to_cpup(&prop[1]);
}
/* Parse supply - current */
snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name);
rc = of_property_read_u32(dev->of_node, prop_name, &prop_val);
if (rc) {
dev_err(dev, "%s: Looking up %s property in node %s failed",
__func__, prop_name, dev->of_node->full_name);
goto done;
}
cdc_vreg->optimum_uA = prop_val;
dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n",
__func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV,
cdc_vreg->optimum_uA, cdc_vreg->ondemand);
done:
return rc;
}
static int msm_cdc_parse_supplies(struct device *dev,
struct cdc_regulator *cdc_reg,
const char *sup_list, int sup_cnt,
bool is_ond)
{
int idx, rc = 0;
const char *name = NULL;
for (idx = 0; idx < sup_cnt; idx++) {
rc = of_property_read_string_index(dev->of_node, sup_list, idx,
&name);
if (rc) {
dev_err(dev, "%s: read string %s[%d] error (%d)\n",
__func__, sup_list, idx, rc);
goto done;
}
dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n",
__func__, name, sup_list);
rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name,
is_ond);
if (rc) {
dev_err(dev, "%s: parse %s vreg info failed (%d)\n",
__func__, name, rc);
goto done;
}
}
done:
return rc;
}
static int msm_cdc_check_supply_param(struct device *dev,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
if (!dev) {
pr_err("%s: device is NULL\n", __func__);
return -ENODEV;
}
if (!cdc_vreg || (num_supplies <= 0)) {
dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n",
__func__, cdc_vreg, num_supplies);
return -EINVAL;
}
return 0;
}
/*
* msm_cdc_disable_static_supplies:
* Disable codec static supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply disable is failed
*/
int msm_cdc_disable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc, i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
for (i = 0; i < num_supplies; i++) {
if (cdc_vreg[i].ondemand)
continue;
rc = regulator_disable(supplies[i].consumer);
if (rc)
dev_err(dev, "%s: failed to disable supply %s, err:%d\n",
__func__, supplies[i].supply, rc);
else
dev_dbg(dev, "%s: disabled regulator %s\n",
__func__, supplies[i].supply);
}
return rc;
}
EXPORT_SYMBOL(msm_cdc_disable_static_supplies);
/*
* msm_cdc_release_supplies:
* Release codec power supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply disable is failed
*/
int msm_cdc_release_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc = 0;
int i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg,
num_supplies);
for (i = 0; i < num_supplies; i++) {
if (regulator_count_voltages(supplies[i].consumer) < 0)
continue;
regulator_set_voltage(supplies[i].consumer, 0,
cdc_vreg[i].max_uV);
regulator_set_load(supplies[i].consumer, 0);
devm_regulator_put(supplies[i].consumer);
supplies[i].consumer = NULL;
}
devm_kfree(dev, supplies);
return rc;
}
EXPORT_SYMBOL(msm_cdc_release_supplies);
/*
* msm_cdc_enable_static_supplies:
* Enable codec static supplies
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply enable is failed
*/
int msm_cdc_enable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
int rc, i;
if ((!dev) || (!supplies) || (!cdc_vreg)) {
pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
for (i = 0; i < num_supplies; i++) {
if (cdc_vreg[i].ondemand)
continue;
rc = regulator_enable(supplies[i].consumer);
if (rc) {
dev_err(dev, "%s: failed to enable supply %s, rc: %d\n",
__func__, supplies[i].supply, rc);
break;
}
}
while (rc && i--)
if (!cdc_vreg[i].ondemand)
regulator_disable(supplies[i].consumer);
return rc;
}
EXPORT_SYMBOL(msm_cdc_enable_static_supplies);
/*
* msm_cdc_init_supplies:
* Initialize codec static supplies with regulator get
*
* @dev: pointer to codec device
* @supplies: pointer to regulator bulk data
* @cdc_vreg: pointer to platform regulator data
* @num_supplies: number of supplies
*
* Return error code if supply init is failed
*/
int msm_cdc_init_supplies(struct device *dev,
struct regulator_bulk_data **supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies)
{
struct regulator_bulk_data *vsup;
int rc;
int i;
if (!dev || !cdc_vreg) {
pr_err("%s: device pointer or dce_vreg is NULL\n",
__func__);
return -EINVAL;
}
/* input parameter validation */
rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
if (rc)
return rc;
vsup = devm_kcalloc(dev, num_supplies,
sizeof(struct regulator_bulk_data),
GFP_KERNEL);
if (!vsup)
return -ENOMEM;
for (i = 0; i < num_supplies; i++) {
if (!cdc_vreg[i].name) {
dev_err(dev, "%s: supply name not defined\n",
__func__);
rc = -EINVAL;
goto err_supply;
}
vsup[i].supply = cdc_vreg[i].name;
}
rc = devm_regulator_bulk_get(dev, num_supplies, vsup);
if (rc) {
dev_err(dev, "%s: failed to get supplies (%d)\n",
__func__, rc);
goto err_supply;
}
/* Set voltage and current on regulators */
for (i = 0; i < num_supplies; i++) {
if (regulator_count_voltages(vsup[i].consumer) < 0)
continue;
rc = regulator_set_voltage(vsup[i].consumer,
cdc_vreg[i].min_uV,
cdc_vreg[i].max_uV);
if (rc) {
dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n",
__func__, vsup[i].supply, rc);
goto err_set_supply;
}
rc = regulator_set_load(vsup[i].consumer,
cdc_vreg[i].optimum_uA);
if (rc < 0) {
dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n",
__func__, vsup[i].supply, rc);
goto err_set_supply;
}
}
*supplies = vsup;
return 0;
err_set_supply:
for (i = 0; i < num_supplies; i++)
devm_regulator_put(vsup[i].consumer);
err_supply:
devm_kfree(dev, vsup);
return rc;
}
EXPORT_SYMBOL(msm_cdc_init_supplies);
/*
* msm_cdc_get_power_supplies:
* Get codec power supplies from device tree.
* Allocate memory to hold regulator data for
* all power supplies.
*
* @dev: pointer to codec device
* @cdc_vreg: pointer to codec regulator
* @total_num_supplies: total number of supplies read from DT
*
* Return error code if supply disable is failed
*/
int msm_cdc_get_power_supplies(struct device *dev,
struct cdc_regulator **cdc_vreg,
int *total_num_supplies)
{
const char *static_prop_name = "qcom,cdc-static-supplies";
const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
const char *cp_prop_name = "qcom,cdc-cp-supplies";
int static_sup_cnt = 0;
int ond_sup_cnt = 0;
int cp_sup_cnt = 0;
int num_supplies = 0;
struct cdc_regulator *cdc_reg;
int rc;
if (!dev) {
pr_err("%s: device pointer is NULL\n", __func__);
return -EINVAL;
}
static_sup_cnt = of_property_count_strings(dev->of_node,
static_prop_name);
if (static_sup_cnt < 0) {
dev_err(dev, "%s: Failed to get static supplies(%d)\n",
__func__, static_sup_cnt);
rc = static_sup_cnt;
goto err_supply_cnt;
}
ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
if (ond_sup_cnt < 0)
ond_sup_cnt = 0;
cp_sup_cnt = of_property_count_strings(dev->of_node,
cp_prop_name);
if (cp_sup_cnt < 0)
cp_sup_cnt = 0;
num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt;
if (num_supplies <= 0) {
dev_err(dev, "%s: supply count is 0 or negative\n", __func__);
rc = -EINVAL;
goto err_supply_cnt;
}
cdc_reg = devm_kcalloc(dev, num_supplies,
sizeof(struct cdc_regulator),
GFP_KERNEL);
if (!cdc_reg) {
rc = -ENOMEM;
goto err_mem_alloc;
}
rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name,
static_sup_cnt, false);
if (rc) {
dev_err(dev, "%s: failed to parse static supplies(%d)\n",
__func__, rc);
goto err_sup;
}
rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt],
ond_prop_name, ond_sup_cnt,
true);
if (rc) {
dev_err(dev, "%s: failed to parse demand supplies(%d)\n",
__func__, rc);
goto err_sup;
}
rc = msm_cdc_parse_supplies(dev,
&cdc_reg[static_sup_cnt + ond_sup_cnt],
cp_prop_name, cp_sup_cnt, true);
if (rc) {
dev_err(dev, "%s: failed to parse cp supplies(%d)\n",
__func__, rc);
goto err_sup;
}
*cdc_vreg = cdc_reg;
*total_num_supplies = num_supplies;
return 0;
err_sup:
devm_kfree(dev, cdc_reg);
err_supply_cnt:
err_mem_alloc:
return rc;
}
EXPORT_SYMBOL(msm_cdc_get_power_supplies);

1611
drivers/mfd/wcd9335-regmap.c Normal file

File diff ditekan karena terlalu besar Load Diff

1325
drivers/mfd/wcd9335-tables.c Normal file

File diff ditekan karena terlalu besar Load Diff

1957
drivers/mfd/wcd934x-regmap.c Normal file

File diff ditekan karena terlalu besar Load Diff

2155
drivers/mfd/wcd934x-tables.c Normal file

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,55 @@
/* Copyright (c) 2017, 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/module.h>
#include <linux/mfd/msm-cdc-pinctrl.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
#include <linux/mfd/wcd9xxx/core.h>
#define NUM_DRIVERS_REG_RET 3
static int __init wcd9xxx_core_init(void)
{
int ret[NUM_DRIVERS_REG_RET] = {0};
int i = 0;
ret[0] = msm_cdc_pinctrl_drv_init();
if (ret[0])
pr_err("%s: Failed init pinctrl drv: %d\n", __func__, ret[0]);
ret[1] = wcd9xxx_irq_drv_init();
if (ret[1])
pr_err("%s: Failed init irq drv: %d\n", __func__, ret[1]);
ret[2] = wcd9xxx_init();
if (ret[2])
pr_err("%s: Failed wcd core drv: %d\n", __func__, ret[2]);
for (i = 0; i < NUM_DRIVERS_REG_RET; i++) {
if (ret[i])
return ret[i];
}
return 0;
}
module_init(wcd9xxx_core_init);
static void __exit wcd9xxx_core_exit(void)
{
wcd9xxx_exit();
wcd9xxx_irq_drv_exit();
msm_cdc_pinctrl_drv_exit();
}
module_exit(wcd9xxx_core_exit);
MODULE_DESCRIPTION("WCD9XXX CODEC core init driver");
MODULE_LICENSE("GPL v2");

1714
drivers/mfd/wcd9xxx-core.c Normal file

File diff ditekan karena terlalu besar Load Diff

853
drivers/mfd/wcd9xxx-irq.c Normal file
Melihat File

@@ -0,0 +1,853 @@
/* Copyright (c) 2011-2017, 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/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/regmap.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
#include <linux/delay.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <soc/qcom/pm.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
#ifndef NO_IRQ
#define NO_IRQ (-1)
#endif
#ifdef CONFIG_OF
struct wcd9xxx_irq_drv_data {
struct irq_domain *domain;
int irq;
};
#endif
static int virq_to_phyirq(
struct wcd9xxx_core_resource *wcd9xxx_res, int virq);
static int phyirq_to_virq(
struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
static unsigned int wcd9xxx_irq_get_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res);
static void wcd9xxx_irq_put_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res);
static int wcd9xxx_map_irq(
struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
static void wcd9xxx_irq_lock(struct irq_data *data)
{
struct wcd9xxx_core_resource *wcd9xxx_res =
irq_data_get_irq_chip_data(data);
mutex_lock(&wcd9xxx_res->irq_lock);
}
static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
{
struct wcd9xxx_core_resource *wcd9xxx_res =
irq_data_get_irq_chip_data(data);
int i;
if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) >
WCD9XXX_MAX_IRQ_REGS) ||
(ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) >
WCD9XXX_MAX_IRQ_REGS)) {
pr_err("%s: Array Size out of bound\n", __func__);
return;
}
if (!wcd9xxx_res->wcd_core_regmap) {
pr_err("%s: Codec core regmap not defined\n",
__func__);
return;
}
for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware.
*/
if (wcd9xxx_res->irq_masks_cur[i] !=
wcd9xxx_res->irq_masks_cache[i]) {
wcd9xxx_res->irq_masks_cache[i] =
wcd9xxx_res->irq_masks_cur[i];
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i,
wcd9xxx_res->irq_masks_cur[i]);
}
}
mutex_unlock(&wcd9xxx_res->irq_lock);
}
static void wcd9xxx_irq_enable(struct irq_data *data)
{
struct wcd9xxx_core_resource *wcd9xxx_res =
irq_data_get_irq_chip_data(data);
int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
int byte = BIT_BYTE(wcd9xxx_irq);
int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur);
if ((byte < size) && (byte >= 0)) {
wcd9xxx_res->irq_masks_cur[byte] &=
~(BYTE_BIT_MASK(wcd9xxx_irq));
} else {
pr_err("%s: Array size is %d but index is %d: Out of range\n",
__func__, size, byte);
}
}
static void wcd9xxx_irq_disable(struct irq_data *data)
{
struct wcd9xxx_core_resource *wcd9xxx_res =
irq_data_get_irq_chip_data(data);
int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
int byte = BIT_BYTE(wcd9xxx_irq);
int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur);
if ((byte < size) && (byte >= 0)) {
wcd9xxx_res->irq_masks_cur[byte]
|= BYTE_BIT_MASK(wcd9xxx_irq);
} else {
pr_err("%s: Array size is %d but index is %d: Out of range\n",
__func__, size, byte);
}
}
static void wcd9xxx_irq_ack(struct irq_data *data)
{
int wcd9xxx_irq = 0;
struct wcd9xxx_core_resource *wcd9xxx_res =
irq_data_get_irq_chip_data(data);
if (wcd9xxx_res == NULL) {
pr_err("%s: wcd9xxx_res is NULL\n", __func__);
return;
}
wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n",
__func__, wcd9xxx_irq);
}
static void wcd9xxx_irq_mask(struct irq_data *d)
{
/* do nothing but required as linux calls irq_mask without NULL check */
}
static struct irq_chip wcd9xxx_irq_chip = {
.name = "wcd9xxx",
.irq_bus_lock = wcd9xxx_irq_lock,
.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
.irq_disable = wcd9xxx_irq_disable,
.irq_enable = wcd9xxx_irq_enable,
.irq_mask = wcd9xxx_irq_mask,
.irq_ack = wcd9xxx_irq_ack,
};
bool wcd9xxx_lock_sleep(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
enum wcd9xxx_pm_state os;
/*
* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
* and its subroutines only motly.
* but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
* It can race with wcd9xxx_irq_thread.
* So need to embrace wlock_holders with mutex.
*
* If system didn't resume, we can simply return false so codec driver's
* IRQ handler can return without handling IRQ.
* As interrupt line is still active, codec will have another IRQ to
* retry shortly.
*/
mutex_lock(&wcd9xxx_res->pm_lock);
if (wcd9xxx_res->wlock_holders++ == 0) {
pr_debug("%s: holding wake lock\n", __func__);
pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
msm_cpuidle_get_deep_idle_latency());
pm_stay_awake(wcd9xxx_res->dev);
}
mutex_unlock(&wcd9xxx_res->pm_lock);
if (!wait_event_timeout(wcd9xxx_res->pm_wq,
((os = wcd9xxx_pm_cmpxchg(wcd9xxx_res,
WCD9XXX_PM_SLEEPABLE,
WCD9XXX_PM_AWAKE)) ==
WCD9XXX_PM_SLEEPABLE ||
(os == WCD9XXX_PM_AWAKE)),
msecs_to_jiffies(
WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
__func__,
WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state,
wcd9xxx_res->wlock_holders);
wcd9xxx_unlock_sleep(wcd9xxx_res);
return false;
}
wake_up_all(&wcd9xxx_res->pm_wq);
return true;
}
EXPORT_SYMBOL(wcd9xxx_lock_sleep);
void wcd9xxx_unlock_sleep(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
mutex_lock(&wcd9xxx_res->pm_lock);
if (--wcd9xxx_res->wlock_holders == 0) {
pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
__func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE);
/*
* if wcd9xxx_lock_sleep failed, pm_state would be still
* WCD9XXX_PM_ASLEEP, don't overwrite
*/
if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE))
wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE;
pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
PM_QOS_DEFAULT_VALUE);
pm_relax(wcd9xxx_res->dev);
}
mutex_unlock(&wcd9xxx_res->pm_lock);
wake_up_all(&wcd9xxx_res->pm_wq);
}
EXPORT_SYMBOL(wcd9xxx_unlock_sleep);
void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res)
{
mutex_lock(&wcd9xxx_res->nested_irq_lock);
}
void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res)
{
mutex_unlock(&wcd9xxx_res->nested_irq_lock);
}
static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res,
struct intr_data *irqdata)
{
int irqbit = irqdata->intr_num;
if (!wcd9xxx_res->wcd_core_regmap) {
pr_err("%s: codec core regmap not defined\n",
__func__);
return;
}
if (irqdata->clear_first) {
wcd9xxx_nested_irq_lock(wcd9xxx_res);
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] +
BIT_BYTE(irqbit),
BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
0x02);
handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
wcd9xxx_nested_irq_unlock(wcd9xxx_res);
} else {
wcd9xxx_nested_irq_lock(wcd9xxx_res);
handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] +
BIT_BYTE(irqbit),
BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
0x02);
wcd9xxx_nested_irq_unlock(wcd9xxx_res);
}
}
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
{
int ret;
int i;
struct intr_data irqdata;
char linebuf[128];
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1);
struct wcd9xxx_core_resource *wcd9xxx_res = data;
int num_irq_regs = wcd9xxx_res->num_irq_regs;
u8 status[4], status1[4] = {0}, unmask_status[4] = {0};
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) {
dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n");
return IRQ_NONE;
}
if (!wcd9xxx_res->wcd_core_regmap) {
dev_err(wcd9xxx_res->dev,
"%s: Codec core regmap not supplied\n",
__func__);
goto err_disable_irq;
}
memset(status, 0, sizeof(status));
ret = regmap_bulk_read(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_STATUS_BASE],
status, num_irq_regs);
if (ret < 0) {
dev_err(wcd9xxx_res->dev,
"Failed to read interrupt status: %d\n", ret);
goto err_disable_irq;
}
/*
* If status is 0 return without clearing.
* status contains: HW status - masked interrupts
* status1 contains: unhandled interrupts - masked interrupts
* unmasked_status contains: unhandled interrupts
*/
if (unlikely(!memcmp(status, status1, sizeof(status)))) {
pr_debug("%s: status is 0\n", __func__);
wcd9xxx_unlock_sleep(wcd9xxx_res);
return IRQ_HANDLED;
}
/*
* Copy status to unmask_status before masking, otherwise SW may miss
* to clear masked interrupt in corner case.
*/
memcpy(unmask_status, status, sizeof(unmask_status));
/* Apply masking */
for (i = 0; i < num_irq_regs; i++)
status[i] &= ~wcd9xxx_res->irq_masks_cur[i];
memcpy(status1, status, sizeof(status1));
/* Find out which interrupt was triggered and call that interrupt's
* handler function
*
* Since codec has only one hardware irq line which is shared by
* codec's different internal interrupts, so it's possible master irq
* handler dispatches multiple nested irq handlers after breaking
* order. Dispatch interrupts in the order that is maintained by
* the interrupt table.
*/
for (i = 0; i < wcd9xxx_res->intr_table_size; i++) {
irqdata = wcd9xxx_res->intr_table[i];
if (status[BIT_BYTE(irqdata.intr_num)] &
BYTE_BIT_MASK(irqdata.intr_num)) {
wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata);
status1[BIT_BYTE(irqdata.intr_num)] &=
~BYTE_BIT_MASK(irqdata.intr_num);
unmask_status[BIT_BYTE(irqdata.intr_num)] &=
~BYTE_BIT_MASK(irqdata.intr_num);
}
}
/*
* As a failsafe if unhandled irq is found, clear it to prevent
* interrupt storm.
* Note that we can say there was an unhandled irq only when no irq
* handled by nested irq handler since Taiko supports qdsp as irqs'
* destination for few irqs. Therefore driver shouldn't clear pending
* irqs when few handled while few others not.
*/
if (unlikely(!memcmp(status, status1, sizeof(status)))) {
if (__ratelimit(&ratelimit)) {
pr_warn("%s: Unhandled irq found\n", __func__);
hex_dump_to_buffer(status, sizeof(status), 16, 1,
linebuf, sizeof(linebuf), false);
pr_warn("%s: status0 : %s\n", __func__, linebuf);
hex_dump_to_buffer(status1, sizeof(status1), 16, 1,
linebuf, sizeof(linebuf), false);
pr_warn("%s: status1 : %s\n", __func__, linebuf);
}
/*
* unmask_status contains unhandled interrupts, hence clear all
* unhandled interrupts.
*/
ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE],
unmask_status, num_irq_regs);
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
0x02);
}
wcd9xxx_unlock_sleep(wcd9xxx_res);
return IRQ_HANDLED;
err_disable_irq:
dev_err(wcd9xxx_res->dev,
"Disable irq %d\n", wcd9xxx_res->irq);
disable_irq_wake(wcd9xxx_res->irq);
disable_irq_nosync(wcd9xxx_res->irq);
wcd9xxx_unlock_sleep(wcd9xxx_res);
return IRQ_NONE;
}
/**
* wcd9xxx_free_irq
*
* @wcd9xxx_res: pointer to core resource
* irq: irq number
* @data: data pointer
*
*/
void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
int irq, void *data)
{
free_irq(phyirq_to_virq(wcd9xxx_res, irq), data);
}
EXPORT_SYMBOL(wcd9xxx_free_irq);
/**
* wcd9xxx_enable_irq
*
* @wcd9xxx_res: pointer to core resource
* irq: irq number
*
*/
void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
{
if (wcd9xxx_res->irq)
enable_irq(phyirq_to_virq(wcd9xxx_res, irq));
}
EXPORT_SYMBOL(wcd9xxx_enable_irq);
/**
* wcd9xxx_disable_irq
*
* @wcd9xxx_res: pointer to core resource
* irq: irq number
*
*/
void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
{
if (wcd9xxx_res->irq)
disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq));
}
EXPORT_SYMBOL(wcd9xxx_disable_irq);
/**
* wcd9xxx_disable_irq_sync
*
* @wcd9xxx_res: pointer to core resource
* irq: irq number
*
*/
void wcd9xxx_disable_irq_sync(
struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
{
if (wcd9xxx_res->irq)
disable_irq(phyirq_to_virq(wcd9xxx_res, irq));
}
EXPORT_SYMBOL(wcd9xxx_disable_irq_sync);
static int wcd9xxx_irq_setup_downstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
int irq, virq, ret;
pr_debug("%s: enter\n", __func__);
for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) {
/* Map OF irq */
virq = wcd9xxx_map_irq(wcd9xxx_res, irq);
pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
if (virq == NO_IRQ) {
pr_err("%s, No interrupt specifier for irq %d\n",
__func__, irq);
return NO_IRQ;
}
ret = irq_set_chip_data(virq, wcd9xxx_res);
if (ret) {
pr_err("%s: Failed to configure irq %d (%d)\n",
__func__, irq, ret);
return ret;
}
if (wcd9xxx_res->irq_level_high[irq])
irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
handle_level_irq);
else
irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
handle_edge_irq);
irq_set_nested_thread(virq, 1);
}
pr_debug("%s: leave\n", __func__);
return 0;
}
/**
* wcd9xxx_irq_init
*
* @wcd9xxx_res: pointer to core resource
*
* Returns 0 on success, appropriate error code otherwise
*/
int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res)
{
int i, ret;
u8 irq_level[wcd9xxx_res->num_irq_regs];
struct irq_domain *domain;
struct device_node *pnode;
mutex_init(&wcd9xxx_res->irq_lock);
mutex_init(&wcd9xxx_res->nested_irq_lock);
pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node);
if (unlikely(!pnode))
return -EINVAL;
domain = irq_find_host(pnode);
if (unlikely(!domain))
return -EINVAL;
wcd9xxx_res->domain = domain;
wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res);
if (!wcd9xxx_res->irq) {
pr_warn("%s: irq driver is not yet initialized\n", __func__);
mutex_destroy(&wcd9xxx_res->irq_lock);
mutex_destroy(&wcd9xxx_res->nested_irq_lock);
return -EPROBE_DEFER;
}
pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq);
/* Setup downstream IRQs */
ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res);
if (ret) {
pr_err("%s: Failed to setup downstream IRQ\n", __func__);
wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
mutex_destroy(&wcd9xxx_res->irq_lock);
mutex_destroy(&wcd9xxx_res->nested_irq_lock);
return ret;
}
/* All other wcd9xxx interrupts are edge triggered */
wcd9xxx_res->irq_level_high[0] = true;
/* mask all the interrupts */
memset(irq_level, 0, wcd9xxx_res->num_irq_regs);
for (i = 0; i < wcd9xxx_res->num_irqs; i++) {
wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
irq_level[BIT_BYTE(i)] |=
wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE);
}
if (!wcd9xxx_res->wcd_core_regmap) {
dev_err(wcd9xxx_res->dev,
"%s: Codec core regmap not defined\n",
__func__);
ret = -EINVAL;
goto fail_irq_init;
}
for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) {
/* Initialize interrupt mask and level registers */
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_LEVEL_BASE] + i,
irq_level[i]);
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i,
wcd9xxx_res->irq_masks_cur[i]);
}
ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"wcd9xxx", wcd9xxx_res);
if (ret != 0)
dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n",
wcd9xxx_res->irq, ret);
else {
ret = enable_irq_wake(wcd9xxx_res->irq);
if (ret)
dev_err(wcd9xxx_res->dev,
"Failed to set wake interrupt on IRQ %d: %d\n",
wcd9xxx_res->irq, ret);
if (ret)
free_irq(wcd9xxx_res->irq, wcd9xxx_res);
}
if (ret)
goto fail_irq_init;
return ret;
fail_irq_init:
dev_err(wcd9xxx_res->dev,
"%s: Failed to init wcd9xxx irq\n", __func__);
wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
mutex_destroy(&wcd9xxx_res->irq_lock);
mutex_destroy(&wcd9xxx_res->nested_irq_lock);
return ret;
}
EXPORT_SYMBOL(wcd9xxx_irq_init);
int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
int irq, irq_handler_t handler,
const char *name, void *data)
{
int virq;
virq = phyirq_to_virq(wcd9xxx_res, irq);
return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
name, data);
}
EXPORT_SYMBOL(wcd9xxx_request_irq);
void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res)
{
dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__,
wcd9xxx_res->irq);
if (wcd9xxx_res->irq) {
disable_irq_wake(wcd9xxx_res->irq);
free_irq(wcd9xxx_res->irq, wcd9xxx_res);
wcd9xxx_res->irq = 0;
wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
}
mutex_destroy(&wcd9xxx_res->irq_lock);
mutex_destroy(&wcd9xxx_res->nested_irq_lock);
}
#ifndef CONFIG_OF
static int phyirq_to_virq(
struct wcd9xxx_core_resource *wcd9xxx_res,
int offset)
{
return wcd9xxx_res->irq_base + offset;
}
static int virq_to_phyirq(
struct wcd9xxx_core_resource *wcd9xxx_res,
int virq)
{
return virq - wcd9xxx_res->irq_base;
}
static unsigned int wcd9xxx_irq_get_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
return wcd9xxx_res->irq;
}
static void wcd9xxx_irq_put_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
/* Do nothing */
}
static int wcd9xxx_map_irq(
struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq)
{
return phyirq_to_virq(wcd9xxx_core_res, irq);
}
#else
static struct wcd9xxx_irq_drv_data *
wcd9xxx_irq_add_domain(struct device_node *node,
struct device_node *parent)
{
struct wcd9xxx_irq_drv_data *data = NULL;
pr_debug("%s: node %s, node parent %s\n", __func__,
node->name, node->parent->name);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
/*
* wcd9xxx_intc interrupt controller supports N to N irq mapping with
* single cell binding with irq numbers(offsets) only.
* Use irq_domain_simple_ops that has irq_domain_simple_map and
* irq_domain_xlate_onetwocell.
*/
data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
&irq_domain_simple_ops, data);
if (!data->domain) {
kfree(data);
return NULL;
}
return data;
}
static struct wcd9xxx_irq_drv_data *
wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res)
{
struct irq_domain *domain;
domain = wcd9xxx_res->domain;
if (domain)
return domain->host_data;
else
return NULL;
}
static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset)
{
struct wcd9xxx_irq_drv_data *data;
data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
if (!data) {
pr_warn("%s: not registered to interrupt controller\n",
__func__);
return -EINVAL;
}
return irq_linear_revmap(data->domain, offset);
}
static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
if (unlikely(!irq_data)) {
pr_err("%s: irq_data is NULL", __func__);
return -EINVAL;
}
return irq_data->hwirq;
}
static unsigned int wcd9xxx_irq_get_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
struct wcd9xxx_irq_drv_data *data;
data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
if (!data) {
pr_err("%s: interrupt controller is not registered\n",
__func__);
return 0;
}
/* Make sure data is updated before return. */
rmb();
return data->irq;
}
static void wcd9xxx_irq_put_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
wcd9xxx_res->domain = NULL;
}
static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
{
return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL);
}
static int wcd9xxx_irq_probe(struct platform_device *pdev)
{
int irq, dir_apps_irq = -EINVAL;
struct wcd9xxx_irq_drv_data *data;
struct device_node *node = pdev->dev.of_node;
int ret = -EINVAL;
irq = of_get_named_gpio(node, "qcom,gpio-connect", 0);
if (!gpio_is_valid(irq))
dir_apps_irq = platform_get_irq_byname(pdev, "wcd_irq");
if (!gpio_is_valid(irq) && dir_apps_irq < 0) {
dev_err(&pdev->dev, "TLMM connect gpio not found\n");
return -EPROBE_DEFER;
}
if (dir_apps_irq > 0) {
irq = dir_apps_irq;
} else {
irq = gpio_to_irq(irq);
if (irq < 0) {
dev_err(&pdev->dev, "Unable to configure irq\n");
return irq;
}
}
dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
data = wcd9xxx_irq_add_domain(node, node->parent);
if (!data) {
pr_err("%s: irq_add_domain failed\n", __func__);
return -EINVAL;
}
data->irq = irq;
/* Make sure irq is saved before return. */
wmb();
ret = 0;
return ret;
}
static int wcd9xxx_irq_remove(struct platform_device *pdev)
{
struct irq_domain *domain;
struct wcd9xxx_irq_drv_data *data;
domain = irq_find_host(pdev->dev.of_node);
if (unlikely(!domain)) {
pr_err("%s: domain is NULL", __func__);
return -EINVAL;
}
data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
data->irq = 0;
/* Make sure irq variable is updated in data, before irq removal. */
wmb();
irq_domain_remove(data->domain);
kfree(data);
domain->host_data = NULL;
return 0;
}
static const struct of_device_id of_match[] = {
{ .compatible = "qcom,wcd9xxx-irq" },
{ }
};
static struct platform_driver wcd9xxx_irq_driver = {
.probe = wcd9xxx_irq_probe,
.remove = wcd9xxx_irq_remove,
.driver = {
.name = "wcd9xxx_intc",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_match),
},
};
int wcd9xxx_irq_drv_init(void)
{
return platform_driver_register(&wcd9xxx_irq_driver);
}
void wcd9xxx_irq_drv_exit(void)
{
platform_driver_unregister(&wcd9xxx_irq_driver);
}
#endif /* CONFIG_OF */

Melihat File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2015-2017, 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_REGMAP_
#define _WCD9XXX_REGMAP_
#include <linux/regmap.h>
#include <linux/mfd/wcd9xxx/core.h>
typedef int (*regmap_patch_fptr)(struct regmap *, int);
extern struct regmap_config wcd934x_regmap_config;
extern int wcd934x_regmap_register_patch(struct regmap *regmap,
int version);
extern struct regmap_config wcd9335_regmap_config;
extern int wcd9335_regmap_register_patch(struct regmap *regmap,
int version);
static inline struct regmap_config *wcd9xxx_get_regmap_config(int type)
{
struct regmap_config *regmap_config;
switch (type) {
case WCD934X:
regmap_config = &wcd934x_regmap_config;
break;
case WCD9335:
regmap_config = &wcd9335_regmap_config;
break;
default:
regmap_config = NULL;
break;
};
return regmap_config;
}
static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type)
{
regmap_patch_fptr apply_patch;
switch (type) {
case WCD9335:
apply_patch = wcd9335_regmap_register_patch;
break;
case WCD934X:
apply_patch = wcd934x_regmap_register_patch;
break;
default:
apply_patch = NULL;
break;
}
return apply_patch;
}
#endif

443
drivers/mfd/wcd9xxx-rst.c Normal file
Melihat File

@@ -0,0 +1,443 @@
/* Copyright (c) 2017, 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/kernel.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include <linux/mfd/wcd9xxx/core.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-utils.h>
#include <linux/mfd/wcd9335/registers.h>
#include <linux/mfd/wcd934x/registers.h>
#include <linux/mfd/wcd9335/irq.h>
#include <linux/mfd/wcd934x/irq.h>
/* wcd9335 interrupt table */
static const struct intr_data wcd9335_intr_table[] = {
{WCD9XXX_IRQ_SLIMBUS, false},
{WCD9335_IRQ_MBHC_SW_DET, true},
{WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true},
{WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true},
{WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true},
{WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
{WCD9335_IRQ_FLL_LOCK_LOSS, false},
{WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false},
{WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false},
{WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false},
{WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false},
{WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false},
{WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false},
{WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false},
{WCD9335_IRQ_HPH_PA_OCPL_FAULT, false},
{WCD9335_IRQ_HPH_PA_OCPR_FAULT, false},
{WCD9335_IRQ_EAR_PA_OCP_FAULT, false},
{WCD9335_IRQ_SOUNDWIRE, false},
{WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false},
{WCD9335_IRQ_RCO_ERROR, false},
{WCD9335_IRQ_SVA_ERROR, false},
{WCD9335_IRQ_MAD_AUDIO, false},
{WCD9335_IRQ_MAD_BEACON, false},
{WCD9335_IRQ_SVA_OUTBOX1, true},
{WCD9335_IRQ_SVA_OUTBOX2, true},
{WCD9335_IRQ_MAD_ULTRASOUND, false},
{WCD9335_IRQ_VBAT_ATTACK, false},
{WCD9335_IRQ_VBAT_RESTORE, false},
};
static const struct intr_data wcd934x_intr_table[] = {
{WCD9XXX_IRQ_SLIMBUS, false},
{WCD934X_IRQ_MBHC_SW_DET, true},
{WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true},
{WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true},
{WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true},
{WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
{WCD934X_IRQ_MISC, false},
{WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false},
{WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false},
{WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false},
{WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false},
{WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false},
{WCD934X_IRQ_SLNQ_ANALOG_ERROR, false},
{WCD934X_IRQ_RESERVED_3, false},
{WCD934X_IRQ_HPH_PA_OCPL_FAULT, false},
{WCD934X_IRQ_HPH_PA_OCPR_FAULT, false},
{WCD934X_IRQ_EAR_PA_OCP_FAULT, false},
{WCD934X_IRQ_SOUNDWIRE, false},
{WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false},
{WCD934X_IRQ_RCO_ERROR, false},
{WCD934X_IRQ_CPE_ERROR, false},
{WCD934X_IRQ_MAD_AUDIO, false},
{WCD934X_IRQ_MAD_BEACON, false},
{WCD934X_IRQ_CPE1_INTR, true},
{WCD934X_IRQ_RESERVED_4, false},
{WCD934X_IRQ_MAD_ULTRASOUND, false},
{WCD934X_IRQ_VBAT_ATTACK, false},
{WCD934X_IRQ_VBAT_RESTORE, false},
};
/*
* wcd9335_bring_down: Bringdown WCD Codec
*
* @wcd9xxx: Pointer to wcd9xxx structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd9335_bring_down(struct wcd9xxx *wcd9xxx)
{
if (!wcd9xxx || !wcd9xxx->regmap)
return -EINVAL;
regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x04);
return 0;
}
/*
* wcd9335_bring_up: Bringup WCD Codec
*
* @wcd9xxx: Pointer to the wcd9xxx structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd9335_bring_up(struct wcd9xxx *wcd9xxx)
{
int ret = 0;
int val, byte0;
struct regmap *wcd_regmap;
if (!wcd9xxx)
return -EINVAL;
if (!wcd9xxx->regmap) {
dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
__func__);
return -EINVAL;
}
wcd_regmap = wcd9xxx->regmap;
regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val);
regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0);
if ((val < 0) || (byte0 < 0)) {
dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n",
__func__);
return -EINVAL;
}
if ((val & 0x80) && (byte0 == 0x0)) {
dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n",
__func__);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x5);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x7);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x3);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
} else if (byte0 == 0x1) {
dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n",
__func__);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F);
regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x5);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x7);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x3);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
} else if ((byte0 == 0) && (!(val & 0x80))) {
dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n",
__func__);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x3);
regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
} else {
dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n",
__func__);
ret = -EINVAL;
}
return ret;
}
/*
* wcd9335_get_cdc_info: Get codec specific information
*
* @wcd9xxx: pointer to wcd9xxx structure
* @wcd_type: pointer to wcd9xxx_codec_type structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd9335_get_cdc_info(struct wcd9xxx *wcd9xxx,
struct wcd9xxx_codec_type *wcd_type)
{
u16 id_minor, id_major;
struct regmap *wcd_regmap;
int rc, val, version = 0;
if (!wcd9xxx || !wcd_type)
return -EINVAL;
if (!wcd9xxx->regmap) {
dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
__func__);
return -EINVAL;
}
wcd_regmap = wcd9xxx->regmap;
rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
(u8 *)&id_minor, sizeof(u16));
if (rc)
return -EINVAL;
rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
(u8 *)&id_major, sizeof(u16));
if (rc)
return -EINVAL;
dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
__func__, id_major, id_minor);
/* Version detection */
if (id_major == TASHA_MAJOR) {
regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0,
&val);
version = ((u8)val & 0x80) >> 7;
} else if (id_major == TASHA2P0_MAJOR)
version = 2;
else
dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n",
__func__, id_major, id_minor);
/* Fill codec type info */
wcd_type->id_major = id_major;
wcd_type->id_minor = id_minor;
wcd_type->num_irqs = WCD9335_NUM_IRQS;
wcd_type->version = version;
wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
wcd_type->i2c_chip_status = 0x01;
wcd_type->intr_tbl = wcd9335_intr_table;
wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table);
wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
WCD9335_INTR_PIN1_STATUS0;
wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
WCD9335_INTR_PIN1_CLEAR0;
wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
WCD9335_INTR_PIN1_MASK0;
wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
WCD9335_INTR_LEVEL0;
wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
WCD9335_INTR_CLR_COMMIT;
return rc;
}
/*
* wcd934x_bring_down: Bringdown WCD Codec
*
* @wcd9xxx: Pointer to wcd9xxx structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd934x_bring_down(struct wcd9xxx *wcd9xxx)
{
if (!wcd9xxx || !wcd9xxx->regmap)
return -EINVAL;
regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
0x04);
return 0;
}
/*
* wcd934x_bring_up: Bringup WCD Codec
*
* @wcd9xxx: Pointer to the wcd9xxx structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd934x_bring_up(struct wcd9xxx *wcd9xxx)
{
struct regmap *wcd_regmap;
if (!wcd9xxx)
return -EINVAL;
if (!wcd9xxx->regmap) {
dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
__func__);
return -EINVAL;
}
wcd_regmap = wcd9xxx->regmap;
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);
regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);
regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);
/* Add 1msec delay for VOUT to settle */
usleep_range(1000, 1100);
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);
regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
return 0;
}
/*
* wcd934x_get_cdc_info: Get codec specific information
*
* @wcd9xxx: pointer to wcd9xxx structure
* @wcd_type: pointer to wcd9xxx_codec_type structure
*
* Returns 0 for success or negative error code for failure
*/
static int wcd934x_get_cdc_info(struct wcd9xxx *wcd9xxx,
struct wcd9xxx_codec_type *wcd_type)
{
u16 id_minor, id_major;
struct regmap *wcd_regmap;
int rc, version = -1;
if (!wcd9xxx || !wcd_type)
return -EINVAL;
if (!wcd9xxx->regmap) {
dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__);
return -EINVAL;
}
wcd_regmap = wcd9xxx->regmap;
rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
(u8 *)&id_minor, sizeof(u16));
if (rc)
return -EINVAL;
rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
(u8 *)&id_major, sizeof(u16));
if (rc)
return -EINVAL;
dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
__func__, id_major, id_minor);
if (id_major != TAVIL_MAJOR)
goto version_unknown;
/*
* As fine version info cannot be retrieved before tavil probe.
* Assign coarse versions for possible future use before tavil probe.
*/
if (id_minor == cpu_to_le16(0))
version = TAVIL_VERSION_1_0;
else if (id_minor == cpu_to_le16(0x01))
version = TAVIL_VERSION_1_1;
version_unknown:
if (version < 0)
dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n",
__func__);
/* Fill codec type info */
wcd_type->id_major = id_major;
wcd_type->id_minor = id_minor;
wcd_type->num_irqs = WCD934X_NUM_IRQS;
wcd_type->version = version;
wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
wcd_type->i2c_chip_status = 0x01;
wcd_type->intr_tbl = wcd934x_intr_table;
wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table);
wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
WCD934X_INTR_PIN1_STATUS0;
wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
WCD934X_INTR_PIN1_CLEAR0;
wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
WCD934X_INTR_PIN1_MASK0;
wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
WCD934X_INTR_LEVEL0;
wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
WCD934X_INTR_CLR_COMMIT;
return rc;
}
codec_bringdown_fn wcd9xxx_bringdown_fn(int type)
{
codec_bringdown_fn cdc_bdown_fn;
switch (type) {
case WCD934X:
cdc_bdown_fn = wcd934x_bring_down;
break;
case WCD9335:
cdc_bdown_fn = wcd9335_bring_down;
break;
default:
cdc_bdown_fn = NULL;
break;
}
return cdc_bdown_fn;
}
codec_bringup_fn wcd9xxx_bringup_fn(int type)
{
codec_bringup_fn cdc_bup_fn;
switch (type) {
case WCD934X:
cdc_bup_fn = wcd934x_bring_up;
break;
case WCD9335:
cdc_bup_fn = wcd9335_bring_up;
break;
default:
cdc_bup_fn = NULL;
break;
}
return cdc_bup_fn;
}
codec_type_fn wcd9xxx_get_codec_info_fn(int type)
{
codec_type_fn cdc_type_fn;
switch (type) {
case WCD934X:
cdc_type_fn = wcd934x_get_cdc_info;
break;
case WCD9335:
cdc_type_fn = wcd9335_get_cdc_info;
break;
default:
cdc_type_fn = NULL;
break;
}
return cdc_type_fn;
}

Melihat File

@@ -0,0 +1,584 @@
/* Copyright (c) 2012-2017, 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/mutex.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
struct wcd9xxx_slim_sch {
u16 rx_port_ch_reg_base;
u16 port_tx_cfg_reg_base;
u16 port_rx_cfg_reg_base;
};
static struct wcd9xxx_slim_sch sh_ch;
static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
u8 wcd9xxx_pgd_la, u32 cnt,
struct wcd9xxx_ch *channels, u32 path);
static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
u32 cnt, struct wcd9xxx_ch *channels);
static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
{
if (wcd9xxx->codec_type->slim_slave_type ==
WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) {
sh_ch.rx_port_ch_reg_base = 0x180;
sh_ch.port_rx_cfg_reg_base = 0x040;
sh_ch.port_tx_cfg_reg_base = 0x040;
} else {
sh_ch.rx_port_ch_reg_base =
0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
sh_ch.port_rx_cfg_reg_base =
0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
sh_ch.port_tx_cfg_reg_base = 0x050;
}
return 0;
}
/**
* wcd9xxx_init_slimslave
*
* @wcd9xxx: pointer to wcd9xxx struct
* @wcd9xxx_pgd_la: pgd_la value
* @tx_num: tx number
* @rx_num: rx number
* @tx_slot: pointer to tx slot
* @rx_slot: pointer to rx slot
*
* Returns 0 on success, appropriate error code otherwise
*/
int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
int ret = 0;
int i;
ret = wcd9xxx_configure_ports(wcd9xxx);
if (ret) {
pr_err("%s: Failed to configure register address offset\n",
__func__);
goto err;
}
if (!rx_num || rx_num > wcd9xxx->num_rx_port) {
pr_err("%s: invalid rx num %d\n", __func__, rx_num);
return -EINVAL;
}
if (wcd9xxx->rx_chs) {
wcd9xxx->num_rx_port = rx_num;
for (i = 0; i < rx_num; i++) {
wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
}
ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
wcd9xxx->num_rx_port,
wcd9xxx->rx_chs,
SLIM_SINK);
if (ret) {
pr_err("%s: Failed to alloc %d rx slimbus channels\n",
__func__, wcd9xxx->num_rx_port);
kfree(wcd9xxx->rx_chs);
wcd9xxx->rx_chs = NULL;
wcd9xxx->num_rx_port = 0;
}
} else {
pr_err("Not able to allocate memory for %d slimbus rx ports\n",
wcd9xxx->num_rx_port);
}
if (!tx_num || tx_num > wcd9xxx->num_tx_port) {
pr_err("%s: invalid tx num %d\n", __func__, tx_num);
return -EINVAL;
}
if (wcd9xxx->tx_chs) {
wcd9xxx->num_tx_port = tx_num;
for (i = 0; i < tx_num; i++) {
wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
}
ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
wcd9xxx->num_tx_port,
wcd9xxx->tx_chs,
SLIM_SRC);
if (ret) {
pr_err("%s: Failed to alloc %d tx slimbus channels\n",
__func__, wcd9xxx->num_tx_port);
kfree(wcd9xxx->tx_chs);
wcd9xxx->tx_chs = NULL;
wcd9xxx->num_tx_port = 0;
}
} else {
pr_err("Not able to allocate memory for %d slimbus tx ports\n",
wcd9xxx->num_tx_port);
}
return 0;
err:
return ret;
}
EXPORT_SYMBOL(wcd9xxx_init_slimslave);
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
{
if (wcd9xxx->num_rx_port) {
wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
wcd9xxx->num_rx_port,
wcd9xxx->rx_chs);
wcd9xxx->num_rx_port = 0;
}
if (wcd9xxx->num_tx_port) {
wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
wcd9xxx->num_tx_port,
wcd9xxx->tx_chs);
wcd9xxx->num_tx_port = 0;
}
return 0;
}
static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
u8 wcd9xxx_pgd_la, u32 cnt,
struct wcd9xxx_ch *channels, u32 path)
{
int ret = 0;
u32 ch_idx;
/* The slimbus channel allocation seem take longer time
* so do the allocation up front to avoid delay in start of
* playback
*/
pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
ret = slim_get_slaveport(wcd9xxx_pgd_la,
channels[ch_idx].port,
&channels[ch_idx].sph, path);
pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
"channels[%d].sph[%d] path[%d]\n",
__func__, wcd9xxx_pgd_la, ch_idx,
channels[ch_idx].port,
ch_idx, channels[ch_idx].sph, path);
if (ret < 0) {
pr_err("%s: slave port failure id[%d] ret[%d]\n",
__func__, channels[ch_idx].ch_num, ret);
goto err;
}
ret = slim_query_ch(wcd9xxx->slim,
channels[ch_idx].ch_num,
&channels[ch_idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
__func__, channels[ch_idx].ch_num, ret);
goto err;
}
}
err:
return ret;
}
static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
u32 cnt, struct wcd9xxx_ch *channels)
{
int idx = 0;
int ret = 0;
/* slim_dealloc_ch */
for (idx = 0; idx < cnt; idx++) {
ret = slim_dealloc_ch(slim, channels[idx].ch_h);
if (ret < 0) {
pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
__func__, ret, channels[idx].ch_h);
}
}
return ret;
}
/* Enable slimbus slave device for RX path */
int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list,
unsigned int rate, unsigned int bit_width,
u16 *grph)
{
u8 ch_cnt = 0;
u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
u8 payload = 0;
u16 codec_port = 0;
int ret;
struct slim_ch prop;
struct wcd9xxx_ch *rx;
int size = ARRAY_SIZE(ch_h);
/* Configure slave interface device */
list_for_each_entry(rx, wcd9xxx_ch_list, list) {
payload |= 1 << rx->shift;
if (ch_cnt < size) {
ch_h[ch_cnt] = rx->ch_h;
ch_cnt++;
pr_debug("list ch->ch_h %d ch->sph %d\n",
rx->ch_h, rx->sph);
} else {
pr_err("%s: allocated channel number %u is out of max rangae %d\n",
__func__, ch_cnt,
size);
ret = EINVAL;
goto err;
}
}
pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
__func__, ch_cnt, rate, WATER_MARK_VAL);
/* slim_define_ch api */
prop.prot = SLIM_AUTO_ISO;
if ((rate == 44100) || (rate == 88200) || (rate == 176400) ||
(rate == 352800)) {
prop.baser = SLIM_RATE_11025HZ;
prop.ratem = (rate/11025);
} else {
prop.baser = SLIM_RATE_4000HZ;
prop.ratem = (rate/4000);
}
prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
prop.sampleszbits = bit_width;
pr_debug("Before slim_define_ch:\n"
"ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
ch_cnt, ch_h[0], ch_h[1], *grph);
ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
true, grph);
if (ret < 0) {
pr_err("%s: slim_define_ch failed ret[%d]\n",
__func__, ret);
goto err;
}
list_for_each_entry(rx, wcd9xxx_ch_list, list) {
codec_port = rx->port;
pr_debug("%s: codec_port %d rx 0x%p, payload %d\n"
"sh_ch.rx_port_ch_reg_base0 0x%x\n"
"sh_ch.port_rx_cfg_reg_base 0x%x\n",
__func__, codec_port, rx, payload,
sh_ch.rx_port_ch_reg_base,
sh_ch.port_rx_cfg_reg_base);
/* look for the valid port range and chose the
* payload accordingly
*/
/* write to interface device */
ret = wcd9xxx_interface_reg_write(wcd9xxx,
SB_PGD_RX_PORT_MULTI_CHANNEL_0(
sh_ch.rx_port_ch_reg_base, codec_port),
payload);
if (ret < 0) {
pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
__func__,
SB_PGD_RX_PORT_MULTI_CHANNEL_0(
sh_ch.rx_port_ch_reg_base, codec_port),
payload, ret);
goto err;
}
/* configure the slave port for water mark and enable*/
ret = wcd9xxx_interface_reg_write(wcd9xxx,
SB_PGD_PORT_CFG_BYTE_ADDR(
sh_ch.port_rx_cfg_reg_base, codec_port),
WATER_MARK_VAL);
if (ret < 0) {
pr_err("%s:watermark set failure for port[%d] ret[%d]",
__func__, codec_port, ret);
}
ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
if (ret < 0) {
pr_err("%s: slim_connect_sink failed ret[%d]\n",
__func__, ret);
goto err_close_slim_sch;
}
}
/* slim_control_ch */
ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
__func__, ret);
goto err_close_slim_sch;
}
return 0;
err_close_slim_sch:
/* release all acquired handles */
wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
err:
return ret;
}
EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx);
/* Enable slimbus slave device for RX path */
int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list,
unsigned int rate, unsigned int bit_width,
u16 *grph)
{
u16 ch_cnt = 0;
u16 payload = 0;
u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
u16 codec_port;
int ret = 0;
struct wcd9xxx_ch *tx;
int size = ARRAY_SIZE(ch_h);
struct slim_ch prop;
list_for_each_entry(tx, wcd9xxx_ch_list, list) {
payload |= 1 << tx->shift;
if (ch_cnt < size) {
ch_h[ch_cnt] = tx->ch_h;
ch_cnt++;
} else {
pr_err("%s: allocated channel number %u is out of max rangae %d\n",
__func__, ch_cnt,
size);
ret = EINVAL;
goto err;
}
}
/* slim_define_ch api */
prop.prot = SLIM_AUTO_ISO;
prop.baser = SLIM_RATE_4000HZ;
prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
prop.ratem = (rate/4000);
prop.sampleszbits = bit_width;
ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
true, grph);
if (ret < 0) {
pr_err("%s: slim_define_ch failed ret[%d]\n",
__func__, ret);
goto err;
}
pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt,
rate, bit_width);
list_for_each_entry(tx, wcd9xxx_ch_list, list) {
codec_port = tx->port;
pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n",
__func__, codec_port, tx, payload);
/* write to interface device */
ret = wcd9xxx_interface_reg_write(wcd9xxx,
SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
payload & 0x00FF);
if (ret < 0) {
pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
__func__,
SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
payload, ret);
goto err;
}
/* ports 8,9 */
ret = wcd9xxx_interface_reg_write(wcd9xxx,
SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
(payload & 0xFF00)>>8);
if (ret < 0) {
pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
__func__,
SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
payload, ret);
goto err;
}
/* configure the slave port for water mark and enable*/
ret = wcd9xxx_interface_reg_write(wcd9xxx,
SB_PGD_PORT_CFG_BYTE_ADDR(
sh_ch.port_tx_cfg_reg_base, codec_port),
WATER_MARK_VAL);
if (ret < 0) {
pr_err("%s:watermark set failure for port[%d] ret[%d]",
__func__, codec_port, ret);
}
ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
if (ret < 0) {
pr_err("%s: slim_connect_src failed ret[%d]\n",
__func__, ret);
goto err;
}
}
/* slim_control_ch */
ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
__func__, ret);
goto err;
}
return 0;
err:
/* release all acquired handles */
wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
return ret;
}
EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx);
int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph)
{
u32 sph[SLIM_MAX_RX_PORTS] = {0};
int ch_cnt = 0;
int ret = 0;
struct wcd9xxx_ch *rx;
list_for_each_entry(rx, wcd9xxx_ch_list, list)
sph[ch_cnt++] = rx->sph;
pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
goto err;
}
err:
return ret;
}
EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx);
int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list,
u16 grph)
{
u32 sph[SLIM_MAX_TX_PORTS] = {0};
int ret = 0;
int ch_cnt = 0;
struct wcd9xxx_ch *tx;
pr_debug("%s\n", __func__);
list_for_each_entry(tx, wcd9xxx_ch_list, list)
sph[ch_cnt++] = tx->sph;
pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
__func__, ch_cnt, sph[0], sph[1]);
/* slim_control_ch (REMOVE) */
ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
if (ret < 0) {
pr_err("%s: slim_control_ch failed ret[%d]\n",
__func__, ret);
goto err;
}
err:
return ret;
}
EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx);
int wcd9xxx_get_slave_port(unsigned int ch_num)
{
int ret = 0;
ret = (ch_num - BASE_CH_NUM);
pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
if (ret < 0) {
pr_err("%s: Error:- Invalid slave port found = %d\n",
__func__, ret);
return -EINVAL;
}
return ret;
}
EXPORT_SYMBOL(wcd9xxx_get_slave_port);
int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph)
{
u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
int ch_cnt = 0;
int ret = 0;
struct wcd9xxx_ch *slim_ch;
list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
sph[ch_cnt++] = slim_ch->sph;
/* slim_disconnect_port */
ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
if (ret < 0) {
pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
__func__, ret);
}
return ret;
}
EXPORT_SYMBOL(wcd9xxx_disconnect_port);
/* This function is called with mutex acquired */
int wcd9xxx_rx_vport_validation(u32 port_id,
struct list_head *codec_dai_list)
{
struct wcd9xxx_ch *ch;
int ret = 0;
pr_debug("%s: port_id %u\n", __func__, port_id);
list_for_each_entry(ch,
codec_dai_list, list) {
pr_debug("%s: ch->port %u\n", __func__, ch->port);
if (ch->port == port_id) {
ret = -EINVAL;
break;
}
}
return ret;
}
EXPORT_SYMBOL(wcd9xxx_rx_vport_validation);
/* This function is called with mutex acquired */
int wcd9xxx_tx_vport_validation(u32 table, u32 port_id,
struct wcd9xxx_codec_dai_data *codec_dai,
u32 num_codec_dais)
{
struct wcd9xxx_ch *ch;
int ret = 0;
u32 index;
unsigned long vtable = table;
u32 size = sizeof(table) * BITS_PER_BYTE;
pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__,
vtable, port_id, size);
for_each_set_bit(index, &vtable, size) {
if (index < num_codec_dais) {
list_for_each_entry(ch,
&codec_dai[index].wcd9xxx_ch_list,
list) {
pr_debug("%s: index %u ch->port %u vtable 0x%lx\n",
__func__, index, ch->port,
vtable);
if (ch->port == port_id) {
pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
__func__, port_id + 1,
(index + 1)/2);
ret = -EINVAL;
break;
}
}
} else {
pr_err("%s: Invalid index %d of codec dai",
__func__, index);
ret = -EINVAL;
}
if (ret)
break;
}
return ret;
}
EXPORT_SYMBOL(wcd9xxx_tx_vport_validation);

1198
drivers/mfd/wcd9xxx-utils.c Normal file

File diff ditekan karena terlalu besar Load Diff

5
drivers/misc/Makefile Normal file
Melihat File

@@ -0,0 +1,5 @@
#
# Makefile for misc devices that really don't fit anywhere else.
#
obj-y += qcom/

19
drivers/misc/qcom/Kconfig Normal file
Melihat File

@@ -0,0 +1,19 @@
config MSM_QDSP6V2_CODECS
bool "Audio QDSP6V2 APR support"
select SND_SOC_QDSP6V2
help
Enable Audio codecs with APR IPC protocol support between
application processor and QDSP6 for B-family. APR is
used by audio driver to configure QDSP6's
ASM, ADM and AFE.
config MSM_ULTRASOUND
bool "QDSP6V2 HW Ultrasound support"
select SND_SOC_QDSP6V2
help
Enable HW Ultrasound support in QDSP6V2.
QDSP6V2 can support HW encoder & decoder and
ultrasound processing. It will enable
ultrasound data paths between
HW and services, calculating input events
upon the ultrasound data.

Melihat File

@@ -0,0 +1 @@
obj-y += qdsp6v2/

Melihat File

@@ -0,0 +1,6 @@
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o g711mlaw_in.o g711alaw_in.o audio_utils.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_alac.o audio_ape.o audio_utils_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_g711mlaw.o audio_g711alaw.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o audio_hwacc_effects.o
obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/

Melihat File

@@ -0,0 +1,709 @@
/*
* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_aac.h>
#include <linux/compat.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 5 frames in buffer with meta */
#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5))
#define AAC_FORMAT_ADTS 65535
#define MAX_SAMPLE_RATE_384K 384000
static long aac_in_ioctl_shared(struct file *file, unsigned int cmd, void *arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_aac_enc_config *enc_cfg;
struct msm_audio_aac_config *aac_config;
uint32_t aac_mode = AAC_ENC_MODE_AAC_LC;
enc_cfg = audio->enc_cfg;
aac_config = audio->codec_cfg;
/* ENCODE CFG (after new set of API's are published )bharath*/
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
pr_info("%s:AUDIO_START already over\n", __func__);
rc = 0;
break;
}
if (audio->opened) {
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed\n",
__func__, audio->ac->session);
break;
}
} else {
if (audio->feedback == NON_TUNNEL_MODE) {
pr_debug("%s: starting in non_tunnel mode",
__func__);
rc = q6asm_open_read_write(audio->ac,
FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:open read write failed\n",
__func__);
break;
}
}
if (audio->feedback == TUNNEL_MODE) {
pr_debug("%s: starting in tunnel mode",
__func__);
rc = q6asm_open_read(audio->ac,
FORMAT_MPEG4_AAC);
if (rc < 0) {
pr_err("%s:open read failed\n",
__func__);
break;
}
}
audio->stopped = 0;
}
pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__,
aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag);
if (aac_config->sbr_ps_on_flag)
aac_mode = AAC_ENC_MODE_EAAC_P;
else if (aac_config->sbr_on_flag)
aac_mode = AAC_ENC_MODE_AAC_P;
else
aac_mode = AAC_ENC_MODE_AAC_LC;
rc = q6asm_enc_cfg_blk_aac(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->sample_rate,
enc_cfg->channels,
enc_cfg->bit_rate,
aac_mode,
enc_cfg->stream_format);
if (rc < 0) {
pr_err("%s:session id %d: cmd media format block failed\n",
__func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed\n",
__func__, audio->ac->session);
break;
}
}
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac);
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__,
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
break;
}
case AUDIO_GET_AAC_ENC_CONFIG: {
struct msm_audio_aac_enc_config *cfg;
struct msm_audio_aac_enc_config *enc_cfg;
cfg = (struct msm_audio_aac_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer for %s\n",
__func__, "AUDIO_GET_AAC_CONFIG");
rc = -EINVAL;
break;
}
memset(cfg, 0, sizeof(*cfg));
enc_cfg = audio->enc_cfg;
if (enc_cfg->channels == CH_MODE_MONO)
cfg->channels = 1;
else
cfg->channels = 2;
cfg->sample_rate = enc_cfg->sample_rate;
cfg->bit_rate = enc_cfg->bit_rate;
switch (enc_cfg->stream_format) {
case 0x00:
cfg->stream_format = AUDIO_AAC_FORMAT_ADTS;
break;
case 0x01:
cfg->stream_format = AUDIO_AAC_FORMAT_LOAS;
break;
case 0x02:
cfg->stream_format = AUDIO_AAC_FORMAT_ADIF;
break;
default:
case 0x03:
cfg->stream_format = AUDIO_AAC_FORMAT_RAW;
}
pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d bitrate=%d\n",
__func__, audio->ac->session,
cfg->stream_format, cfg->sample_rate, cfg->bit_rate);
break;
}
case AUDIO_SET_AAC_ENC_CONFIG: {
struct msm_audio_aac_enc_config *cfg;
struct msm_audio_aac_enc_config *enc_cfg;
uint32_t min_bitrate, max_bitrate;
cfg = (struct msm_audio_aac_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer for %s\n",
"AUDIO_SET_AAC_ENC_CONFIG", __func__);
rc = -EINVAL;
break;
}
enc_cfg = audio->enc_cfg;
pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__,
audio->ac->session, cfg->stream_format);
switch (cfg->stream_format) {
case AUDIO_AAC_FORMAT_ADTS:
enc_cfg->stream_format = 0x00;
break;
case AUDIO_AAC_FORMAT_LOAS:
enc_cfg->stream_format = 0x01;
break;
case AUDIO_AAC_FORMAT_ADIF:
enc_cfg->stream_format = 0x02;
break;
case AUDIO_AAC_FORMAT_RAW:
enc_cfg->stream_format = 0x03;
break;
default:
pr_err("%s:session id %d: unsupported AAC format %d\n",
__func__, audio->ac->session,
cfg->stream_format);
rc = -EINVAL;
break;
}
if (cfg->channels == 1) {
cfg->channels = CH_MODE_MONO;
} else if (cfg->channels == 2) {
cfg->channels = CH_MODE_STEREO;
} else {
rc = -EINVAL;
break;
}
if (cfg->sample_rate > MAX_SAMPLE_RATE_384K) {
pr_err("%s: ERROR: invalid sample rate = %u",
__func__, cfg->sample_rate);
rc = -EINVAL;
break;
}
min_bitrate = ((cfg->sample_rate)*(cfg->channels))/2;
/* This calculation should be based on AAC mode. But we cannot
* get AAC mode in this setconfig. min_bitrate's logical max
* value is 24000. So if min_bitrate is higher than 24000,
* choose 24000.
*/
if (min_bitrate > 24000)
min_bitrate = 24000;
max_bitrate = 6*(cfg->sample_rate)*(cfg->channels);
if (max_bitrate > 192000)
max_bitrate = 192000;
if ((cfg->bit_rate < min_bitrate) ||
(cfg->bit_rate > max_bitrate)) {
pr_err("%s: bitrate permissible: max=%d, min=%d\n",
__func__, max_bitrate, min_bitrate);
pr_err("%s: ERROR in setting bitrate = %d\n",
__func__, cfg->bit_rate);
rc = -EINVAL;
break;
}
enc_cfg->sample_rate = cfg->sample_rate;
enc_cfg->channels = cfg->channels;
enc_cfg->bit_rate = cfg->bit_rate;
pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x bitrate=0x%x, format(adts/raw) = %d\n",
__func__, audio->ac->session, enc_cfg->sample_rate,
enc_cfg->channels, enc_cfg->bit_rate,
enc_cfg->stream_format);
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config *aac_cfg;
struct msm_audio_aac_config *audio_aac_cfg;
struct msm_audio_aac_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
audio_aac_cfg = audio->codec_cfg;
aac_cfg = (struct msm_audio_aac_config *)arg;
if (aac_cfg == NULL) {
pr_err("%s: NULL config pointer %s\n",
__func__, "AUDIO_SET_AAC_CONFIG");
rc = -EINVAL;
break;
}
pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d sbr_ps_flag = %d\n",
__func__, audio->ac->session, aac_cfg->sbr_on_flag,
aac_cfg->sbr_ps_on_flag);
audio_aac_cfg->sbr_on_flag = aac_cfg->sbr_on_flag;
audio_aac_cfg->sbr_ps_on_flag = aac_cfg->sbr_ps_on_flag;
if ((audio_aac_cfg->sbr_on_flag == 1) ||
(audio_aac_cfg->sbr_ps_on_flag == 1)) {
if (enc_cfg->sample_rate < 24000) {
pr_err("%s: ERROR in setting samplerate = %d\n",
__func__, enc_cfg->sample_rate);
rc = -EINVAL;
break;
}
}
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = aac_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AAC_ENC_CONFIG: {
struct msm_audio_aac_enc_config cfg;
rc = aac_in_ioctl_shared(file, cmd, &cfg);
if (rc) {
pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG failed. rc=%d\n",
__func__, rc);
break;
}
if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AAC_ENC_CONFIG: {
struct msm_audio_aac_enc_config cfg;
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = aac_in_ioctl_shared(file, cmd, &cfg);
if (rc)
pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
__func__, rc);
break;
}
case AUDIO_GET_AAC_CONFIG: {
if (copy_to_user((void *)arg, &audio->codec_cfg,
sizeof(struct msm_audio_aac_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config aac_cfg;
if (copy_from_user(&aac_cfg, (void *)arg,
sizeof(struct msm_audio_aac_config))) {
pr_err("%s: copy_to_user for AUDIO_SET_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc=%d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd=%d\n", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_aac_enc_config32 {
u32 channels;
u32 sample_rate;
u32 bit_rate;
u32 stream_format;
};
struct msm_audio_aac_config32 {
s16 format;
u16 audio_object;
u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
u16 aac_section_data_resilience_flag;
u16 aac_scalefactor_data_resilience_flag;
u16 aac_spectral_data_resilience_flag;
u16 sbr_on_flag;
u16 sbr_ps_on_flag;
u16 dual_mono_mode;
u16 channel_configuration;
u16 sample_rate;
};
enum {
AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
AUDIO_SET_AAC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config32),
AUDIO_GET_AAC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config32)
};
static long aac_in_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = aac_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AAC_ENC_CONFIG_32: {
struct msm_audio_aac_enc_config cfg;
struct msm_audio_aac_enc_config32 cfg_32;
memset(&cfg_32, 0, sizeof(cfg_32));
cmd = AUDIO_GET_AAC_ENC_CONFIG;
rc = aac_in_ioctl_shared(file, cmd, &cfg);
if (rc) {
pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG_32 failed. Rc= %d\n",
__func__, rc);
break;
}
cfg_32.channels = cfg.channels;
cfg_32.sample_rate = cfg.sample_rate;
cfg_32.bit_rate = cfg.bit_rate;
cfg_32.stream_format = cfg.stream_format;
if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AAC_ENC_CONFIG_32: {
struct msm_audio_aac_enc_config cfg;
struct msm_audio_aac_enc_config32 cfg_32;
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.channels = cfg_32.channels;
cfg.sample_rate = cfg_32.sample_rate;
cfg.bit_rate = cfg_32.bit_rate;
cfg.stream_format = cfg_32.stream_format;
/* The command should be converted from 32 bit to normal
* before the shared ioctl is called as shared ioctl
* can process only normal commands
*/
cmd = AUDIO_SET_AAC_ENC_CONFIG;
rc = aac_in_ioctl_shared(file, cmd, &cfg);
if (rc)
pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG_32 failed. rc=%d\n",
__func__, rc);
break;
}
case AUDIO_GET_AAC_CONFIG_32: {
struct msm_audio_aac_config *aac_config;
struct msm_audio_aac_config32 aac_config_32;
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
aac_config_32.format = aac_config->format;
aac_config_32.audio_object = aac_config->audio_object;
aac_config_32.ep_config = aac_config->ep_config;
aac_config_32.aac_section_data_resilience_flag =
aac_config->aac_section_data_resilience_flag;
aac_config_32.aac_scalefactor_data_resilience_flag =
aac_config->aac_scalefactor_data_resilience_flag;
aac_config_32.aac_spectral_data_resilience_flag =
aac_config->aac_spectral_data_resilience_flag;
aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
aac_config_32.channel_configuration =
aac_config->channel_configuration;
aac_config_32.sample_rate = aac_config->sample_rate;
if (copy_to_user((void *)arg, &aac_config_32,
sizeof(aac_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG_32: {
struct msm_audio_aac_config aac_cfg;
struct msm_audio_aac_config32 aac_cfg_32;
if (copy_from_user(&aac_cfg_32, (void *)arg,
sizeof(aac_cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
aac_cfg.format = aac_cfg_32.format;
aac_cfg.audio_object = aac_cfg_32.audio_object;
aac_cfg.ep_config = aac_cfg_32.ep_config;
aac_cfg.aac_section_data_resilience_flag =
aac_cfg_32.aac_section_data_resilience_flag;
aac_cfg.aac_scalefactor_data_resilience_flag =
aac_cfg_32.aac_scalefactor_data_resilience_flag;
aac_cfg.aac_spectral_data_resilience_flag =
aac_cfg_32.aac_spectral_data_resilience_flag;
aac_cfg.sbr_on_flag = aac_cfg_32.sbr_on_flag;
aac_cfg.sbr_ps_on_flag = aac_cfg_32.sbr_ps_on_flag;
aac_cfg.dual_mono_mode = aac_cfg_32.dual_mono_mode;
aac_cfg.channel_configuration =
aac_cfg_32.channel_configuration;
aac_cfg.sample_rate = aac_cfg_32.sample_rate;
cmd = AUDIO_SET_AAC_CONFIG;
rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d\n", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#else
#define aac_in_compat_ioctl NULL
#endif
static int aac_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_aac_enc_config *enc_cfg;
struct msm_audio_aac_config *aac_config;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
aac_config = audio->codec_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 1536;
audio->max_frames_per_buf = 5;
enc_cfg->sample_rate = 8000;
enc_cfg->channels = 1;
enc_cfg->bit_rate = 16000;
enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
aac_config->format = AUDIO_AAC_FORMAT_ADTS;
aac_config->audio_object = AUDIO_AAC_OBJECT_LC;
aac_config->sbr_on_flag = 0;
aac_config->sbr_ps_on_flag = 0;
aac_config->channel_configuration = 1;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->enc_cfg);
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
/* open aac encoder in tunnel mode */
audio->buf_cfg.frames_per_buf = 0x01;
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
audio->buf_cfg.meta_info_enable = 0x01;
pr_info("%s:session id %d: NT mode encoder success\n", __func__,
audio->ac->session);
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC);
if (rc < 0) {
pr_err("%s:session id %d: Tunnel Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__,
audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
audio->buf_cfg.meta_info_enable = 0x00;
pr_info("%s:session id %d: T mode encoder success\n", __func__,
audio->ac->session);
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
audio->reset_event = false;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = aac_in_compat_ioctl;
audio->enc_ioctl = aac_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = aac_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
.compat_ioctl = audio_in_compat_ioctl
};
struct miscdevice audio_aac_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_aac_in",
.fops = &audio_in_fops,
};
static int __init aac_in_init(void)
{
return misc_register(&audio_aac_in_misc);
}
device_initcall(aac_in_init);

Melihat File

@@ -0,0 +1,402 @@
/*
* Copyright (c) 2010-2012, 2014, 2016-2017 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/msm_audio_amrnb.h>
#include <linux/compat.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10))
static long amrnb_in_ioctl_shared(struct file *file,
unsigned int cmd, void *arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
enc_cfg = audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
pr_info("%s:AUDIO_START already over\n", __func__);
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed\n",
__func__, audio->ac->session);
break;
}
rc = q6asm_enc_cfg_blk_amrnb(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->band_mode,
enc_cfg->dtx_enable);
if (rc < 0) {
pr_err("%s:session id %d: cmd amrnb media format block failed\n",
__func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed\n",
__func__, audio->ac->session);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
__func__, audio->ac->session,
audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:AUDIO_STOP\n", __func__);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
break;
}
case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
struct msm_audio_amrnb_enc_config_v2 *cfg;
struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
cfg = (struct msm_audio_amrnb_enc_config_v2 *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer for %s\n",
__func__,
"AUDIO_SET_AMRNB_ENC_CONFIG_V2");
rc = -EINVAL;
break;
}
enc_cfg = audio->enc_cfg;
if (cfg->band_mode > 8 ||
cfg->band_mode < 1) {
pr_err("%s:session id %d: invalid band mode\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
/* AMR NB encoder accepts values between 0-7
* while openmax provides value between 1-8
* as per spec
*/
enc_cfg->band_mode = (cfg->band_mode - 1);
enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
enc_cfg->frame_format = 0;
pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
__func__, audio->ac->session,
enc_cfg->band_mode, enc_cfg->dtx_enable);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
static long amrnb_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = amrnb_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_amrnb_enc_config_v2))) {
pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
struct msm_audio_amrnb_enc_config_v2 cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(cfg))) {
pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = amrnb_in_ioctl_shared(file, cmd, &cfg);
if (rc)
pr_err("%s: AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed. rc=%d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd=%d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_amrnb_enc_config_v2_32 {
u32 band_mode;
u32 dtx_enable;
u32 frame_format;
};
enum {
AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+2),
struct msm_audio_amrnb_enc_config_v2_32),
AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+3),
struct msm_audio_amrnb_enc_config_v2_32)
};
static long amrnb_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = amrnb_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AMRNB_ENC_CONFIG_V2_32: {
struct msm_audio_amrnb_enc_config_v2 *amrnb_config;
struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32;
memset(&amrnb_config_32, 0, sizeof(amrnb_config_32));
amrnb_config =
(struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg;
amrnb_config_32.band_mode = amrnb_config->band_mode;
amrnb_config_32.dtx_enable = amrnb_config->dtx_enable;
amrnb_config_32.frame_format = amrnb_config->frame_format;
if (copy_to_user((void *)arg, &amrnb_config_32,
sizeof(amrnb_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 failed",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AMRNB_ENC_CONFIG_V2_32: {
struct msm_audio_amrnb_enc_config_v2_32 cfg_32;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cmd = AUDIO_SET_AMRNB_ENC_CONFIG_V2;
rc = amrnb_in_ioctl_shared(file, cmd, &cfg_32);
if (rc)
pr_err("%s:AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#else
#define amrnb_in_compat_ioctl NULL
#endif
static int amrnb_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 32;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->band_mode = 7;
enc_cfg->dtx_enable = 0;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open amrnb encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: NT mode encoder success\n",
__func__, audio->ac->session);
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_AMRNB);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__, audio->ac->session,
rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: T mode encoder success\n",
__func__, audio->ac->session);
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = amrnb_in_compat_ioctl;
audio->enc_ioctl = amrnb_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = amrnb_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
.compat_ioctl = audio_in_compat_ioctl
};
struct miscdevice audio_amrnb_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_amrnb_in",
.fops = &audio_in_fops,
};
static int __init amrnb_in_init(void)
{
return misc_register(&audio_amrnb_in_misc);
}
device_initcall(amrnb_in_init);

Melihat File

@@ -0,0 +1,400 @@
/*
* Copyright (c) 2011-2012, 2014, 2016-2017 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/dma-mapping.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/msm_audio_amrwb.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/compat.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10))
static long amrwb_in_ioctl_shared(struct file *file,
unsigned int cmd, void *arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_amrwb_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
pr_info("%s:AUDIO_START already over\n", __func__);
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed\n",
__func__, audio->ac->session);
break;
}
rc = q6asm_enc_cfg_blk_amrwb(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->band_mode,
enc_cfg->dtx_enable);
if (rc < 0) {
pr_err("%s:session id %d: cmd amrwb media format block failed\n",
__func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed\n",
__func__, audio->ac->session);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
__func__, audio->ac->session,
audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:AUDIO_STOP\n", __func__);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
break;
}
case AUDIO_SET_AMRWB_ENC_CONFIG: {
struct msm_audio_amrwb_enc_config *cfg;
struct msm_audio_amrwb_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
cfg = (struct msm_audio_amrwb_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer for %s\n",
__func__, "AUDIO_SET_AMRWB_ENC_CONFIG");
rc = -EINVAL;
break;
}
if (cfg->band_mode > 8) {
pr_err("%s:session id %d: invalid band mode\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
/* ToDo: AMR WB encoder accepts values between 0-8
* while openmax provides value between 9-17
* as per spec
*/
enc_cfg->band_mode = cfg->band_mode;
enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
/* Currently DSP does not support different frameformat */
enc_cfg->frame_format = 0;
pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
__func__, audio->ac->session,
enc_cfg->band_mode, enc_cfg->dtx_enable);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
static long amrwb_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = amrwb_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AMRWB_ENC_CONFIG: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_amrwb_enc_config)))
pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
case AUDIO_SET_AMRWB_ENC_CONFIG: {
struct msm_audio_amrwb_enc_config cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(cfg))) {
pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = amrwb_in_ioctl_shared(file, cmd, &cfg);
if (rc)
pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_amrwb_enc_config_32 {
u32 band_mode;
u32 dtx_enable;
u32 frame_format;
};
enum {
AUDIO_GET_AMRWB_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0),
struct msm_audio_amrwb_enc_config_32),
AUDIO_SET_AMRWB_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1),
struct msm_audio_amrwb_enc_config_32)
};
static long amrwb_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = amrwb_in_ioctl_shared(file, cmd, NULL);
break;
}
case AUDIO_GET_AMRWB_ENC_CONFIG_32: {
struct msm_audio_amrwb_enc_config *amrwb_config;
struct msm_audio_amrwb_enc_config_32 amrwb_config_32;
memset(&amrwb_config_32, 0, sizeof(amrwb_config_32));
amrwb_config =
(struct msm_audio_amrwb_enc_config *)audio->enc_cfg;
amrwb_config_32.band_mode = amrwb_config->band_mode;
amrwb_config_32.dtx_enable = amrwb_config->dtx_enable;
amrwb_config_32.frame_format = amrwb_config->frame_format;
if (copy_to_user((void *)arg, &amrwb_config_32,
sizeof(struct msm_audio_amrwb_enc_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AMRWB_ENC_CONFIG_32: {
struct msm_audio_amrwb_enc_config cfg_32;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cmd = AUDIO_SET_AMRWB_ENC_CONFIG;
rc = amrwb_in_ioctl_shared(file, cmd, &cfg_32);
if (rc)
pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#else
#define amrwb_in_compat_ioctl NULL
#endif
static int amrwb_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_amrwb_enc_config *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 32;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->band_mode = 8;
enc_cfg->dtx_enable = 0;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 16000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s:audio[%pK]: Could not allocate memory for audio client\n",
__func__, audio);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open amrwb encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: NT mode encoder success\n",
__func__, audio->ac->session);
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_AMRWB);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__, audio->ac->session,
rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: T mode encoder success\n",
__func__, audio->ac->session);
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = amrwb_in_compat_ioctl;
audio->enc_ioctl = amrwb_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = amrwb_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
.compat_ioctl = audio_in_compat_ioctl
};
struct miscdevice audio_amrwb_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_amrwb_in",
.fops = &audio_in_fops,
};
static int __init amrwb_in_init(void)
{
return misc_register(&audio_amrwb_in_misc);
}
device_initcall(amrwb_in_init);

Melihat File

@@ -0,0 +1,474 @@
/* aac audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/msm_audio_aac.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
#define AUDIO_AAC_DUAL_MONO_INVALID -1
#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out))
static struct miscdevice audio_aac_misc;
static struct ws_mgr audio_aac_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_aac_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_aac_cfg aac_cfg;
struct msm_audio_aac_config *aac_config;
uint32_t sbr_ps = 0x00;
pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
/* turn on both sbr and ps */
rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
if (rc < 0)
pr_err("sbr-ps enable failed\n");
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
if (aac_config->sbr_ps_on_flag)
aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
else if (aac_config->sbr_on_flag)
aac_cfg.aot = AAC_ENC_MODE_AAC_P;
else
aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
switch (aac_config->format) {
case AUDIO_AAC_FORMAT_ADTS:
aac_cfg.format = 0x00;
break;
case AUDIO_AAC_FORMAT_LOAS:
aac_cfg.format = 0x01;
break;
case AUDIO_AAC_FORMAT_ADIF:
aac_cfg.format = 0x02;
break;
default:
case AUDIO_AAC_FORMAT_RAW:
aac_cfg.format = 0x03;
}
aac_cfg.ep_config = aac_config->ep_config;
aac_cfg.section_data_resilience =
aac_config->aac_section_data_resilience_flag;
aac_cfg.scalefactor_data_resilience =
aac_config->aac_scalefactor_data_resilience_flag;
aac_cfg.spectral_data_resilience =
aac_config->aac_spectral_data_resilience_flag;
aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
if (audio->feedback == TUNNEL_MODE) {
aac_cfg.sample_rate = aac_config->sample_rate;
aac_cfg.ch_cfg = aac_config->channel_configuration;
} else {
aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
}
pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
__func__, aac_cfg.format,
aac_cfg.aot, aac_cfg.ch_cfg,
aac_cfg.sample_rate);
/* Configure Media format block */
rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
rc = enable_volume_ramp(audio);
if (rc < 0) {
pr_err("%s: Failed to enable volume ramp\n",
__func__);
}
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config *aac_config;
uint16_t sce_left = 1, sce_right = 2;
pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
aac_config = (struct msm_audio_aac_config *)arg;
if (aac_config == NULL) {
pr_err("%s: Invalid config pointer\n", __func__);
rc = -EINVAL;
break;
}
memcpy(audio->codec_cfg, aac_config,
sizeof(struct msm_audio_aac_config));
/* PL_PR is 0 only need to check PL_SR */
if (aac_config->dual_mono_mode >
AUDIO_AAC_DUAL_MONO_PL_SR) {
pr_err("%s:Invalid dual_mono mode =%d\n", __func__,
aac_config->dual_mono_mode);
} else {
/* convert the data from user into sce_left
* and sce_right based on the definitions
*/
pr_debug("%s: modify dual_mono mode =%d\n", __func__,
aac_config->dual_mono_mode);
switch (aac_config->dual_mono_mode) {
case AUDIO_AAC_DUAL_MONO_PL_PR:
sce_left = 1;
sce_right = 1;
break;
case AUDIO_AAC_DUAL_MONO_SL_SR:
sce_left = 2;
sce_right = 2;
break;
case AUDIO_AAC_DUAL_MONO_SL_PR:
sce_left = 2;
sce_right = 1;
break;
case AUDIO_AAC_DUAL_MONO_PL_SR:
default:
sce_left = 1;
sce_right = 2;
break;
}
rc = q6asm_cfg_dual_mono_aac(audio->ac,
sce_left, sce_right);
if (rc < 0)
pr_err("%s:asm cmd dualmono failed rc=%d\n",
__func__, rc);
}
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AAC_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_aac_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config aac_config;
pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
if (copy_from_user(&aac_config, (void *)arg,
sizeof(aac_config))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = audio_ioctl_shared(file, cmd, &aac_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
__func__, audio, rc);
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_aac_config32 {
s16 format;
u16 audio_object;
u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
u16 aac_section_data_resilience_flag;
u16 aac_scalefactor_data_resilience_flag;
u16 aac_spectral_data_resilience_flag;
u16 sbr_on_flag;
u16 sbr_ps_on_flag;
u16 dual_mono_mode;
u16 channel_configuration;
u16 sample_rate;
};
enum {
AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AAC_CONFIG_32: {
struct msm_audio_aac_config *aac_config;
struct msm_audio_aac_config32 aac_config_32;
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
aac_config_32.format = aac_config->format;
aac_config_32.audio_object = aac_config->audio_object;
aac_config_32.ep_config = aac_config->ep_config;
aac_config_32.aac_section_data_resilience_flag =
aac_config->aac_section_data_resilience_flag;
aac_config_32.aac_scalefactor_data_resilience_flag =
aac_config->aac_scalefactor_data_resilience_flag;
aac_config_32.aac_spectral_data_resilience_flag =
aac_config->aac_spectral_data_resilience_flag;
aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
aac_config_32.channel_configuration =
aac_config->channel_configuration;
aac_config_32.sample_rate = aac_config->sample_rate;
if (copy_to_user((void *)arg, &aac_config_32,
sizeof(aac_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG_32: {
struct msm_audio_aac_config aac_config;
struct msm_audio_aac_config32 aac_config_32;
pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
if (copy_from_user(&aac_config_32, (void *)arg,
sizeof(aac_config_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
aac_config.format = aac_config_32.format;
aac_config.audio_object = aac_config_32.audio_object;
aac_config.ep_config = aac_config_32.ep_config;
aac_config.aac_section_data_resilience_flag =
aac_config_32.aac_section_data_resilience_flag;
aac_config.aac_scalefactor_data_resilience_flag =
aac_config_32.aac_scalefactor_data_resilience_flag;
aac_config.aac_spectral_data_resilience_flag =
aac_config_32.aac_spectral_data_resilience_flag;
aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
aac_config.channel_configuration =
aac_config_32.channel_configuration;
aac_config.sample_rate = aac_config_32.sample_rate;
cmd = AUDIO_SET_AAC_CONFIG;
rc = audio_ioctl_shared(file, cmd, &aac_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
__func__, audio, rc);
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
struct msm_audio_aac_config *aac_config = NULL;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_aac_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
aac_config = audio->codec_cfg;
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC;
audio->miscdevice = &audio_aac_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_aac_ws_mgr;
aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_MPEG4_AAC);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open AAC decoder, expected frames is always 1
* audio->buf_cfg.frames_per_buf = 0x01;
*/
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_aac_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_aac_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_aac_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_aac_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_aac",
.fops = &audio_aac_fops,
};
static int __init audio_aac_init(void)
{
int ret = misc_register(&audio_aac_misc);
if (ret == 0)
device_init_wakeup(audio_aac_misc.this_device, true);
audio_aac_ws_mgr.ref_cnt = 0;
mutex_init(&audio_aac_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_aac_init);

Melihat File

@@ -0,0 +1,435 @@
/* Copyright (c) 2015-2017, 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/types.h>
#include <linux/msm_audio_alac.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_alac_misc;
static struct ws_mgr audio_alac_ws_mgr;
static const struct file_operations audio_alac_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
static struct dentry *config_debugfs_create_file(const char *name, void *data)
{
return debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)data, &audio_alac_debug_fops);
}
static int alac_channel_map(u8 *channel_mapping, uint32_t channels);
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_alac_cfg alac_cfg;
struct msm_audio_alac_config *alac_config;
u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
if (alac_channel_map(channel_mapping,
audio->pcm_cfg.channel_count)) {
pr_err("%s: setting channel map failed %d\n",
__func__, audio->pcm_cfg.channel_count);
}
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count,
16, /*bits per sample*/
false, false, channel_mapping);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
alac_cfg.frame_length = alac_config->frameLength;
alac_cfg.compatible_version = alac_config->compatVersion;
alac_cfg.bit_depth = alac_config->bitDepth;
alac_cfg.pb = alac_config->pb;
alac_cfg.mb = alac_config->mb;
alac_cfg.kb = alac_config->kb;
alac_cfg.num_channels = alac_config->channelCount;
alac_cfg.max_run = alac_config->maxRun;
alac_cfg.max_frame_bytes = alac_config->maxSize;
alac_cfg.avg_bit_rate = alac_config->averageBitRate;
alac_cfg.sample_rate = alac_config->sampleRate;
alac_cfg.channel_layout_tag = alac_config->channelLayout;
pr_debug("%s: frame_length %d compatible_version %d bit_depth %d pb %d mb %d kb %d num_channels %d max_run %d max_frame_bytes %d avg_bit_rate %d sample_rate %d channel_layout_tag %d\n",
__func__, alac_config->frameLength,
alac_config->compatVersion,
alac_config->bitDepth, alac_config->pb,
alac_config->mb, alac_config->kb,
alac_config->channelCount, alac_config->maxRun,
alac_config->maxSize,
alac_config->averageBitRate,
alac_config->sampleRate,
alac_config->channelLayout);
/* Configure Media format block */
rc = q6asm_media_format_block_alac(audio->ac, &alac_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_ALAC_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_alac_config))) {
pr_err("%s:copy_to_user for AUDIO_GET_ALAC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_ALAC_CONFIG: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_alac_config))) {
pr_err("%s:copy_from_user for AUDIO_SET_ALAC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
default: {
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_alac_config_32 {
u32 frameLength;
u8 compatVersion;
u8 bitDepth;
u8 pb;
u8 mb;
u8 kb;
u8 channelCount;
u16 maxRun;
u32 maxSize;
u32 averageBitRate;
u32 sampleRate;
u32 channelLayout;
};
enum {
AUDIO_GET_ALAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config_32),
AUDIO_SET_ALAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_ALAC_CONFIG_32: {
struct msm_audio_alac_config *alac_config;
struct msm_audio_alac_config_32 alac_config_32;
memset(&alac_config_32, 0, sizeof(alac_config_32));
alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
alac_config_32.frameLength = alac_config->frameLength;
alac_config_32.compatVersion =
alac_config->compatVersion;
alac_config_32.bitDepth = alac_config->bitDepth;
alac_config_32.pb = alac_config->pb;
alac_config_32.mb = alac_config->mb;
alac_config_32.kb = alac_config->kb;
alac_config_32.channelCount = alac_config->channelCount;
alac_config_32.maxRun = alac_config->maxRun;
alac_config_32.maxSize = alac_config->maxSize;
alac_config_32.averageBitRate = alac_config->averageBitRate;
alac_config_32.sampleRate = alac_config->sampleRate;
alac_config_32.channelLayout = alac_config->channelLayout;
if (copy_to_user((void *)arg, &alac_config_32,
sizeof(alac_config_32))) {
pr_err("%s: copy_to_user for GET_ALAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_ALAC_CONFIG_32: {
struct msm_audio_alac_config *alac_config;
struct msm_audio_alac_config_32 alac_config_32;
if (copy_from_user(&alac_config_32, (void *)arg,
sizeof(alac_config_32))) {
pr_err("%s: copy_from_user for SET_ALAC_CONFIG_32 failed\n"
, __func__);
rc = -EFAULT;
break;
}
alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
alac_config->frameLength = alac_config_32.frameLength;
alac_config->compatVersion =
alac_config_32.compatVersion;
alac_config->bitDepth = alac_config_32.bitDepth;
alac_config->pb = alac_config_32.pb;
alac_config->mb = alac_config_32.mb;
alac_config->kb = alac_config_32.kb;
alac_config->channelCount = alac_config_32.channelCount;
alac_config->maxRun = alac_config_32.maxRun;
alac_config->maxSize = alac_config_32.maxSize;
alac_config->averageBitRate = alac_config_32.averageBitRate;
alac_config->sampleRate = alac_config_32.sampleRate;
alac_config->channelLayout = alac_config_32.channelLayout;
break;
}
default: {
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_alac_" + 5];
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (!audio)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config),
GFP_KERNEL);
if (!audio->codec_cfg) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_alac_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_alac_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_ALAC);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open ALAC decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_ALAC);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
snprintf(name, sizeof(name), "msm_alac_%04x", audio->ac->session);
audio->dentry = config_debugfs_create_file(name, (void *)audio);
if (IS_ERR_OR_NULL(audio->dentry))
pr_debug("debugfs_create_file failed\n");
pr_debug("%s:alacdec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static int alac_channel_map(u8 *channel_mapping, uint32_t channels)
{
u8 *lchannel_mapping;
lchannel_mapping = channel_mapping;
pr_debug("%s: channels passed: %d\n", __func__, channels);
if (channels == 1) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
} else if (channels == 2) {
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
} else if (channels == 3) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
} else if (channels == 4) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_CS;
} else if (channels == 5) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
} else if (channels == 6) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_LFE;
} else if (channels == 7) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_CS;
lchannel_mapping[6] = PCM_CHANNEL_LFE;
} else if (channels == 8) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FLC;
lchannel_mapping[2] = PCM_CHANNEL_FRC;
lchannel_mapping[3] = PCM_CHANNEL_FL;
lchannel_mapping[4] = PCM_CHANNEL_FR;
lchannel_mapping[5] = PCM_CHANNEL_LS;
lchannel_mapping[6] = PCM_CHANNEL_RS;
lchannel_mapping[7] = PCM_CHANNEL_LFE;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n",
__func__, channels);
return -EINVAL;
}
return 0;
}
static const struct file_operations audio_alac_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_alac_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_alac",
.fops = &audio_alac_fops,
};
static int __init audio_alac_init(void)
{
int ret = misc_register(&audio_alac_misc);
if (ret == 0)
device_init_wakeup(audio_alac_misc.this_device, true);
audio_alac_ws_mgr.ref_cnt = 0;
mutex_init(&audio_alac_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_alac_init);

Melihat File

@@ -0,0 +1,226 @@
/* amrnb audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/types.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_amrnb_misc;
static struct ws_mgr audio_amrnb_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_amrnb_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
}
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s: pcm output block config failed rc=%d\n",
__func__, rc);
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s: Audio Start procedure failed rc=%d\n",
__func__, rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
}
return rc;
}
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_amrnb_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_amrnb_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_amrnb_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_AMRNB);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_AMRNB);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_amrnb_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_amrnb_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio);
return rc;
}
static const struct file_operations audio_amrnb_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl,
};
static struct miscdevice audio_amrnb_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_amrnb",
.fops = &audio_amrnb_fops,
};
static int __init audio_amrnb_init(void)
{
int ret = misc_register(&audio_amrnb_misc);
if (ret == 0)
device_init_wakeup(audio_amrnb_misc.this_device, true);
audio_amrnb_ws_mgr.ref_cnt = 0;
mutex_init(&audio_amrnb_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_amrnb_init);

Melihat File

@@ -0,0 +1,231 @@
/* amrwb audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/compat.h>
#include <linux/types.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_amrwb_misc;
static struct ws_mgr audio_amrwb_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_amrwb_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
}
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s: pcm output block config failed rc=%d\n",
__func__, rc);
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s: Audio Start procedure failed rc=%d\n",
__func__, rc);
break;
}
pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
}
return rc;
}
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_amrwb_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_amrwb_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_amrwb_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_AMRWB);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_AMRWB);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_amrwb_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_amrwb_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio);
return rc;
}
static const struct file_operations audio_amrwb_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl,
};
static struct miscdevice audio_amrwb_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_amrwb",
.fops = &audio_amrwb_fops,
};
static int __init audio_amrwb_init(void)
{
int ret = misc_register(&audio_amrwb_misc);
if (ret == 0)
device_init_wakeup(audio_amrwb_misc.this_device, true);
audio_amrwb_ws_mgr.ref_cnt = 0;
mutex_init(&audio_amrwb_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_amrwb_init);

Melihat File

@@ -0,0 +1,397 @@
/* amr-wbplus audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/msm_audio_amrwbplus.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_amrwbplus_misc;
static struct ws_mgr audio_amrwbplus_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_amrwbplus_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
static void config_debug_fs(struct q6audio_aio *audio)
{
if (audio != NULL) {
char name[sizeof("msm_amrwbplus_") + 5];
snprintf(name, sizeof(name), "msm_amrwbplus_%04x",
audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_amrwbplus_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
}
}
#else
static void config_debug_fs(struct q6audio_aio *audio)
{
}
#endif
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct asm_amrwbplus_cfg q6_amrwbplus_cfg;
struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config;
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
pr_err("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
amrwbplus_drv_config =
(struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg;
q6_amrwbplus_cfg.size_bytes =
amrwbplus_drv_config->size_bytes;
q6_amrwbplus_cfg.version =
amrwbplus_drv_config->version;
q6_amrwbplus_cfg.num_channels =
amrwbplus_drv_config->num_channels;
q6_amrwbplus_cfg.amr_band_mode =
amrwbplus_drv_config->amr_band_mode;
q6_amrwbplus_cfg.amr_dtx_mode =
amrwbplus_drv_config->amr_dtx_mode;
q6_amrwbplus_cfg.amr_frame_fmt =
amrwbplus_drv_config->amr_frame_fmt;
q6_amrwbplus_cfg.amr_lsf_idx =
amrwbplus_drv_config->amr_lsf_idx;
rc = q6asm_media_format_block_amrwbplus(audio->ac,
&q6_amrwbplus_cfg);
if (rc < 0) {
pr_err("q6asm_media_format_block_amrwb+ failed...\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AMRWBPLUS_CONFIG_V2: {
if ((audio) && (arg) && (audio->codec_cfg)) {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_amrwbplus_config_v2))) {
rc = -EFAULT;
pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2 failed\n",
__func__);
break;
}
} else {
pr_err("%s: wb+ config v2 invalid parameters\n"
, __func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AMRWBPLUS_CONFIG_V2: {
if ((audio) && (arg) && (audio->codec_cfg)) {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_amrwbplus_config_v2))) {
rc = -EFAULT;
pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2 failed\n",
__func__);
break;
}
} else {
pr_err("%s: wb+ config invalid parameters\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_amrwbplus_config_v2_32 {
u32 size_bytes;
u32 version;
u32 num_channels;
u32 amr_band_mode;
u32 amr_dtx_mode;
u32 amr_frame_fmt;
u32 amr_lsf_idx;
};
enum {
AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+2),
struct msm_audio_amrwbplus_config_v2_32),
AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+3),
struct msm_audio_amrwbplus_config_v2_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AMRWBPLUS_CONFIG_V2_32: {
if (audio && arg && (audio->codec_cfg)) {
struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
struct msm_audio_amrwbplus_config_v2_32
amrwbplus_config_32;
memset(&amrwbplus_config_32, 0,
sizeof(amrwbplus_config_32));
amrwbplus_config =
(struct msm_audio_amrwbplus_config_v2 *)
audio->codec_cfg;
amrwbplus_config_32.size_bytes =
amrwbplus_config->size_bytes;
amrwbplus_config_32.version =
amrwbplus_config->version;
amrwbplus_config_32.num_channels =
amrwbplus_config->num_channels;
amrwbplus_config_32.amr_band_mode =
amrwbplus_config->amr_band_mode;
amrwbplus_config_32.amr_dtx_mode =
amrwbplus_config->amr_dtx_mode;
amrwbplus_config_32.amr_frame_fmt =
amrwbplus_config->amr_frame_fmt;
amrwbplus_config_32.amr_lsf_idx =
amrwbplus_config->amr_lsf_idx;
if (copy_to_user((void *)arg, &amrwbplus_config_32,
sizeof(amrwbplus_config_32))) {
rc = -EFAULT;
pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 failed\n"
, __func__);
}
} else {
pr_err("%s: wb+ Get config v2 invalid parameters\n"
, __func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_AMRWBPLUS_CONFIG_V2_32: {
if ((audio) && (arg) && (audio->codec_cfg)) {
struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
struct msm_audio_amrwbplus_config_v2_32
amrwbplus_config_32;
if (copy_from_user(&amrwbplus_config_32, (void *)arg,
sizeof(struct msm_audio_amrwbplus_config_v2_32))) {
rc = -EFAULT;
pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 failed\n"
, __func__);
break;
}
amrwbplus_config =
(struct msm_audio_amrwbplus_config_v2 *)
audio->codec_cfg;
amrwbplus_config->size_bytes =
amrwbplus_config_32.size_bytes;
amrwbplus_config->version =
amrwbplus_config_32.version;
amrwbplus_config->num_channels =
amrwbplus_config_32.num_channels;
amrwbplus_config->amr_band_mode =
amrwbplus_config_32.amr_band_mode;
amrwbplus_config->amr_dtx_mode =
amrwbplus_config_32.amr_dtx_mode;
amrwbplus_config->amr_frame_fmt =
amrwbplus_config_32.amr_frame_fmt;
amrwbplus_config->amr_lsf_idx =
amrwbplus_config_32.amr_lsf_idx;
} else {
pr_err("%s: wb+ config invalid parameters\n",
__func__);
rc = -EFAULT;
}
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->codec_cfg =
kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_amrwbplus_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_amrwbplus_ws_mgr;
audio->ac =
q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_AMR_WB_PLUS);
if (rc < 0) {
pr_err("amrwbplus NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS);
if (rc < 0) {
pr_err("wb+ T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("audio_amrwbplus Not supported mode\n");
rc = -EACCES;
goto fail;
}
config_debug_fs(audio);
pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_amrwbplus_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_amrwbplus_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_amrwbplus",
.fops = &audio_amrwbplus_fops,
};
static int __init audio_amrwbplus_init(void)
{
int ret = misc_register(&audio_amrwbplus_misc);
if (ret == 0)
device_init_wakeup(audio_amrwbplus_misc.this_device, true);
audio_amrwbplus_ws_mgr.ref_cnt = 0;
mutex_init(&audio_amrwbplus_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_amrwbplus_init);

Melihat File

@@ -0,0 +1,359 @@
/* Copyright (c) 2015-2017, 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/types.h>
#include <linux/msm_audio_ape.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_ape_misc;
static struct ws_mgr audio_ape_ws_mgr;
static const struct file_operations audio_ape_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
static struct dentry *config_debugfs_create_file(const char *name, void *data)
{
return debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)data, &audio_ape_debug_fops);
}
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_ape_cfg ape_cfg;
struct msm_audio_ape_config *ape_config;
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
ape_cfg.compatible_version = ape_config->compatibleVersion;
ape_cfg.compression_level = ape_config->compressionLevel;
ape_cfg.format_flags = ape_config->formatFlags;
ape_cfg.blocks_per_frame = ape_config->blocksPerFrame;
ape_cfg.final_frame_blocks = ape_config->finalFrameBlocks;
ape_cfg.total_frames = ape_config->totalFrames;
ape_cfg.bits_per_sample = ape_config->bitsPerSample;
ape_cfg.num_channels = ape_config->numChannels;
ape_cfg.sample_rate = ape_config->sampleRate;
ape_cfg.seek_table_present = ape_config->seekTablePresent;
pr_debug("%s: compatibleVersion %d compressionLevel %d formatFlags %d blocksPerFrame %d finalFrameBlocks %d totalFrames %d bitsPerSample %d numChannels %d sampleRate %d seekTablePresent %d\n",
__func__, ape_config->compatibleVersion,
ape_config->compressionLevel,
ape_config->formatFlags,
ape_config->blocksPerFrame,
ape_config->finalFrameBlocks,
ape_config->totalFrames,
ape_config->bitsPerSample,
ape_config->numChannels,
ape_config->sampleRate,
ape_config->seekTablePresent);
/* Configure Media format block */
rc = q6asm_media_format_block_ape(audio->ac, &ape_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_APE_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_ape_config))) {
pr_err("%s:copy_to_user for AUDIO_GET_APE_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_APE_CONFIG: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_ape_config))) {
pr_err("%s:copy_from_user for AUDIO_SET_APE_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_ape_config_32 {
u16 compatibleVersion;
u16 compressionLevel;
u32 formatFlags;
u32 blocksPerFrame;
u32 finalFrameBlocks;
u32 totalFrames;
u16 bitsPerSample;
u16 numChannels;
u32 sampleRate;
u32 seekTablePresent;
};
enum {
AUDIO_GET_APE_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config_32),
AUDIO_SET_APE_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_APE_CONFIG_32: {
struct msm_audio_ape_config *ape_config;
struct msm_audio_ape_config_32 ape_config_32;
memset(&ape_config_32, 0, sizeof(ape_config_32));
ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
ape_config_32.compatibleVersion = ape_config->compatibleVersion;
ape_config_32.compressionLevel =
ape_config->compressionLevel;
ape_config_32.formatFlags = ape_config->formatFlags;
ape_config_32.blocksPerFrame = ape_config->blocksPerFrame;
ape_config_32.finalFrameBlocks = ape_config->finalFrameBlocks;
ape_config_32.totalFrames = ape_config->totalFrames;
ape_config_32.bitsPerSample = ape_config->bitsPerSample;
ape_config_32.numChannels = ape_config->numChannels;
ape_config_32.sampleRate = ape_config->sampleRate;
ape_config_32.seekTablePresent = ape_config->seekTablePresent;
if (copy_to_user((void *)arg, &ape_config_32,
sizeof(ape_config_32))) {
pr_err("%s: copy_to_user for GET_APE_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_APE_CONFIG_32: {
struct msm_audio_ape_config *ape_config;
struct msm_audio_ape_config_32 ape_config_32;
if (copy_from_user(&ape_config_32, (void *)arg,
sizeof(ape_config_32))) {
pr_err("%s: copy_from_user for SET_APE_CONFIG_32 failed\n"
, __func__);
rc = -EFAULT;
break;
}
ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
ape_config->compatibleVersion = ape_config_32.compatibleVersion;
ape_config->compressionLevel =
ape_config_32.compressionLevel;
ape_config->formatFlags = ape_config_32.formatFlags;
ape_config->blocksPerFrame = ape_config_32.blocksPerFrame;
ape_config->finalFrameBlocks = ape_config_32.finalFrameBlocks;
ape_config->totalFrames = ape_config_32.totalFrames;
ape_config->bitsPerSample = ape_config_32.bitsPerSample;
ape_config->numChannels = ape_config_32.numChannels;
ape_config->sampleRate = ape_config_32.sampleRate;
ape_config->seekTablePresent = ape_config_32.seekTablePresent;
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_ape_" + 5];
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (!audio)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config),
GFP_KERNEL);
if (!audio->codec_cfg) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_ape_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_ape_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_APE);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open APE decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_APE);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
snprintf(name, sizeof(name), "msm_ape_%04x", audio->ac->session);
audio->dentry = config_debugfs_create_file(name, (void *)audio);
if (IS_ERR_OR_NULL(audio->dentry))
pr_debug("debugfs_create_file failed\n");
pr_debug("%s:apedec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_ape_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_ape_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_ape",
.fops = &audio_ape_fops,
};
static int __init audio_ape_init(void)
{
int ret = misc_register(&audio_ape_misc);
if (ret == 0)
device_init_wakeup(audio_ape_misc.this_device, true);
audio_ape_ws_mgr.ref_cnt = 0;
mutex_init(&audio_ape_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_ape_init);

Melihat File

@@ -0,0 +1,184 @@
/* evrc audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 "audio_utils_aio.h"
static struct miscdevice audio_evrc_misc;
static struct ws_mgr audio_evrc_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_evrc_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
}
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_evrc_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_evrc_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_evrc_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_EVRC);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_EVRC);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_evrc_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_evrc_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio);
return rc;
}
static const struct file_operations audio_evrc_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
};
static struct miscdevice audio_evrc_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_evrc",
.fops = &audio_evrc_fops,
};
static int __init audio_evrc_init(void)
{
int ret = misc_register(&audio_evrc_misc);
if (ret == 0)
device_init_wakeup(audio_evrc_misc.this_device, true);
audio_evrc_ws_mgr.ref_cnt = 0;
mutex_init(&audio_evrc_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_evrc_init);

Melihat File

@@ -0,0 +1,396 @@
/* Copyright (c) 2016-2017, 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/types.h>
#include <linux/msm_audio_g711_dec.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_g711alaw_misc;
static struct ws_mgr audio_g711_ws_mgr;
static const struct file_operations audio_g711_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
static struct dentry *config_debugfs_create_file(const char *name, void *data)
{
return debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)data, &audio_g711_debug_fops);
}
static int g711_channel_map(u8 *channel_mapping, uint32_t channels);
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_g711_dec_cfg g711_dec_cfg;
struct msm_audio_g711_dec_config *g711_dec_config;
u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg));
if (g711_channel_map(channel_mapping,
audio->pcm_cfg.channel_count)) {
pr_err("%s: setting channel map failed %d\n",
__func__, audio->pcm_cfg.channel_count);
}
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count,
16, /*bits per sample*/
false, false, channel_mapping);
if (rc < 0) {
pr_err("%s: pcm output block config failed rc=%d\n",
__func__, rc);
break;
}
}
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_cfg.sample_rate = g711_dec_config->sample_rate;
/* Configure Media format block */
rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("%s: cmd media format block failed rc=%d\n",
__func__, rc);
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s: Audio Start procedure failed rc=%d\n",
__func__, rc);
break;
}
pr_debug("%s: AUDIO_START success enable[%d]\n",
__func__, audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_G711_DEC_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_g711_dec_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_DEC_CONFIG: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_g711_dec_config))) {
pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
default: {
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n",
__func__, rc, cmd);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_g711_dec_config_32 {
u32 sample_rate;
};
enum {
AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32),
AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_G711_DEC_CONFIG_32: {
struct msm_audio_g711_dec_config *g711_dec_config;
struct msm_audio_g711_dec_config_32 g711_dec_config_32;
memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_config_32.sample_rate = g711_dec_config->sample_rate;
if (copy_to_user((void *)arg, &g711_dec_config_32,
sizeof(g711_dec_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_DEC_CONFIG_32: {
struct msm_audio_g711_dec_config *g711_dec_config;
struct msm_audio_g711_dec_config_32 g711_dec_config_32;
memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
if (copy_from_user(&g711_dec_config_32, (void *)arg,
sizeof(g711_dec_config_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_config->sample_rate = g711_dec_config_32.sample_rate;
break;
}
default: {
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n",
__func__, rc, cmd);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_g711_" + 5];
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (!audio)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config),
GFP_KERNEL);
if (!audio->codec_cfg) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_g711alaw_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_g711_ws_mgr;
init_waitqueue_head(&audio->event_wait);
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */ /*foramt:G711_ALAW*/
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_G711_ALAW_FS);
if (rc < 0) {
pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc);
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open G711 decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_G711_ALAW_FS);
if (rc < 0) {
pr_err("%s: T mode Open failed rc=%d\n", __func__, rc);
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("%s: %d mode is not supported mode\n",
__func__, file->f_mode);
rc = -EACCES;
goto fail;
}
snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session);
audio->dentry = config_debugfs_create_file(name, (void *)audio);
if (IS_ERR_OR_NULL(audio->dentry))
pr_debug("%s: debugfs_create_file failed\n", __func__);
pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static int g711_channel_map(u8 *channel_mapping, uint32_t channels)
{
u8 *lchannel_mapping;
lchannel_mapping = channel_mapping;
pr_debug("%s: channels passed: %d\n", __func__, channels);
if (channels == 1) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
} else if (channels == 2) {
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
} else if (channels == 3) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
} else if (channels == 4) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_CS;
} else if (channels == 5) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
} else if (channels == 6) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_LFE;
} else if (channels == 7) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_CS;
lchannel_mapping[6] = PCM_CHANNEL_LFE;
} else if (channels == 8) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FLC;
lchannel_mapping[2] = PCM_CHANNEL_FRC;
lchannel_mapping[3] = PCM_CHANNEL_FL;
lchannel_mapping[4] = PCM_CHANNEL_FR;
lchannel_mapping[5] = PCM_CHANNEL_LS;
lchannel_mapping[6] = PCM_CHANNEL_RS;
lchannel_mapping[7] = PCM_CHANNEL_LFE;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n",
__func__, channels);
return -EINVAL;
}
return 0;
}
static const struct file_operations audio_g711_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.compat_ioctl = audio_compat_ioctl,
.fsync = audio_aio_fsync,
};
static struct miscdevice audio_g711alaw_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_g711alaw",
.fops = &audio_g711_fops,
};
static int __init audio_g711alaw_init(void)
{
int ret = misc_register(&audio_g711alaw_misc);
if (ret == 0)
device_init_wakeup(audio_g711alaw_misc.this_device, true);
audio_g711_ws_mgr.ref_cnt = 0;
mutex_init(&audio_g711_ws_mgr.ws_lock);
return ret;
}
static void __exit audio_g711alaw_exit(void)
{
misc_deregister(&audio_g711alaw_misc);
mutex_destroy(&audio_g711_ws_mgr.ws_lock);
}
device_initcall(audio_g711alaw_init);
__exitcall(audio_g711alaw_exit);

Melihat File

@@ -0,0 +1,396 @@
/* Copyright (c) 2016-2017, 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/types.h>
#include <linux/msm_audio_g711_dec.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_g711mlaw_misc;
static struct ws_mgr audio_g711_ws_mgr;
static const struct file_operations audio_g711_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
static struct dentry *config_debugfs_create_file(const char *name, void *data)
{
return debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)data, &audio_g711_debug_fops);
}
static int g711_channel_map(u8 *channel_mapping, uint32_t channels);
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_g711_dec_cfg g711_dec_cfg;
struct msm_audio_g711_dec_config *g711_dec_config;
u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg));
if (g711_channel_map(channel_mapping,
audio->pcm_cfg.channel_count)) {
pr_err("%s: setting channel map failed %d\n",
__func__, audio->pcm_cfg.channel_count);
}
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count,
16, /*bits per sample*/
false, false, channel_mapping);
if (rc < 0) {
pr_err("%s: pcm output block config failed rc=%d\n",
__func__, rc);
break;
}
}
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_cfg.sample_rate = g711_dec_config->sample_rate;
/* Configure Media format block */
rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("%s: cmd media format block failed rc=%d\n",
__func__, rc);
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s: Audio Start procedure failed rc=%d\n",
__func__, rc);
break;
}
pr_debug("%s: AUDIO_START success enable[%d]\n",
__func__, audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_G711_DEC_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_g711_dec_config))) {
pr_err("%s: AUDIO_GET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_DEC_CONFIG: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_g711_dec_config))) {
pr_err("%s: AUDIO_SET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
default: {
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n",
__func__, rc, cmd);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_g711_dec_config_32 {
u32 sample_rate;
};
enum {
AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32),
AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_G711_DEC_CONFIG_32: {
struct msm_audio_g711_dec_config *g711_dec_config;
struct msm_audio_g711_dec_config_32 g711_dec_config_32;
memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_config_32.sample_rate = g711_dec_config->sample_rate;
if (copy_to_user((void *)arg, &g711_dec_config_32,
sizeof(g711_dec_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_DEC_CONFIG_32: {
struct msm_audio_g711_dec_config *g711_dec_config;
struct msm_audio_g711_dec_config_32 g711_dec_config_32;
memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
if (copy_from_user(&g711_dec_config_32, (void *)arg,
sizeof(g711_dec_config_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
g711_dec_config =
(struct msm_audio_g711_dec_config *)audio->codec_cfg;
g711_dec_config->sample_rate = g711_dec_config_32.sample_rate;
break;
}
default: {
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n",
__func__, rc, cmd);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_g711_" + 5];
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (!audio)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config),
GFP_KERNEL);
if (!audio->codec_cfg) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_g711mlaw_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_g711_ws_mgr;
init_waitqueue_head(&audio->event_wait);
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */ /*foramt:G711_ALAW*/
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_G711_MLAW_FS);
if (rc < 0) {
pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc);
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open G711 decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_G711_MLAW_FS);
if (rc < 0) {
pr_err("%s: T mode Open failed rc=%d\n", __func__, rc);
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("%s: %d mode is not supported\n", __func__,
file->f_mode);
rc = -EACCES;
goto fail;
}
snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session);
audio->dentry = config_debugfs_create_file(name, (void *)audio);
if (IS_ERR_OR_NULL(audio->dentry))
pr_debug("%s: debugfs_create_file failed\n", __func__);
pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static int g711_channel_map(u8 *channel_mapping, uint32_t channels)
{
u8 *lchannel_mapping;
lchannel_mapping = channel_mapping;
pr_debug("%s: channels passed: %d\n", __func__, channels);
if (channels == 1) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
} else if (channels == 2) {
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
} else if (channels == 3) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
} else if (channels == 4) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_CS;
} else if (channels == 5) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
} else if (channels == 6) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_LFE;
} else if (channels == 7) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FL;
lchannel_mapping[2] = PCM_CHANNEL_FR;
lchannel_mapping[3] = PCM_CHANNEL_LS;
lchannel_mapping[4] = PCM_CHANNEL_RS;
lchannel_mapping[5] = PCM_CHANNEL_CS;
lchannel_mapping[6] = PCM_CHANNEL_LFE;
} else if (channels == 8) {
lchannel_mapping[0] = PCM_CHANNEL_FC;
lchannel_mapping[1] = PCM_CHANNEL_FLC;
lchannel_mapping[2] = PCM_CHANNEL_FRC;
lchannel_mapping[3] = PCM_CHANNEL_FL;
lchannel_mapping[4] = PCM_CHANNEL_FR;
lchannel_mapping[5] = PCM_CHANNEL_LS;
lchannel_mapping[6] = PCM_CHANNEL_RS;
lchannel_mapping[7] = PCM_CHANNEL_LFE;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n",
__func__, channels);
return -EINVAL;
}
return 0;
}
static const struct file_operations audio_g711_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.compat_ioctl = audio_compat_ioctl,
.fsync = audio_aio_fsync,
};
static struct miscdevice audio_g711mlaw_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_g711mlaw",
.fops = &audio_g711_fops,
};
static int __init audio_g711mlaw_init(void)
{
int ret = misc_register(&audio_g711mlaw_misc);
if (ret == 0)
device_init_wakeup(audio_g711mlaw_misc.this_device, true);
audio_g711_ws_mgr.ref_cnt = 0;
mutex_init(&audio_g711_ws_mgr.ws_lock);
return ret;
}
static void __exit audio_g711mlaw_exit(void)
{
misc_deregister(&audio_g711mlaw_misc);
mutex_destroy(&audio_g711_ws_mgr.ws_lock);
}
device_initcall(audio_g711mlaw_init);
__exitcall(audio_g711mlaw_exit);

Melihat File

@@ -0,0 +1,778 @@
/*
* Copyright (c) 2014-2017, 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/msm_audio.h>
#include <linux/compat.h>
#include "q6audio_common.h"
#include "audio_utils_aio.h"
#include <sound/msm-audio-effects-q6-v2.h>
#define MAX_CHANNELS_SUPPORTED 8
#define WAIT_TIMEDOUT_DURATION_SECS 1
struct q6audio_effects {
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
struct audio_client *ac;
struct msm_hwacc_effects_config config;
struct mutex lock;
atomic_t in_count;
atomic_t out_count;
int opened;
int started;
int buf_alloc;
struct msm_nt_eff_all_config audio_effects;
};
static void audio_effects_init_pp(struct audio_client *ac)
{
int ret = 0;
struct asm_softvolume_params softvol = {
.period = SOFT_VOLUME_PERIOD,
.step = SOFT_VOLUME_STEP,
.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
};
if (!ac) {
pr_err("%s: audio client null to init pp\n", __func__);
return;
}
ret = q6asm_set_softvolume_v2(ac, &softvol,
SOFT_VOLUME_INSTANCE_1);
if (ret < 0)
pr_err("%s: Send SoftVolume Param failed ret=%d\n",
__func__, ret);
}
static void audio_effects_deinit_pp(struct audio_client *ac)
{
if (!ac) {
pr_err("%s: audio client null to deinit pp\n", __func__);
return;
}
}
static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
struct q6audio_effects *effects;
if (!payload || !priv) {
pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n",
__func__, payload, priv);
return;
}
effects = (struct q6audio_effects *)priv;
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2: {
atomic_inc(&effects->out_count);
wake_up(&effects->write_wait);
break;
}
case ASM_DATA_EVENT_READ_DONE_V2: {
atomic_inc(&effects->in_count);
wake_up(&effects->read_wait);
break;
}
case APR_BASIC_RSP_RESULT: {
pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n",
__func__, payload[0], payload[1]);
switch (payload[0]) {
case ASM_SESSION_CMD_RUN_V2:
pr_debug("ASM_SESSION_CMD_RUN_V2\n");
break;
default:
pr_debug("%s: Payload = [0x%x] stat[0x%x]\n",
__func__, payload[0], payload[1]);
break;
}
break;
}
default:
pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n",
__func__, opcode, token);
break;
}
}
static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s: AUDIO_START\n", __func__);
mutex_lock(&effects->lock);
rc = q6asm_open_read_write_v2(effects->ac,
FORMAT_LINEAR_PCM,
FORMAT_MULTI_CHANNEL_LINEAR_PCM,
effects->config.meta_mode_enabled,
effects->config.output.bits_per_sample,
true /*overwrite topology*/,
ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER);
if (rc < 0) {
pr_err("%s: Open failed for hw accelerated effects:rc=%d\n",
__func__, rc);
rc = -EINVAL;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
effects->opened = 1;
pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.input.buf_size,
effects->config.input.num_buf);
rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac,
effects->config.output.buf_size,
effects->config.output.num_buf);
if (rc < 0) {
pr_err("%s: Write buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_set(&effects->in_count, effects->config.input.num_buf);
rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac,
effects->config.input.buf_size,
effects->config.input.num_buf);
if (rc < 0) {
pr_err("%s: Read buffer Allocation failed rc = %d\n",
__func__, rc);
rc = -ENOMEM;
mutex_unlock(&effects->lock);
goto readbuf_fail;
}
atomic_set(&effects->out_count, effects->config.output.num_buf);
effects->buf_alloc = 1;
pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n",
__func__, effects->config.input.sample_rate,
effects->config.input.num_channels);
rc = q6asm_enc_cfg_blk_pcm(effects->ac,
effects->config.input.sample_rate,
effects->config.input.num_channels);
if (rc < 0) {
pr_err("%s: pcm read block config failed\n", __func__);
rc = -EINVAL;
mutex_unlock(&effects->lock);
goto cfg_fail;
}
pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n",
__func__, effects->config.output.sample_rate,
effects->config.output.num_channels,
effects->config.output.bits_per_sample);
rc = q6asm_media_format_block_pcm_format_support(
effects->ac, effects->config.output.sample_rate,
effects->config.output.num_channels,
effects->config.output.bits_per_sample);
if (rc < 0) {
pr_err("%s: pcm write format block config failed\n",
__func__);
rc = -EINVAL;
mutex_unlock(&effects->lock);
goto cfg_fail;
}
audio_effects_init_pp(effects->ac);
rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00);
if (!rc)
effects->started = 1;
else {
effects->started = 0;
pr_err("%s: ASM run state failed\n", __func__);
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_WRITE: {
char *bufptr = NULL;
uint32_t idx = 0;
uint32_t size = 0;
mutex_lock(&effects->lock);
if (!effects->started) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
rc = wait_event_timeout(effects->write_wait,
atomic_read(&effects->out_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc) {
pr_err("%s: write wait_event_timeout\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if (!atomic_read(&effects->out_count)) {
pr_err("%s: pcm stopped out_count 0\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx);
if (bufptr) {
if ((effects->config.buf_cfg.output_len > size) ||
copy_from_user(bufptr, (void *)arg,
effects->config.buf_cfg.output_len)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
rc = q6asm_write(effects->ac,
effects->config.buf_cfg.output_len,
0, 0, NO_TIMESTAMP);
if (rc < 0) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_dec(&effects->out_count);
} else {
pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n",
__func__);
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_READ: {
char *bufptr = NULL;
uint32_t idx = 0;
uint32_t size = 0;
mutex_lock(&effects->lock);
if (!effects->started) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
atomic_set(&effects->in_count, 0);
q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len);
/* Read might fail initially, don't error out */
if (rc < 0)
pr_err("%s: read failed\n", __func__);
rc = wait_event_timeout(effects->read_wait,
atomic_read(&effects->in_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc) {
pr_err("%s: read wait_event_timeout\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if (!atomic_read(&effects->in_count)) {
pr_err("%s: pcm stopped in_count 0\n", __func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx);
if (bufptr) {
if (!((void *)arg)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
if ((effects->config.buf_cfg.input_len > size) ||
copy_to_user((void *)arg, bufptr,
effects->config.buf_cfg.input_len)) {
rc = -EFAULT;
mutex_unlock(&effects->lock);
goto ioctl_fail;
}
}
mutex_unlock(&effects->lock);
break;
}
default:
pr_err("%s: Invalid effects config module\n", __func__);
rc = -EINVAL;
break;
}
ioctl_fail:
return rc;
readbuf_fail:
q6asm_audio_client_buf_free_contiguous(IN,
effects->ac);
return rc;
cfg_fail:
q6asm_audio_client_buf_free_contiguous(IN,
effects->ac);
q6asm_audio_client_buf_free_contiguous(OUT,
effects->ac);
effects->buf_alloc = 0;
return rc;
}
static long audio_effects_set_pp_param(struct q6audio_effects *effects,
long *values)
{
int rc = 0;
int effects_module = values[0];
switch (effects_module) {
case VIRTUALIZER_MODULE:
pr_debug("%s: VIRTUALIZER_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_virtualizer_handler(
effects->ac,
&(effects->audio_effects.virtualizer),
(long *)&values[1]);
break;
case REVERB_MODULE:
pr_debug("%s: REVERB_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_reverb_handler(effects->ac,
&(effects->audio_effects.reverb),
(long *)&values[1]);
break;
case BASS_BOOST_MODULE:
pr_debug("%s: BASS_BOOST_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_bass_boost_handler(
effects->ac,
&(effects->audio_effects.bass_boost),
(long *)&values[1]);
break;
case PBE_MODULE:
pr_debug("%s: PBE_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_pbe_handler(
effects->ac,
&(effects->audio_effects.pbe),
(long *)&values[1]);
break;
case EQ_MODULE:
pr_debug("%s: EQ_MODULE\n", __func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_popless_eq_handler(
effects->ac,
&(effects->audio_effects.equalizer),
(long *)&values[1]);
break;
case SOFT_VOLUME_MODULE:
pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__);
msm_audio_effects_volume_handler_v2(effects->ac,
&(effects->audio_effects.saplus_vol),
(long *)&values[1], SOFT_VOLUME_INSTANCE_1);
break;
case SOFT_VOLUME2_MODULE:
pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n",
__func__);
if (msm_audio_effects_is_effmodule_supp_in_top(
effects_module, effects->ac->topology))
msm_audio_effects_volume_handler_v2(effects->ac,
&(effects->audio_effects.topo_switch_vol),
(long *)&values[1], SOFT_VOLUME_INSTANCE_2);
break;
default:
pr_err("%s: Invalid effects config module\n", __func__);
rc = -EINVAL;
}
return rc;
}
static long audio_effects_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
long argvalues[MAX_PP_PARAMS_SZ] = {0};
switch (cmd) {
case AUDIO_SET_EFFECTS_CONFIG: {
pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__);
mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&effects->config, (void *)arg,
sizeof(effects->config))) {
pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.output.sample_rate,
effects->config.output.num_channels);
pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
__func__, effects->config.input.buf_size,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN: {
mutex_lock(&effects->lock);
if (copy_from_user(&effects->config.buf_cfg, (void *)arg,
sizeof(effects->config.buf_cfg))) {
pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
__func__);
rc = -EFAULT;
}
pr_debug("%s: write buf len: %d, read buf len: %d\n",
__func__, effects->config.buf_cfg.output_len,
effects->config.buf_cfg.input_len);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_GET_BUF_AVAIL: {
struct msm_hwacc_buf_avail buf_avail;
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
mutex_lock(&effects->lock);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
__func__, buf_avail.output_num_avail,
buf_avail.input_num_avail);
if (copy_to_user((void *)arg, &buf_avail,
sizeof(buf_avail))) {
pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
__func__);
rc = -EFAULT;
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS: {
mutex_lock(&effects->lock);
if (copy_from_user(argvalues, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(long))) {
pr_err("%s: copy from user for pp params failed\n",
__func__);
mutex_unlock(&effects->lock);
return -EFAULT;
}
rc = audio_effects_set_pp_param(effects, argvalues);
mutex_unlock(&effects->lock);
break;
}
default:
pr_debug("%s: Calling shared ioctl\n", __func__);
rc = audio_effects_shared_ioctl(file, cmd, arg);
break;
}
if (rc)
pr_err("%s: cmd 0x%x failed\n", __func__, cmd);
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_hwacc_data_config32 {
__u32 buf_size;
__u32 num_buf;
__u32 num_channels;
__u8 channel_map[MAX_CHANNELS_SUPPORTED];
__u32 sample_rate;
__u32 bits_per_sample;
};
struct msm_hwacc_buf_cfg32 {
__u32 input_len;
__u32 output_len;
};
struct msm_hwacc_buf_avail32 {
__u32 input_num_avail;
__u32 output_num_avail;
};
struct msm_hwacc_effects_config32 {
struct msm_hwacc_data_config32 input;
struct msm_hwacc_data_config32 output;
struct msm_hwacc_buf_cfg32 buf_cfg;
__u32 meta_mode_enabled;
__u32 overwrite_topology;
__s32 topology;
};
enum {
AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99,
struct msm_hwacc_effects_config32),
AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100,
struct msm_hwacc_buf_cfg32),
AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101,
struct msm_hwacc_buf_avail32),
AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t),
AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t),
AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104,
compat_uptr_t),
AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int),
};
static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0, i;
switch (cmd) {
case AUDIO_SET_EFFECTS_CONFIG32: {
struct msm_hwacc_effects_config32 config32;
struct msm_hwacc_effects_config *config = &effects->config;
mutex_lock(&effects->lock);
memset(&effects->config, 0, sizeof(effects->config));
if (copy_from_user(&config32, (void *)arg,
sizeof(config32))) {
pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n",
__func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
break;
}
config->input.buf_size = config32.input.buf_size;
config->input.num_buf = config32.input.num_buf;
config->input.num_channels = config32.input.num_channels;
config->input.sample_rate = config32.input.sample_rate;
config->input.bits_per_sample = config32.input.bits_per_sample;
config->input.buf_size = config32.input.buf_size;
for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
config->input.channel_map[i] =
config32.input.channel_map[i];
config->output.buf_size = config32.output.buf_size;
config->output.num_buf = config32.output.num_buf;
config->output.num_channels = config32.output.num_channels;
config->output.sample_rate = config32.output.sample_rate;
config->output.bits_per_sample =
config32.output.bits_per_sample;
config->output.buf_size = config32.output.buf_size;
for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
config->output.channel_map[i] =
config32.output.channel_map[i];
config->buf_cfg.input_len = config32.buf_cfg.input_len;
config->buf_cfg.output_len = config32.buf_cfg.output_len;
config->meta_mode_enabled = config32.meta_mode_enabled;
config->overwrite_topology = config32.overwrite_topology;
config->topology = config32.topology;
pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
__func__, effects->config.output.buf_size,
effects->config.output.num_buf,
effects->config.output.sample_rate,
effects->config.output.num_channels);
pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
__func__, effects->config.input.buf_size,
effects->config.input.num_buf,
effects->config.input.sample_rate,
effects->config.input.num_channels);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_BUF_LEN32: {
struct msm_hwacc_buf_cfg32 buf_cfg32;
struct msm_hwacc_effects_config *config = &effects->config;
mutex_lock(&effects->lock);
if (copy_from_user(&buf_cfg32, (void *)arg,
sizeof(buf_cfg32))) {
pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
__func__);
rc = -EFAULT;
mutex_unlock(&effects->lock);
break;
}
config->buf_cfg.input_len = buf_cfg32.input_len;
config->buf_cfg.output_len = buf_cfg32.output_len;
pr_debug("%s: write buf len: %d, read buf len: %d\n",
__func__, effects->config.buf_cfg.output_len,
effects->config.buf_cfg.input_len);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
struct msm_hwacc_buf_avail32 buf_avail;
memset(&buf_avail, 0, sizeof(buf_avail));
mutex_lock(&effects->lock);
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
__func__, buf_avail.output_num_avail,
buf_avail.input_num_avail);
if (copy_to_user((void *)arg, &buf_avail,
sizeof(buf_avail))) {
pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
__func__);
rc = -EFAULT;
}
mutex_unlock(&effects->lock);
break;
}
case AUDIO_EFFECTS_SET_PP_PARAMS32: {
long argvalues[MAX_PP_PARAMS_SZ] = {0};
int argvalues32[MAX_PP_PARAMS_SZ] = {0};
mutex_lock(&effects->lock);
if (copy_from_user(argvalues32, (void *)arg,
MAX_PP_PARAMS_SZ*sizeof(int))) {
pr_err("%s: copy from user failed for pp params\n",
__func__);
mutex_unlock(&effects->lock);
return -EFAULT;
}
for (i = 0; i < MAX_PP_PARAMS_SZ; i++)
argvalues[i] = argvalues32[i];
rc = audio_effects_set_pp_param(effects, argvalues);
mutex_unlock(&effects->lock);
break;
}
case AUDIO_START32: {
rc = audio_effects_shared_ioctl(file, AUDIO_START, arg);
break;
}
case AUDIO_EFFECTS_WRITE32: {
rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg);
break;
}
case AUDIO_EFFECTS_READ32: {
rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg);
break;
}
default:
pr_debug("%s: unhandled ioctl\n", __func__);
rc = -EINVAL;
break;
}
return rc;
}
#endif
static int audio_effects_release(struct inode *inode, struct file *file)
{
struct q6audio_effects *effects = file->private_data;
int rc = 0;
if (!effects) {
pr_err("%s: effect is NULL\n", __func__);
return -EINVAL;
}
if (effects->opened) {
rc = wait_event_timeout(effects->write_wait,
atomic_read(&effects->out_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc)
pr_err("%s: write wait_event_timeout failed\n",
__func__);
rc = wait_event_timeout(effects->read_wait,
atomic_read(&effects->in_count),
WAIT_TIMEDOUT_DURATION_SECS * HZ);
if (!rc)
pr_err("%s: read wait_event_timeout failed\n",
__func__);
rc = q6asm_cmd(effects->ac, CMD_CLOSE);
if (rc < 0)
pr_err("%s[%pK]:Failed to close the session rc=%d\n",
__func__, effects, rc);
effects->opened = 0;
effects->started = 0;
audio_effects_deinit_pp(effects->ac);
}
if (effects->buf_alloc) {
q6asm_audio_client_buf_free_contiguous(IN, effects->ac);
q6asm_audio_client_buf_free_contiguous(OUT, effects->ac);
}
q6asm_audio_client_free(effects->ac);
mutex_destroy(&effects->lock);
kfree(effects);
pr_debug("%s: close session success\n", __func__);
return rc;
}
static int audio_effects_open(struct inode *inode, struct file *file)
{
struct q6audio_effects *effects;
int rc = 0;
effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL);
if (!effects)
return -ENOMEM;
effects->ac = q6asm_audio_client_alloc(
(app_cb)audio_effects_event_handler,
(void *)effects);
if (!effects->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(effects);
return -ENOMEM;
}
init_waitqueue_head(&effects->read_wait);
init_waitqueue_head(&effects->write_wait);
mutex_init(&effects->lock);
effects->opened = 0;
effects->started = 0;
effects->buf_alloc = 0;
file->private_data = effects;
pr_debug("%s: open session success\n", __func__);
return rc;
}
static const struct file_operations audio_effects_fops = {
.owner = THIS_MODULE,
.open = audio_effects_open,
.release = audio_effects_release,
.unlocked_ioctl = audio_effects_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = audio_effects_compat_ioctl,
#endif
};
struct miscdevice audio_effects_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_hweffects",
.fops = &audio_effects_fops,
};
static int __init audio_effects_init(void)
{
return misc_register(&audio_effects_misc);
}
device_initcall(audio_effects_init);
MODULE_DESCRIPTION("Audio hardware accelerated effects driver");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -0,0 +1,188 @@
/* mp3 audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 "audio_utils_aio.h"
static struct miscdevice audio_mp3_misc;
static struct ws_mgr audio_mp3_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_mp3_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
rc = enable_volume_ramp(audio);
if (rc < 0) {
pr_err("%s: Failed to enable volume ramp\n",
__func__);
}
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
}
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_mp3_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_mp3_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_mp3_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_MP3);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open MP3 decoder, expected frames is always 1
* audio->buf_cfg.frames_per_buf = 0x01;
*/
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_MP3);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_mp3_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_mp3_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio);
return rc;
}
static const struct file_operations audio_mp3_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
};
static struct miscdevice audio_mp3_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_mp3",
.fops = &audio_mp3_fops,
};
static int __init audio_mp3_init(void)
{
int ret = misc_register(&audio_mp3_misc);
if (ret == 0)
device_init_wakeup(audio_mp3_misc.this_device, true);
audio_mp3_ws_mgr.ref_cnt = 0;
mutex_init(&audio_mp3_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_mp3_init);

Melihat File

@@ -0,0 +1,523 @@
/* aac audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/msm_audio_aac.h>
#include <linux/compat.h>
#include <soc/qcom/socinfo.h>
#include "audio_utils_aio.h"
#define AUDIO_AAC_DUAL_MONO_INVALID -1
/* Default number of pre-allocated event packets */
#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out))
static struct miscdevice audio_multiaac_misc;
static struct ws_mgr audio_multiaac_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_aac_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_aac_cfg aac_cfg;
struct msm_audio_aac_config *aac_config;
uint32_t sbr_ps = 0x00;
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
if (audio->feedback == TUNNEL_MODE) {
aac_cfg.sample_rate = aac_config->sample_rate;
aac_cfg.ch_cfg = aac_config->channel_configuration;
} else {
aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
}
pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm_native(audio->ac,
aac_cfg.sample_rate,
aac_cfg.ch_cfg);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
/* turn on both sbr and ps */
rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
if (rc < 0)
pr_err("sbr-ps enable failed\n");
if (aac_config->sbr_ps_on_flag)
aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
else if (aac_config->sbr_on_flag)
aac_cfg.aot = AAC_ENC_MODE_AAC_P;
else
aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
switch (aac_config->format) {
case AUDIO_AAC_FORMAT_ADTS:
aac_cfg.format = 0x00;
break;
case AUDIO_AAC_FORMAT_LOAS:
aac_cfg.format = 0x01;
break;
case AUDIO_AAC_FORMAT_ADIF:
aac_cfg.format = 0x02;
break;
default:
case AUDIO_AAC_FORMAT_RAW:
aac_cfg.format = 0x03;
}
aac_cfg.ep_config = aac_config->ep_config;
aac_cfg.section_data_resilience =
aac_config->aac_section_data_resilience_flag;
aac_cfg.scalefactor_data_resilience =
aac_config->aac_scalefactor_data_resilience_flag;
aac_cfg.spectral_data_resilience =
aac_config->aac_spectral_data_resilience_flag;
pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
__func__, aac_cfg.format,
aac_cfg.aot, aac_cfg.ch_cfg,
aac_cfg.sample_rate);
/* Configure Media format block */
rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = q6asm_set_encdec_chan_map(audio->ac, 2);
if (rc < 0) {
pr_err("%s: cmd set encdec_chan_map failed\n",
__func__);
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config *aac_config;
uint16_t sce_left = 1, sce_right = 2;
if (arg == NULL) {
pr_err("%s: NULL config pointer\n", __func__);
rc = -EINVAL;
break;
}
memcpy(audio->codec_cfg, arg,
sizeof(struct msm_audio_aac_config));
aac_config = audio->codec_cfg;
if (aac_config->dual_mono_mode >
AUDIO_AAC_DUAL_MONO_PL_SR) {
pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n",
__func__, aac_config->dual_mono_mode);
} else {
/* convert the data from user into sce_left
* and sce_right based on the definitions
*/
pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n",
__func__, aac_config->dual_mono_mode);
switch (aac_config->dual_mono_mode) {
case AUDIO_AAC_DUAL_MONO_PL_PR:
sce_left = 1;
sce_right = 1;
break;
case AUDIO_AAC_DUAL_MONO_SL_SR:
sce_left = 2;
sce_right = 2;
break;
case AUDIO_AAC_DUAL_MONO_SL_PR:
sce_left = 2;
sce_right = 1;
break;
case AUDIO_AAC_DUAL_MONO_PL_SR:
default:
sce_left = 1;
sce_right = 2;
break;
}
rc = q6asm_cfg_dual_mono_aac(audio->ac,
sce_left, sce_right);
if (rc < 0)
pr_err("%s: asm cmd dualmono failed rc=%d\n",
__func__, rc);
} break;
break;
}
case AUDIO_SET_AAC_MIX_CONFIG: {
u32 *mix_coeff = (u32 *)arg;
if (!arg) {
pr_err("%s: Invalid param for %s\n",
__func__, "AUDIO_SET_AAC_MIX_CONFIG");
rc = -EINVAL;
break;
}
pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
pr_debug("%s, value of coeff = %d",
__func__, *mix_coeff);
q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff);
if (rc < 0)
pr_err("%s asm aac_sel_mix_coef failed rc=%d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AAC_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_aac_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n"
, __func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG: {
struct msm_audio_aac_config aac_config;
if (copy_from_user(&aac_config, (void *)arg,
sizeof(aac_config))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n"
, __func__);
rc = -EFAULT;
}
rc = audio_ioctl_shared(file, cmd, &aac_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
case AUDIO_SET_AAC_MIX_CONFIG: {
u32 mix_config;
pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
if (copy_from_user(&mix_config, (void *)arg,
sizeof(u32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = audio_ioctl_shared(file, cmd, &mix_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default: {
pr_debug("Calling utils ioctl\n");
rc = audio->codec_ioctl(file, cmd, arg);
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_aac_config32 {
s16 format;
u16 audio_object;
u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
u16 aac_section_data_resilience_flag;
u16 aac_scalefactor_data_resilience_flag;
u16 aac_spectral_data_resilience_flag;
u16 sbr_on_flag;
u16 sbr_ps_on_flag;
u16 dual_mono_mode;
u16 channel_configuration;
u16 sample_rate;
};
enum {
AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_AAC_CONFIG_32: {
struct msm_audio_aac_config *aac_config;
struct msm_audio_aac_config32 aac_config_32;
memset(&aac_config_32, 0, sizeof(aac_config_32));
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
aac_config_32.format = aac_config->format;
aac_config_32.audio_object = aac_config->audio_object;
aac_config_32.ep_config = aac_config->ep_config;
aac_config_32.aac_section_data_resilience_flag =
aac_config->aac_section_data_resilience_flag;
aac_config_32.aac_scalefactor_data_resilience_flag =
aac_config->aac_scalefactor_data_resilience_flag;
aac_config_32.aac_spectral_data_resilience_flag =
aac_config->aac_spectral_data_resilience_flag;
aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
aac_config_32.channel_configuration =
aac_config->channel_configuration;
aac_config_32.sample_rate = aac_config->sample_rate;
if (copy_to_user((void *)arg, &aac_config_32,
sizeof(aac_config_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_AAC_CONFIG_32: {
struct msm_audio_aac_config aac_config;
struct msm_audio_aac_config32 aac_config_32;
pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
if (copy_from_user(&aac_config_32, (void *)arg,
sizeof(aac_config_32))) {
pr_err(
"%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed",
__func__);
rc = -EFAULT;
break;
}
aac_config.format = aac_config_32.format;
aac_config.audio_object = aac_config_32.audio_object;
aac_config.ep_config = aac_config_32.ep_config;
aac_config.aac_section_data_resilience_flag =
aac_config_32.aac_section_data_resilience_flag;
aac_config.aac_scalefactor_data_resilience_flag =
aac_config_32.aac_scalefactor_data_resilience_flag;
aac_config.aac_spectral_data_resilience_flag =
aac_config_32.aac_spectral_data_resilience_flag;
aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
aac_config.channel_configuration =
aac_config_32.channel_configuration;
aac_config.sample_rate = aac_config_32.sample_rate;
cmd = AUDIO_SET_AAC_CONFIG;
rc = audio_ioctl_shared(file, cmd, &aac_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
case AUDIO_SET_AAC_MIX_CONFIG: {
u32 mix_config;
pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG\n", __func__);
if (copy_from_user(&mix_config, (void *)arg,
sizeof(u32))) {
pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n"
, __func__);
rc = -EFAULT;
break;
}
rc = audio_ioctl_shared(file, cmd, &mix_config);
if (rc)
pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default: {
pr_debug("Calling utils ioctl\n");
rc = audio->codec_compat_ioctl(file, cmd, arg);
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
struct msm_audio_aac_config *aac_config = NULL;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_multi_aac_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
aac_config = audio->codec_cfg;
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM;
audio->miscdevice = &audio_multiaac_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_multiaac_ws_mgr;
aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_MPEG4_MULTI_AAC);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open AAC decoder, expected frames is always 1
* audio->buf_cfg.frames_per_buf = 0x01;
*/
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_multi_aac_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_aac_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n",
__func__, audio->feedback, audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_aac_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_multiaac_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_multi_aac",
.fops = &audio_aac_fops,
};
static int __init audio_aac_init(void)
{
int ret = misc_register(&audio_multiaac_misc);
if (ret == 0)
device_init_wakeup(audio_multiaac_misc.this_device, true);
audio_multiaac_ws_mgr.ref_cnt = 0;
mutex_init(&audio_multiaac_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_aac_init);

Melihat File

@@ -0,0 +1,191 @@
/* qcelp(v13k) audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 "audio_utils_aio.h"
#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in))
static struct miscdevice audio_qcelp_misc;
static struct ws_mgr audio_qcelp_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_qcelp_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
audio->ac->session,
audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
}
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_qcelp_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
audio->pcm_cfg.sample_rate = 8000;
audio->pcm_cfg.channel_count = 1;
audio->miscdevice = &audio_qcelp_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_qcelp_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_V13K);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_V13K);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_qcelp_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_qcelp_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio);
return rc;
}
static const struct file_operations audio_qcelp_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
};
static struct miscdevice audio_qcelp_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_qcelp",
.fops = &audio_qcelp_fops,
};
static int __init audio_qcelp_init(void)
{
int ret = misc_register(&audio_qcelp_misc);
if (ret == 0)
device_init_wakeup(audio_qcelp_misc.this_device, true);
audio_qcelp_ws_mgr.ref_cnt = 0;
mutex_init(&audio_qcelp_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_qcelp_init);

Melihat File

@@ -0,0 +1,954 @@
/* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/*
* Define maximum buffer size. Below values are chosen considering the higher
* values used among all native drivers.
*/
#define MAX_FRAME_SIZE 1536
#define MAX_FRAMES 5
#define META_SIZE (sizeof(struct meta_out_dsp))
#define MAX_BUFFER_SIZE (1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES))
static int audio_in_pause(struct q6audio_in *audio)
{
int rc;
rc = q6asm_cmd(audio->ac, CMD_PAUSE);
if (rc < 0)
pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
audio->ac->session, rc);
return rc;
}
static int audio_in_flush(struct q6audio_in *audio)
{
int rc;
pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
/* Flush if session running */
if (audio->enabled) {
/* Implicitly issue a pause to the encoder before flushing */
rc = audio_in_pause(audio);
if (rc < 0) {
pr_err("%s:session id %d: pause cmd failed rc=%d\n",
__func__, audio->ac->session, rc);
return rc;
}
rc = q6asm_cmd(audio->ac, CMD_FLUSH);
if (rc < 0) {
pr_err("%s:session id %d: flush cmd failed rc=%d\n",
__func__, audio->ac->session, rc);
return rc;
}
/* 2nd arg: 0 -> run immediately
* 3rd arg: 0 -> msw_ts,
* 4th arg: 0 ->lsw_ts
*/
q6asm_run(audio->ac, 0x00, 0x00, 0x00);
pr_debug("Rerun the session\n");
}
audio->rflush = 1;
audio->wflush = 1;
memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
wake_up(&audio->read_wait);
/* get read_lock to ensure no more waiting read thread */
mutex_lock(&audio->read_lock);
audio->rflush = 0;
mutex_unlock(&audio->read_lock);
wake_up(&audio->write_wait);
/* get write_lock to ensure no more waiting write thread */
mutex_lock(&audio->write_lock);
audio->wflush = 0;
mutex_unlock(&audio->write_lock);
pr_debug("%s:session id %d: in_bytes %d\n", __func__,
audio->ac->session, atomic_read(&audio->in_bytes));
pr_debug("%s:session id %d: in_samples %d\n", __func__,
audio->ac->session, atomic_read(&audio->in_samples));
atomic_set(&audio->in_bytes, 0);
atomic_set(&audio->in_samples, 0);
atomic_set(&audio->out_count, 0);
return 0;
}
/* must be called with audio->lock held */
int audio_in_enable(struct q6audio_in *audio)
{
if (audio->enabled)
return 0;
/* 2nd arg: 0 -> run immediately
* 3rd arg: 0 -> msw_ts,
* 4th arg: 0 ->lsw_ts
*/
return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
}
/* must be called with audio->lock held */
int audio_in_disable(struct q6audio_in *audio)
{
int rc = 0;
if (!audio->stopped) {
audio->enabled = 0;
audio->opened = 0;
pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
__func__, audio->ac->session,
atomic_read(&audio->in_bytes),
atomic_read(&audio->in_samples));
rc = q6asm_cmd(audio->ac, CMD_CLOSE);
if (rc < 0)
pr_err("%s:session id %d: Failed to close the session rc=%d\n",
__func__, audio->ac->session,
rc);
audio->stopped = 1;
memset(audio->out_frame_info, 0,
sizeof(audio->out_frame_info));
wake_up(&audio->read_wait);
wake_up(&audio->write_wait);
}
pr_debug("%s:session id %d: enabled[%d]\n", __func__,
audio->ac->session, audio->enabled);
return rc;
}
int audio_in_buf_alloc(struct q6audio_in *audio)
{
int rc = 0;
switch (audio->buf_alloc) {
case NO_BUF_ALLOC:
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_audio_client_buf_alloc(IN,
audio->ac,
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
pr_err("%s:session id %d: Buffer Alloc failed\n",
__func__,
audio->ac->session);
rc = -ENOMEM;
break;
}
audio->buf_alloc |= BUF_ALLOC_IN;
}
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
audio->str_cfg.buffer_count);
if (rc < 0) {
pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENOMEM;
break;
}
audio->buf_alloc |= BUF_ALLOC_OUT;
break;
case BUF_ALLOC_IN:
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
audio->str_cfg.buffer_count);
if (rc < 0) {
pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENOMEM;
break;
}
audio->buf_alloc |= BUF_ALLOC_OUT;
break;
case BUF_ALLOC_OUT:
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
pr_err("%s:session id %d: Buffer Alloc failed\n",
__func__,
audio->ac->session);
rc = -ENOMEM;
break;
}
audio->buf_alloc |= BUF_ALLOC_IN;
}
break;
default:
pr_debug("%s:session id %d: buf[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
}
return rc;
}
int audio_in_set_config(struct file *file,
struct msm_audio_config *cfg)
{
int rc = 0;
struct q6audio_in *audio = file->private_data;
if (audio->feedback != NON_TUNNEL_MODE) {
pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
__func__, audio->ac->session);
rc = -EACCES;
goto ret;
}
if ((cfg->buffer_count > PCM_BUF_COUNT) ||
(cfg->buffer_count == 1))
cfg->buffer_count = PCM_BUF_COUNT;
audio->pcm_cfg.buffer_count = cfg->buffer_count;
audio->pcm_cfg.buffer_size = cfg->buffer_size;
audio->pcm_cfg.channel_count = cfg->channel_count;
audio->pcm_cfg.sample_rate = cfg->sample_rate;
if (audio->opened && audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
pr_err("%s:session id %d: Buffer Alloc failed\n",
__func__, audio->ac->session);
rc = -ENOMEM;
goto ret;
}
}
audio->buf_alloc |= BUF_ALLOC_IN;
rc = 0;
pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
audio->ac->session, audio->pcm_cfg.buffer_count,
audio->pcm_cfg.buffer_size);
ret:
return rc;
}
/* ------------------- device --------------------- */
static long audio_in_ioctl_shared(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_FLUSH: {
/* Make sure we're stopped and we wake any threads
* that might be blocked holding the read_lock.
* While audio->stopped read threads will always
* exit immediately.
*/
rc = audio_in_flush(audio);
if (rc < 0)
pr_err("%s:session id %d: Flush Fail rc=%d\n",
__func__, audio->ac->session, rc);
else { /* Register back the flushed read buffer with DSP */
int cnt = 0;
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
pr_debug("register the read buffer\n");
}
break;
}
case AUDIO_PAUSE: {
pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
audio->ac->session);
if (audio->enabled)
audio_in_pause(audio);
break;
}
case AUDIO_GET_SESSION_ID: {
if (copy_to_user((void *) arg, &audio->ac->session,
sizeof(u16))) {
pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
__func__);
rc = -EFAULT;
}
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
long audio_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
if (cmd == AUDIO_GET_STATS) {
struct msm_audio_stats stats;
memset(&stats, 0, sizeof(stats));
stats.byte_count = atomic_read(&audio->in_bytes);
stats.sample_count = atomic_read(&audio->in_samples);
if (copy_to_user((void *) arg, &stats, sizeof(stats)))
return -EFAULT;
return rc;
}
mutex_lock(&audio->lock);
switch (cmd) {
case AUDIO_FLUSH:
case AUDIO_PAUSE:
case AUDIO_GET_SESSION_ID:
rc = audio_in_ioctl_shared(file, cmd, arg);
break;
case AUDIO_GET_STREAM_CONFIG: {
struct msm_audio_stream_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.buffer_size = audio->str_cfg.buffer_size;
cfg.buffer_count = audio->str_cfg.buffer_count;
if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
rc = -EFAULT;
pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
__func__, audio->ac->session, cfg.buffer_size,
cfg.buffer_count);
break;
}
case AUDIO_SET_STREAM_CONFIG: {
struct msm_audio_stream_config cfg;
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n"
, __func__);
rc = -EFAULT;
break;
}
/* Minimum single frame size,
* but with in maximum frames number
*/
if ((cfg.buffer_size < (audio->min_frame_size +
sizeof(struct meta_out_dsp))) ||
(cfg.buffer_count < FRAME_NUM)) {
rc = -EINVAL;
break;
}
if (cfg.buffer_size > MAX_BUFFER_SIZE) {
rc = -EINVAL;
break;
}
audio->str_cfg.buffer_size = cfg.buffer_size;
audio->str_cfg.buffer_count = cfg.buffer_count;
if (audio->opened) {
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
audio->str_cfg.buffer_count);
if (rc < 0) {
pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENOMEM;
break;
}
}
audio->buf_alloc |= BUF_ALLOC_OUT;
rc = 0;
pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
__func__, audio->ac->session,
audio->str_cfg.buffer_size,
audio->str_cfg.buffer_count);
break;
}
case AUDIO_SET_BUF_CFG: {
struct msm_audio_buf_cfg cfg;
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
rc = -EFAULT;
break;
}
if ((audio->feedback == NON_TUNNEL_MODE) &&
!cfg.meta_info_enable) {
rc = -EFAULT;
break;
}
/* Restrict the num of frames per buf to coincide with
* default buf size
*/
if (cfg.frames_per_buf > audio->max_frames_per_buf) {
rc = -EFAULT;
break;
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__,
audio->ac->session, cfg.meta_info_enable,
cfg.frames_per_buf);
break;
}
case AUDIO_GET_BUF_CFG: {
pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
if (copy_to_user((void *)arg, &audio->buf_cfg,
sizeof(struct msm_audio_buf_cfg)))
rc = -EFAULT;
break;
}
case AUDIO_GET_CONFIG: {
if (copy_to_user((void *)arg, &audio->pcm_cfg,
sizeof(struct msm_audio_config)))
rc = -EFAULT;
break;
}
case AUDIO_SET_CONFIG: {
struct msm_audio_config cfg;
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = audio_in_set_config(file, &cfg);
break;
}
default:
/* call codec specific ioctl */
rc = audio->enc_ioctl(file, cmd, arg);
}
mutex_unlock(&audio->lock);
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_stats32 {
u32 byte_count;
u32 sample_count;
u32 unused[2];
};
struct msm_audio_stream_config32 {
u32 buffer_size;
u32 buffer_count;
};
struct msm_audio_config32 {
u32 buffer_size;
u32 buffer_count;
u32 channel_count;
u32 sample_rate;
u32 type;
u32 meta_field;
u32 bits;
u32 unused[3];
};
struct msm_audio_buf_cfg32 {
u32 meta_info_enable;
u32 frames_per_buf;
};
enum {
AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
struct msm_audio_config32),
AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
struct msm_audio_config32),
AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
struct msm_audio_stats32),
AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
struct msm_audio_stream_config32),
AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
struct msm_audio_stream_config32),
AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
struct msm_audio_buf_cfg32),
AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
struct msm_audio_buf_cfg32),
};
long audio_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
if (cmd == AUDIO_GET_STATS_32) {
struct msm_audio_stats32 stats_32;
memset(&stats_32, 0, sizeof(stats_32));
stats_32.byte_count = atomic_read(&audio->in_bytes);
stats_32.sample_count = atomic_read(&audio->in_samples);
if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) {
pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n",
__func__);
return -EFAULT;
}
return rc;
}
mutex_lock(&audio->lock);
switch (cmd) {
case AUDIO_FLUSH:
case AUDIO_PAUSE:
case AUDIO_GET_SESSION_ID:
rc = audio_in_ioctl_shared(file, cmd, arg);
break;
case AUDIO_GET_STREAM_CONFIG_32: {
struct msm_audio_stream_config32 cfg_32;
memset(&cfg_32, 0, sizeof(cfg_32));
cfg_32.buffer_size = audio->str_cfg.buffer_size;
cfg_32.buffer_count = audio->str_cfg.buffer_count;
if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
pr_err("%s: Copy to user failed\n", __func__);
rc = -EFAULT;
}
pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
__func__, audio->ac->session,
cfg_32.buffer_size,
cfg_32.buffer_count);
break;
}
case AUDIO_SET_STREAM_CONFIG_32: {
struct msm_audio_stream_config32 cfg_32;
struct msm_audio_stream_config cfg;
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.buffer_size = cfg_32.buffer_size;
cfg.buffer_count = cfg_32.buffer_count;
/* Minimum single frame size,
* but with in maximum frames number
*/
if ((cfg.buffer_size < (audio->min_frame_size +
sizeof(struct meta_out_dsp))) ||
(cfg.buffer_count < FRAME_NUM)) {
rc = -EINVAL;
break;
}
audio->str_cfg.buffer_size = cfg.buffer_size;
audio->str_cfg.buffer_count = cfg.buffer_count;
if (audio->opened) {
rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
audio->str_cfg.buffer_count);
if (rc < 0) {
pr_err("%s: session id %d:\n",
__func__, audio->ac->session);
pr_err("Buffer Alloc failed rc=%d\n", rc);
rc = -ENOMEM;
break;
}
}
audio->buf_alloc |= BUF_ALLOC_OUT;
pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
__func__, audio->ac->session,
audio->str_cfg.buffer_size,
audio->str_cfg.buffer_count);
break;
}
case AUDIO_SET_BUF_CFG_32: {
struct msm_audio_buf_cfg32 cfg_32;
struct msm_audio_buf_cfg cfg;
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed",
__func__);
rc = -EFAULT;
break;
}
cfg.meta_info_enable = cfg_32.meta_info_enable;
cfg.frames_per_buf = cfg_32.frames_per_buf;
if ((audio->feedback == NON_TUNNEL_MODE) &&
!cfg.meta_info_enable) {
rc = -EFAULT;
break;
}
/* Restrict the num of frames per buf to coincide with
* default buf size
*/
if (cfg.frames_per_buf > audio->max_frames_per_buf) {
rc = -EFAULT;
break;
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__, audio->ac->session, cfg.meta_info_enable,
cfg.frames_per_buf);
break;
}
case AUDIO_GET_BUF_CFG_32: {
struct msm_audio_buf_cfg32 cfg_32;
pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(struct msm_audio_buf_cfg32))) {
pr_err("%s: Copy to user failed\n", __func__);
rc = -EFAULT;
}
break;
}
case AUDIO_GET_CONFIG_32: {
struct msm_audio_config32 cfg_32;
memset(&cfg_32, 0, sizeof(cfg_32));
cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
cfg_32.channel_count = audio->pcm_cfg.channel_count;
cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
cfg_32.type = audio->pcm_cfg.type;
cfg_32.meta_field = audio->pcm_cfg.meta_field;
cfg_32.bits = audio->pcm_cfg.bits;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(struct msm_audio_config32))) {
pr_err("%s: Copy to user failed\n", __func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_CONFIG_32: {
struct msm_audio_config32 cfg_32;
struct msm_audio_config cfg;
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.buffer_size = cfg_32.buffer_size;
cfg.buffer_count = cfg_32.buffer_count;
cfg.channel_count = cfg_32.channel_count;
cfg.sample_rate = cfg_32.sample_rate;
cfg.type = cfg_32.type;
cfg.meta_field = cfg_32.meta_field;
cfg.bits = cfg_32.bits;
rc = audio_in_set_config(file, &cfg);
break;
}
default:
/* call codec specific ioctl */
rc = audio->enc_compat_ioctl(file, cmd, arg);
}
mutex_unlock(&audio->lock);
return rc;
}
#endif
ssize_t audio_in_read(struct file *file,
char __user *buf,
size_t count, loff_t *pos)
{
struct q6audio_in *audio = file->private_data;
const char __user *start = buf;
unsigned char *data;
uint32_t offset = 0;
uint32_t size = 0;
int rc = 0;
uint32_t idx;
struct meta_out_dsp meta;
uint32_t bytes_to_copy = 0;
uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
(sizeof(unsigned char) +
(sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
memset(&meta, 0, sizeof(meta));
pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session,
count);
if (audio->reset_event)
return -ENETRESET;
if (!audio->enabled)
return -EFAULT;
mutex_lock(&audio->read_lock);
while (count > 0) {
rc = wait_event_interruptible(
audio->read_wait,
((atomic_read(&audio->out_count) > 0) ||
(audio->stopped) ||
audio->rflush || audio->eos_rsp ||
audio->event_abort));
if (audio->event_abort) {
rc = -EIO;
break;
}
if (rc < 0)
break;
if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
audio->rflush) {
pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
__func__,
audio->ac->session);
rc = 0;/* End of File */
break;
}
if (!(atomic_read(&audio->out_count)) &&
(audio->eos_rsp == 1) &&
(count >= (sizeof(unsigned char) +
sizeof(struct meta_out_dsp)))) {
unsigned char num_of_frames;
pr_info("%s:session id %d: eos %d at output\n",
__func__, audio->ac->session, audio->eos_rsp);
if (buf != start)
break;
num_of_frames = 0xFF;
if (copy_to_user(buf, &num_of_frames,
sizeof(unsigned char))) {
rc = -EFAULT;
break;
}
buf += sizeof(unsigned char);
meta.frame_size = 0xFFFF;
meta.encoded_pcm_samples = 0xFFFF;
meta.msw_ts = 0x00;
meta.lsw_ts = 0x00;
meta.nflags = AUD_EOS_SET;
audio->eos_rsp = 0;
if (copy_to_user(buf, &meta, sizeof(meta))) {
rc = -EFAULT;
break;
}
buf += sizeof(meta);
break;
}
data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
&size, &idx);
if ((count >= (size + mfield_size)) && data) {
if (audio->buf_cfg.meta_info_enable) {
if (copy_to_user(buf,
&audio->out_frame_info[idx][0],
sizeof(unsigned char))) {
rc = -EFAULT;
break;
}
bytes_to_copy =
(size + audio->out_frame_info[idx][1]);
/* Number of frames information copied */
buf += sizeof(unsigned char);
count -= sizeof(unsigned char);
} else {
offset = audio->out_frame_info[idx][1];
bytes_to_copy = size;
}
pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
__func__, audio->ac->session,
audio->out_frame_info[idx][1],
audio->out_frame_info[idx][0]);
if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
rc = -EFAULT;
break;
}
count -= bytes_to_copy;
buf += bytes_to_copy;
} else {
pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n",
__func__,
audio->ac->session,
data, size, count);
}
atomic_dec(&audio->out_count);
q6asm_read(audio->ac);
break;
}
mutex_unlock(&audio->read_lock);
pr_debug("%s:session id %d: read: %zd bytes\n", __func__,
audio->ac->session, (buf-start));
if (buf > start)
return buf - start;
return rc;
}
static int extract_meta_info(char *buf, unsigned long *msw_ts,
unsigned long *lsw_ts, unsigned int *flags)
{
struct meta_in *meta = (struct meta_in *)buf;
*msw_ts = meta->ntimestamp.highpart;
*lsw_ts = meta->ntimestamp.lowpart;
*flags = meta->nflags;
return 0;
}
ssize_t audio_in_write(struct file *file,
const char __user *buf,
size_t count, loff_t *pos)
{
struct q6audio_in *audio = file->private_data;
const char __user *start = buf;
size_t xfer = 0;
char *cpy_ptr;
int rc = 0;
unsigned char *data;
uint32_t size = 0;
uint32_t idx = 0;
uint32_t nflags = 0;
unsigned long msw_ts = 0;
unsigned long lsw_ts = 0;
uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
sizeof(struct meta_in);
pr_debug("%s:session id %d: to write[%zd]\n", __func__,
audio->ac->session, count);
if (audio->reset_event)
return -ENETRESET;
if (!audio->enabled)
return -EFAULT;
mutex_lock(&audio->write_lock);
while (count > 0) {
rc = wait_event_interruptible(audio->write_wait,
((atomic_read(&audio->in_count) > 0) ||
(audio->stopped) ||
(audio->wflush) || (audio->event_abort)));
if (audio->event_abort) {
rc = -EIO;
break;
}
if (rc < 0)
break;
if (audio->stopped || audio->wflush) {
pr_debug("%s: session id %d: stop or flush\n", __func__,
audio->ac->session);
rc = -EBUSY;
break;
}
/* if no PCM data, might have only eos buffer
* such case do not hold cpu buffer
*/
if ((buf == start) && (count == mfield_size)) {
char eos_buf[sizeof(struct meta_in)];
/* Processing beginning of user buffer */
if (copy_from_user(eos_buf, buf, mfield_size)) {
rc = -EFAULT;
break;
}
/* Check if EOS flag is set and buffer has
* contains just meta field
*/
extract_meta_info(eos_buf, &msw_ts, &lsw_ts,
&nflags);
buf += mfield_size;
/* send the EOS and return */
pr_debug("%s:session id %d: send EOS 0x%8x\n",
__func__,
audio->ac->session, nflags);
break;
}
data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
&size, &idx);
if (!data) {
pr_debug("%s:session id %d: No buf available\n",
__func__, audio->ac->session);
continue;
}
cpy_ptr = data;
if (audio->buf_cfg.meta_info_enable) {
if (buf == start) {
/* Processing beginning of user buffer */
if (copy_from_user(cpy_ptr, buf, mfield_size)) {
rc = -EFAULT;
break;
}
/* Check if EOS flag is set and buffer has
* contains just meta field
*/
extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
&nflags);
buf += mfield_size;
count -= mfield_size;
} else {
pr_debug("%s:session id %d: continuous buffer\n",
__func__, audio->ac->session);
}
}
xfer = (count > (audio->pcm_cfg.buffer_size)) ?
(audio->pcm_cfg.buffer_size) : count;
if (copy_from_user(cpy_ptr, buf, xfer)) {
rc = -EFAULT;
break;
}
rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
if (rc < 0) {
rc = -EFAULT;
break;
}
atomic_dec(&audio->in_count);
count -= xfer;
buf += xfer;
}
mutex_unlock(&audio->write_lock);
pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n",
__func__, audio->ac->session,
nflags, buf, start);
if (nflags & AUD_EOS_SET) {
rc = q6asm_cmd(audio->ac, CMD_EOS);
pr_info("%s:session id %d: eos %d at input\n", __func__,
audio->ac->session, audio->eos_rsp);
}
pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__,
audio->ac->session, (buf - start - mfield_size),
atomic_read(&audio->in_count));
if (!rc) {
if (buf > start)
return buf - start;
}
return rc;
}
int audio_in_release(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = file->private_data;
pr_info("%s: session id %d\n", __func__, audio->ac->session);
mutex_lock(&audio->lock);
audio_in_disable(audio);
q6asm_audio_client_free(audio->ac);
mutex_unlock(&audio->lock);
kfree(audio->enc_cfg);
kfree(audio->codec_cfg);
kfree(audio);
return 0;
}

Melihat File

@@ -0,0 +1,114 @@
/* Copyright (c) 2010-2015, 2017 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/msm_audio.h>
#include <linux/compat.h>
#include "q6audio_common.h"
#define FRAME_NUM (8)
#define PCM_BUF_COUNT (2)
#define AUD_EOS_SET 0x01
#define TUNNEL_MODE 0x0000
#define NON_TUNNEL_MODE 0x0001
#define NO_BUF_ALLOC 0x00
#define BUF_ALLOC_IN 0x01
#define BUF_ALLOC_OUT 0x02
#define BUF_ALLOC_INOUT 0x03
#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095))
struct timestamp {
u32 lowpart;
u32 highpart;
} __packed;
struct meta_in {
unsigned short offset;
struct timestamp ntimestamp;
unsigned int nflags;
} __packed;
struct meta_out_dsp {
u32 offset_to_frame;
u32 frame_size;
u32 encoded_pcm_samples;
u32 msw_ts;
u32 lsw_ts;
u32 nflags;
} __packed;
struct meta_out {
unsigned char num_of_frames;
struct meta_out_dsp meta_out_dsp[];
} __packed;
struct q6audio_in {
spinlock_t dsp_lock;
atomic_t in_bytes;
atomic_t in_samples;
struct mutex lock;
struct mutex read_lock;
struct mutex write_lock;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
struct audio_client *ac;
struct msm_audio_stream_config str_cfg;
void *enc_cfg;
struct msm_audio_buf_cfg buf_cfg;
struct msm_audio_config pcm_cfg;
void *codec_cfg;
/* number of buffers available to read/write */
atomic_t in_count;
atomic_t out_count;
/* first idx: num of frames per buf, second idx: offset to frame */
uint32_t out_frame_info[FRAME_NUM][2];
int eos_rsp;
int opened;
int enabled;
int stopped;
int event_abort;
int feedback; /* Flag indicates whether used
* in Non Tunnel mode
*/
int rflush;
int wflush;
int buf_alloc;
uint16_t min_frame_size;
uint16_t max_frames_per_buf;
bool reset_event;
long (*enc_ioctl)(struct file *, unsigned int, unsigned long);
long (*enc_compat_ioctl)(struct file *, unsigned int, unsigned long);
};
int audio_in_enable(struct q6audio_in *audio);
int audio_in_disable(struct q6audio_in *audio);
int audio_in_buf_alloc(struct q6audio_in *audio);
long audio_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
long audio_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
#else
#define audio_in_compat_ioctl NULL
#endif
ssize_t audio_in_read(struct file *file, char __user *buf,
size_t count, loff_t *pos);
ssize_t audio_in_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos);
int audio_in_release(struct inode *inode, struct file *file);
int audio_in_set_config(struct file *file, struct msm_audio_config *cfg);

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,232 @@
/* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/wakelock.h>
#include <linux/msm_audio.h>
#include <linux/debugfs.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/msm_ion.h>
#include <asm/ioctls.h>
#include <linux/atomic.h>
#include "q6audio_common.h"
#define TUNNEL_MODE 0x0000
#define NON_TUNNEL_MODE 0x0001
#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */
#define ADRV_STATUS_FSYNC 0x00000008
#define ADRV_STATUS_PAUSE 0x00000010
#define AUDIO_DEC_EOS_SET 0x00000001
#define AUDIO_DEC_EOF_SET 0x00000010
#define AUDIO_EVENT_NUM 10
#define __CONTAINS(r, v, l) ({ \
typeof(r) __r = r; \
typeof(v) __v = v; \
typeof(v) __e = __v + l; \
int res = ((__v >= __r->vaddr) && \
(__e <= __r->vaddr + __r->len)); \
res; \
})
#define CONTAINS(r1, r2) ({ \
typeof(r2) __r2 = r2; \
__CONTAINS(r1, __r2->vaddr, __r2->len); \
})
#define IN_RANGE(r, v) ({ \
typeof(r) __r = r; \
typeof(v) __vv = v; \
int res = ((__vv >= __r->vaddr) && \
(__vv < (__r->vaddr + __r->len))); \
res; \
})
#define OVERLAPS(r1, r2) ({ \
typeof(r1) __r1 = r1; \
typeof(r2) __r2 = r2; \
typeof(__r2->vaddr) __v = __r2->vaddr; \
typeof(__v) __e = __v + __r2->len - 1; \
int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
res; \
})
struct timestamp {
u32 lowpart;
u32 highpart;
} __packed;
struct meta_out_dsp {
u32 offset_to_frame;
u32 frame_size;
u32 encoded_pcm_samples;
u32 msw_ts;
u32 lsw_ts;
u32 nflags;
} __packed;
struct dec_meta_in {
unsigned char reserved[18];
unsigned short offset;
struct timestamp ntimestamp;
unsigned int nflags;
} __packed;
struct dec_meta_out {
unsigned int reserved[7];
unsigned int num_of_frames;
struct meta_out_dsp meta_out_dsp[];
} __packed;
/* General meta field to store meta info locally */
union meta_data {
struct dec_meta_out meta_out;
struct dec_meta_in meta_in;
} __packed;
/* per device wakeup source manager */
struct ws_mgr {
struct mutex ws_lock;
uint32_t ref_cnt;
};
#define PCM_BUF_COUNT (2)
/* Buffer with meta */
#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out))
/* FRAME_NUM must be a power of two */
#define FRAME_NUM (2)
#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in))
struct audio_aio_ion_region {
struct list_head list;
struct ion_handle *handle;
int fd;
void *vaddr;
phys_addr_t paddr;
void *kvaddr;
unsigned long len;
unsigned int ref_cnt;
};
struct audio_aio_event {
struct list_head list;
int event_type;
union msm_audio_event_payload payload;
};
struct audio_aio_buffer_node {
struct list_head list;
struct msm_audio_aio_buf buf;
unsigned long paddr;
uint32_t token;
void *kvaddr;
union meta_data meta_info;
};
struct q6audio_aio;
struct audio_aio_drv_operations {
void (*out_flush)(struct q6audio_aio *);
void (*in_flush)(struct q6audio_aio *);
};
struct q6audio_aio {
atomic_t in_bytes;
atomic_t in_samples;
struct msm_audio_stream_config str_cfg;
struct msm_audio_buf_cfg buf_cfg;
struct msm_audio_config pcm_cfg;
void *codec_cfg;
struct audio_client *ac;
struct mutex lock;
struct mutex read_lock;
struct mutex write_lock;
struct mutex get_event_lock;
wait_queue_head_t cmd_wait;
wait_queue_head_t write_wait;
wait_queue_head_t event_wait;
spinlock_t dsp_lock;
spinlock_t event_queue_lock;
struct miscdevice *miscdevice;
uint32_t wakelock_voted;
struct ws_mgr *audio_ws_mgr;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
#endif
struct list_head out_queue; /* queue to retain output buffers */
struct list_head in_queue; /* queue to retain input buffers */
struct list_head free_event_queue;
struct list_head event_queue;
struct list_head ion_region_queue; /* protected by lock */
struct ion_client *client;
struct audio_aio_drv_operations drv_ops;
union msm_audio_event_payload eos_write_payload;
uint32_t device_events;
uint16_t volume;
uint32_t drv_status;
int event_abort;
int eos_rsp;
int eos_flag;
int opened;
int enabled;
int stopped;
int feedback;
int rflush; /* Read flush */
int wflush; /* Write flush */
bool reset_event;
long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long);
};
void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
uint32_t *payload);
void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
uint32_t *payload);
int insert_eos_buf(struct q6audio_aio *audio,
struct audio_aio_buffer_node *buf_node);
void extract_meta_out_info(struct q6audio_aio *audio,
struct audio_aio_buffer_node *buf_node, int dir);
int audio_aio_open(struct q6audio_aio *audio, struct file *file);
int audio_aio_enable(struct q6audio_aio *audio);
void audio_aio_post_event(struct q6audio_aio *audio, int type,
union msm_audio_event_payload payload);
int audio_aio_release(struct inode *inode, struct file *file);
int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
void audio_aio_async_out_flush(struct q6audio_aio *audio);
void audio_aio_async_in_flush(struct q6audio_aio *audio);
void audio_aio_ioport_reset(struct q6audio_aio *audio);
int enable_volume_ramp(struct q6audio_aio *audio);
#ifdef CONFIG_DEBUG_FS
int audio_aio_debug_open(struct inode *inode, struct file *file);
ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
#endif

Melihat File

@@ -0,0 +1,345 @@
/* wma audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/types.h>
#include <linux/msm_audio_wma.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_wma_misc;
static struct ws_mgr audio_wma_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_wma_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_wma_cfg wma_cfg;
struct msm_audio_wma_config_v2 *wma_config;
pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
wma_cfg.format_tag = wma_config->format_tag;
wma_cfg.ch_cfg = wma_config->numchannels;
wma_cfg.sample_rate = wma_config->samplingrate;
wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond;
wma_cfg.block_align = wma_config->block_align;
wma_cfg.valid_bits_per_sample =
wma_config->validbitspersample;
wma_cfg.ch_mask = wma_config->channelmask;
wma_cfg.encode_opt = wma_config->encodeopt;
/* Configure Media format block */
rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_WMA_CONFIG_V2: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_wma_config_v2))) {
pr_err("%s:copy_to_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_WMA_CONFIG_V2: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_wma_config_v2))) {
pr_err("%s:copy_from_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_wma_config_v2_32 {
u16 format_tag;
u16 numchannels;
u32 samplingrate;
u32 avgbytespersecond;
u16 block_align;
u16 validbitspersample;
u32 channelmask;
u16 encodeopt;
};
enum {
AUDIO_GET_WMA_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2_32),
AUDIO_SET_WMA_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2_32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
case AUDIO_GET_WMA_CONFIG_V2_32: {
struct msm_audio_wma_config_v2 *wma_config;
struct msm_audio_wma_config_v2_32 wma_config_32;
memset(&wma_config_32, 0, sizeof(wma_config_32));
wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
wma_config_32.format_tag = wma_config->format_tag;
wma_config_32.numchannels = wma_config->numchannels;
wma_config_32.samplingrate = wma_config->samplingrate;
wma_config_32.avgbytespersecond = wma_config->avgbytespersecond;
wma_config_32.block_align = wma_config->block_align;
wma_config_32.validbitspersample =
wma_config->validbitspersample;
wma_config_32.channelmask = wma_config->channelmask;
wma_config_32.encodeopt = wma_config->encodeopt;
if (copy_to_user((void *)arg, &wma_config_32,
sizeof(wma_config_32))) {
pr_err("%s: copy_to_user for GET_WMA_CONFIG_V2_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_SET_WMA_CONFIG_V2_32: {
struct msm_audio_wma_config_v2 *wma_config;
struct msm_audio_wma_config_v2_32 wma_config_32;
if (copy_from_user(&wma_config_32, (void *)arg,
sizeof(wma_config_32))) {
pr_err("%s: copy_from_user for SET_WMA_CONFIG_V2_32 failed\n"
, __func__);
rc = -EFAULT;
break;
}
wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
wma_config->format_tag = wma_config_32.format_tag;
wma_config->numchannels = wma_config_32.numchannels;
wma_config->samplingrate = wma_config_32.samplingrate;
wma_config->avgbytespersecond = wma_config_32.avgbytespersecond;
wma_config->block_align = wma_config_32.block_align;
wma_config->validbitspersample =
wma_config_32.validbitspersample;
wma_config->channelmask = wma_config_32.channelmask;
wma_config->encodeopt = wma_config_32.encodeopt;
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_wma_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_wma_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_wma_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_WMA_V9);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open WMA decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_wma_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_wma_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__,
audio->feedback,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_wma_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_wma_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_wma",
.fops = &audio_wma_fops,
};
static int __init audio_wma_init(void)
{
int ret = misc_register(&audio_wma_misc);
if (ret == 0)
device_init_wakeup(audio_wma_misc.this_device, true);
audio_wma_ws_mgr.ref_cnt = 0;
mutex_init(&audio_wma_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_wma_init);

Melihat File

@@ -0,0 +1,418 @@
/* wmapro audio output device
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/types.h>
#include <linux/msm_audio_wmapro.h>
#include <linux/compat.h>
#include "audio_utils_aio.h"
static struct miscdevice audio_wmapro_misc;
static struct ws_mgr audio_wmapro_ws_mgr;
#ifdef CONFIG_DEBUG_FS
static const struct file_operations audio_wmapro_debug_fops = {
.read = audio_aio_debug_read,
.open = audio_aio_debug_open,
};
#endif
static long audio_ioctl_shared(struct file *file, unsigned int cmd,
void *arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START: {
struct asm_wmapro_cfg wmapro_cfg;
struct msm_audio_wmapro_config *wmapro_config;
pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count,
16, /* bits per sample */
true, /* use default channel map */
true, /* use back channel map flavor */
NULL);
if (rc < 0) {
pr_err("pcm output block config failed\n");
break;
}
}
wmapro_config = (struct msm_audio_wmapro_config *)
audio->codec_cfg;
if ((wmapro_config->formattag == 0x162) ||
(wmapro_config->formattag == 0x163) ||
(wmapro_config->formattag == 0x166) ||
(wmapro_config->formattag == 0x167)) {
wmapro_cfg.format_tag = wmapro_config->formattag;
} else {
pr_err("%s:AUDIO_START failed: formattag = %d\n",
__func__, wmapro_config->formattag);
rc = -EINVAL;
break;
}
if (wmapro_config->numchannels > 0) {
wmapro_cfg.ch_cfg = wmapro_config->numchannels;
} else {
pr_err("%s:AUDIO_START failed: channels = %d\n",
__func__, wmapro_config->numchannels);
rc = -EINVAL;
break;
}
if (wmapro_config->samplingrate > 0) {
wmapro_cfg.sample_rate = wmapro_config->samplingrate;
} else {
pr_err("%s:AUDIO_START failed: sample_rate = %d\n",
__func__, wmapro_config->samplingrate);
rc = -EINVAL;
break;
}
wmapro_cfg.avg_bytes_per_sec =
wmapro_config->avgbytespersecond;
if ((wmapro_config->asfpacketlength <= 13376) ||
(wmapro_config->asfpacketlength > 0)) {
wmapro_cfg.block_align =
wmapro_config->asfpacketlength;
} else {
pr_err("%s:AUDIO_START failed: block_align = %d\n",
__func__, wmapro_config->asfpacketlength);
rc = -EINVAL;
break;
}
if ((wmapro_config->validbitspersample == 16) ||
(wmapro_config->validbitspersample == 24)) {
wmapro_cfg.valid_bits_per_sample =
wmapro_config->validbitspersample;
} else {
pr_err("%s:AUDIO_START failed: bitspersample = %d\n",
__func__, wmapro_config->validbitspersample);
rc = -EINVAL;
break;
}
wmapro_cfg.ch_mask = wmapro_config->channelmask;
wmapro_cfg.encode_opt = wmapro_config->encodeopt;
wmapro_cfg.adv_encode_opt =
wmapro_config->advancedencodeopt;
wmapro_cfg.adv_encode_opt2 =
wmapro_config->advancedencodeopt2;
/* Configure Media format block */
rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg,
audio->ac->stream_id);
if (rc < 0) {
pr_err("cmd media format block failed\n");
break;
}
rc = audio_aio_enable(audio);
audio->eos_rsp = 0;
audio->eos_flag = 0;
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("Audio Start procedure failed rc=%d\n", rc);
break;
}
pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
if (audio->stopped == 1)
audio->stopped = 0;
break;
}
default:
pr_err("%s: Unknown ioctl cmd %d\n", __func__, cmd);
rc = -EINVAL;
break;
}
return rc;
}
static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_GET_WMAPRO_CONFIG: {
if (copy_to_user((void *)arg, audio->codec_cfg,
sizeof(struct msm_audio_wmapro_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_WMAPRO_CONFIG: {
if (copy_from_user(audio->codec_cfg, (void *)arg,
sizeof(struct msm_audio_wmapro_config))) {
pr_err("%s: copy_from_user for AUDIO_SET_WMAPRO_CONFIG_V2 failed\n",
__func__);
rc = -EFAULT;
break;
}
break;
}
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_wmapro_config32 {
u16 armdatareqthr;
u8 validbitspersample;
u8 numchannels;
u16 formattag;
u32 samplingrate;
u32 avgbytespersecond;
u16 asfpacketlength;
u32 channelmask;
u16 encodeopt;
u16 advancedencodeopt;
u32 advancedencodeopt2;
};
enum {
AUDIO_GET_WMAPRO_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config32),
AUDIO_SET_WMAPRO_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config32)
};
static long audio_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct q6audio_aio *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_GET_WMAPRO_CONFIG_32: {
struct msm_audio_wmapro_config *wmapro_config;
struct msm_audio_wmapro_config32 wmapro_config_32;
memset(&wmapro_config_32, 0, sizeof(wmapro_config_32));
wmapro_config =
(struct msm_audio_wmapro_config *)audio->codec_cfg;
wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr;
wmapro_config_32.validbitspersample =
wmapro_config->validbitspersample;
wmapro_config_32.numchannels = wmapro_config->numchannels;
wmapro_config_32.formattag = wmapro_config->formattag;
wmapro_config_32.samplingrate = wmapro_config->samplingrate;
wmapro_config_32.avgbytespersecond =
wmapro_config->avgbytespersecond;
wmapro_config_32.asfpacketlength =
wmapro_config->asfpacketlength;
wmapro_config_32.channelmask = wmapro_config->channelmask;
wmapro_config_32.encodeopt = wmapro_config->encodeopt;
wmapro_config_32.advancedencodeopt =
wmapro_config->advancedencodeopt;
wmapro_config_32.advancedencodeopt2 =
wmapro_config->advancedencodeopt2;
if (copy_to_user((void *)arg, &wmapro_config_32,
sizeof(struct msm_audio_wmapro_config32))) {
pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG_V2_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_WMAPRO_CONFIG_32: {
struct msm_audio_wmapro_config *wmapro_config;
struct msm_audio_wmapro_config32 wmapro_config_32;
if (copy_from_user(&wmapro_config_32, (void *)arg,
sizeof(struct msm_audio_wmapro_config32))) {
pr_err(
"%s: copy_from_user for AUDIO_SET_WMAPRO_CONFG_V2_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
wmapro_config =
(struct msm_audio_wmapro_config *)audio->codec_cfg;
wmapro_config->armdatareqthr = wmapro_config_32.armdatareqthr;
wmapro_config->validbitspersample =
wmapro_config_32.validbitspersample;
wmapro_config->numchannels = wmapro_config_32.numchannels;
wmapro_config->formattag = wmapro_config_32.formattag;
wmapro_config->samplingrate = wmapro_config_32.samplingrate;
wmapro_config->avgbytespersecond =
wmapro_config_32.avgbytespersecond;
wmapro_config->asfpacketlength =
wmapro_config_32.asfpacketlength;
wmapro_config->channelmask = wmapro_config_32.channelmask;
wmapro_config->encodeopt = wmapro_config_32.encodeopt;
wmapro_config->advancedencodeopt =
wmapro_config_32.advancedencodeopt;
wmapro_config->advancedencodeopt2 =
wmapro_config_32.advancedencodeopt2;
break;
}
case AUDIO_START: {
rc = audio_ioctl_shared(file, cmd, (void *)arg);
break;
}
default: {
pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
break;
}
}
return rc;
}
#else
#define audio_compat_ioctl NULL
#endif
static int audio_open(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = NULL;
int rc = 0;
#ifdef CONFIG_DEBUG_FS
/* 4 bytes represents decoder number, 1 byte for terminate string */
char name[sizeof "msm_wmapro_" + 5];
#endif
audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config),
GFP_KERNEL);
if (audio->codec_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
audio->miscdevice = &audio_wmapro_misc;
audio->wakelock_voted = false;
audio->audio_ws_mgr = &audio_wmapro_ws_mgr;
audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
(void *)audio);
if (!audio->ac) {
pr_err("Could not allocate memory for audio client\n");
kfree(audio->codec_cfg);
kfree(audio);
return -ENOMEM;
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
pr_err("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
/* open in T/NT mode */
if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
FORMAT_WMA_V10PRO);
if (rc < 0) {
pr_err("NT mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = NON_TUNNEL_MODE;
/* open WMA decoder, expected frames is always 1*/
audio->buf_cfg.frames_per_buf = 0x01;
audio->buf_cfg.meta_info_enable = 0x01;
} else if ((file->f_mode & FMODE_WRITE) &&
!(file->f_mode & FMODE_READ)) {
rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO);
if (rc < 0) {
pr_err("T mode Open failed rc=%d\n", rc);
rc = -ENODEV;
goto fail;
}
audio->feedback = TUNNEL_MODE;
audio->buf_cfg.meta_info_enable = 0x00;
} else {
pr_err("Not supported mode\n");
rc = -EACCES;
goto fail;
}
#ifdef CONFIG_DEBUG_FS
snprintf(name, sizeof(name), "msm_wmapro_%04x", audio->ac->session);
audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
NULL, (void *)audio,
&audio_wmapro_debug_fops);
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__,
audio->ac->session);
return rc;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->codec_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_wmapro_fops = {
.owner = THIS_MODULE,
.open = audio_open,
.release = audio_aio_release,
.unlocked_ioctl = audio_ioctl,
.fsync = audio_aio_fsync,
.compat_ioctl = audio_compat_ioctl
};
static struct miscdevice audio_wmapro_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_wmapro",
.fops = &audio_wmapro_fops,
};
static int __init audio_wmapro_init(void)
{
int ret = misc_register(&audio_wmapro_misc);
if (ret == 0)
device_init_wakeup(audio_wmapro_misc.this_device, true);
audio_wmapro_ws_mgr.ref_cnt = 0;
mutex_init(&audio_wmapro_ws_mgr.ws_lock);
return ret;
}
device_initcall(audio_wmapro_init);

Melihat File

@@ -0,0 +1,410 @@
/* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_qcp.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10))
static long evrc_in_ioctl_shared(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_evrc_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
pr_info("%s:AUDIO_START already over\n", __func__);
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed\n",
__func__, audio->ac->session);
break;
}
/* rate_modulation_cmd set to zero
* currently not configurable from user space
*/
rc = q6asm_enc_cfg_blk_evrc(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate, 0);
if (rc < 0) {
pr_err("%s:session id %d: cmd evrc media format block failed\n",
__func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed\n",
__func__, audio->ac->session);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
break;
}
case AUDIO_SET_EVRC_ENC_CONFIG: {
struct msm_audio_evrc_enc_config *cfg;
struct msm_audio_evrc_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
cfg = (struct msm_audio_evrc_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer for %s\n",
__func__, "AUDIO_SET_EVRC_ENC_CONFIG");
rc = -EINVAL;
break;
}
if (cfg->min_bit_rate > 4 ||
cfg->min_bit_rate < 1 ||
(cfg->min_bit_rate == 2)) {
pr_err("%s:session id %d: invalid min bitrate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
if (cfg->max_bit_rate > 4 ||
cfg->max_bit_rate < 1 ||
(cfg->max_bit_rate == 2)) {
pr_err("%s:session id %d: invalid max bitrate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
enc_cfg->min_bit_rate = cfg->min_bit_rate;
enc_cfg->max_bit_rate = cfg->max_bit_rate;
pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
__func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
static long evrc_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = evrc_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_EVRC_ENC_CONFIG: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_evrc_enc_config))) {
pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_EVRC_ENC_CONFIG: {
struct msm_audio_evrc_enc_config cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(struct msm_audio_evrc_enc_config))) {
pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG failed\n",
__func__);
rc = -EFAULT;
break;
}
rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_evrc_enc_config32 {
u32 cdma_rate;
u32 min_bit_rate;
u32 max_bit_rate;
};
enum {
AUDIO_SET_EVRC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
2, struct msm_audio_evrc_enc_config32),
AUDIO_GET_EVRC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
3, struct msm_audio_evrc_enc_config32)
};
static long evrc_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = evrc_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_EVRC_ENC_CONFIG_32: {
struct msm_audio_evrc_enc_config32 cfg_32;
struct msm_audio_evrc_enc_config *enc_cfg;
memset(&cfg_32, 0, sizeof(cfg_32));
enc_cfg = audio->enc_cfg;
cfg_32.cdma_rate = enc_cfg->cdma_rate;
cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(cfg_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_EVRC_ENC_CONFIG_32: {
struct msm_audio_evrc_enc_config cfg;
struct msm_audio_evrc_enc_config32 cfg_32;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.cdma_rate = cfg_32.cdma_rate;
cfg.min_bit_rate = cfg_32.min_bit_rate;
cfg.max_bit_rate = cfg_32.max_bit_rate;
cmd = AUDIO_SET_EVRC_ENC_CONFIG;
rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#else
#define evrc_in_compat_ioctl NULL
#endif
static int evrc_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_evrc_enc_config *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 23;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->min_bit_rate = 4;
enc_cfg->max_bit_rate = 4;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open evrc encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: NT mode encoder success\n",
__func__, audio->ac->session);
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_EVRC);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__,
audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: T mode encoder success\n", __func__,
audio->ac->session);
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
audio->reset_event = false;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = evrc_in_compat_ioctl;
audio->enc_ioctl = evrc_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = evrc_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
.compat_ioctl = audio_in_compat_ioctl
};
struct miscdevice audio_evrc_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_evrc_in",
.fops = &audio_in_fops,
};
static int __init evrc_in_init(void)
{
return misc_register(&audio_evrc_in_misc);
}
device_initcall(evrc_in_init);

Melihat File

@@ -0,0 +1,382 @@
/* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_g711.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10))
static long g711_in_ioctl_shared(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_g711_enc_config *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate);
rc = q6asm_enc_cfg_blk_g711(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->sample_rate);
if (rc < 0) {
pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
audio->ac->session, audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session,
rc);
break;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG: {
struct msm_audio_g711_enc_config *cfg;
struct msm_audio_g711_enc_config *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
cfg = (struct msm_audio_g711_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer\n", __func__);
rc = -EINVAL;
break;
}
if (cfg->sample_rate != 8000 &&
cfg->sample_rate != 16000) {
pr_err("%s:session id %d: invalid sample rate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
enc_cfg->sample_rate = cfg->sample_rate;
pr_debug("%s:session id %d: sample_rate= 0x%x",
__func__,
audio->ac->session, enc_cfg->sample_rate);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
static long g711_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = g711_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_G711_ENC_CONFIG: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_g711_enc_config))) {
pr_err(
"%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG: {
struct msm_audio_g711_enc_config cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(cfg))) {
pr_err(
"%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
break;
}
rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_g711_enc_config32 {
uint32_t sample_rate;
};
enum {
AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32),
AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32)
};
static long g711_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = g711_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_G711_ENC_CONFIG_32: {
struct msm_audio_g711_enc_config32 cfg_32;
struct msm_audio_g711_enc_config32 *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg;
cfg_32.sample_rate = enc_cfg->sample_rate;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(cfg_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG_32: {
struct msm_audio_g711_enc_config32 cfg_32;
struct msm_audio_g711_enc_config32 cfg;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.sample_rate = cfg_32.sample_rate;
cmd = AUDIO_SET_G711_ENC_CONFIG;
rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
#else
#define g711_in_compat_ioctl NULL
#endif
static int g711_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_g711_enc_config *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/*
* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 320;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->sample_rate = 8000;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open g711 encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_G711_ALAW_FS,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_G711_ALAW_FS);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
audio->reset_event = false;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = g711_in_compat_ioctl;
audio->enc_ioctl = g711_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = g711_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = audio_in_compat_ioctl,
#endif
};
struct miscdevice audio_g711alaw_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_g711alaw_in",
.fops = &audio_in_fops,
};
static int __init g711alaw_in_init(void)
{
return misc_register(&audio_g711alaw_in_misc);
}
device_initcall(g711alaw_in_init);

Melihat File

@@ -0,0 +1,385 @@
/* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_g711.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
#ifdef CONFIG_COMPAT
#undef PROC_ADD
#endif
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10))
static long g711_in_ioctl_shared(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_g711_enc_config *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate);
rc = q6asm_enc_cfg_blk_g711(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->sample_rate);
if (rc < 0) {
pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
audio->ac->session, audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session,
rc);
break;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG: {
struct msm_audio_g711_enc_config *cfg;
struct msm_audio_g711_enc_config *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
cfg = (struct msm_audio_g711_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer\n", __func__);
rc = -EINVAL;
break;
}
if (cfg->sample_rate != 8000 &&
cfg->sample_rate != 16000) {
pr_err("%s:session id %d: invalid sample rate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
enc_cfg->sample_rate = cfg->sample_rate;
pr_debug("%s:session id %d: sample_rate= 0x%x",
__func__,
audio->ac->session, enc_cfg->sample_rate);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
static long g711_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = g711_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_G711_ENC_CONFIG: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_g711_enc_config))) {
pr_err(
"%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG: {
struct msm_audio_g711_enc_config cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(cfg))) {
pr_err(
"%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
break;
}
rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_g711_enc_config32 {
uint32_t sample_rate;
};
enum {
AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32),
AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32)
};
static long g711_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = g711_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_G711_ENC_CONFIG_32: {
struct msm_audio_g711_enc_config32 cfg_32;
struct msm_audio_g711_enc_config32 *enc_cfg;
enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg;
cfg_32.sample_rate = enc_cfg->sample_rate;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(cfg_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_G711_ENC_CONFIG_32: {
struct msm_audio_g711_enc_config32 cfg_32;
struct msm_audio_g711_enc_config32 cfg;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.sample_rate = cfg_32.sample_rate;
cmd = AUDIO_SET_G711_ENC_CONFIG;
rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -ENOIOCTLCMD;
}
return rc;
}
#else
#define g711_in_compat_ioctl NULL
#endif
static int g711_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_g711_enc_config *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/*
* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 320;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->sample_rate = 8000;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open g711 encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_G711_MLAW_FS,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_G711_MLAW_FS);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
audio->reset_event = false;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = g711_in_compat_ioctl;
audio->enc_ioctl = g711_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = g711_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = audio_in_compat_ioctl,
#endif
};
struct miscdevice audio_g711mlaw_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_g711mlaw_in",
.fops = &audio_in_fops,
};
static int __init g711mlaw_in_init(void)
{
return misc_register(&audio_g711mlaw_in_misc);
}
device_initcall(g711mlaw_in_init);

Melihat File

@@ -0,0 +1,37 @@
/* Copyright (c) 2012-2014, 2017 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.
*
*/
/* For Decoders */
#ifndef __Q6_AUDIO_COMMON_H__
#define __Q6_AUDIO_COMMON_H__
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
void q6_audio_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv);
void audio_aio_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *audio);
/* For Encoders */
void q6asm_in_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv);
void audio_in_get_dsp_frames(void *audio,
uint32_t token, uint32_t *payload);
#endif /*__Q6_AUDIO_COMMON_H__*/

Melihat File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2012-2013, 2015-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
void q6asm_in_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
struct q6audio_in *audio = (struct q6audio_in *)priv;
unsigned long flags;
pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
audio->ac->session, opcode);
spin_lock_irqsave(&audio->dsp_lock, flags);
switch (opcode) {
case ASM_DATA_EVENT_READ_DONE_V2:
audio_in_get_dsp_frames(audio, token, payload);
break;
case ASM_DATA_EVENT_WRITE_DONE_V2:
atomic_inc(&audio->in_count);
wake_up(&audio->write_wait);
break;
case ASM_DATA_EVENT_RENDERED_EOS:
audio->eos_rsp = 1;
wake_up(&audio->read_wait);
break;
case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
break;
case ASM_SESSION_EVENTX_OVERFLOW:
pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
__func__, audio->ac->session);
break;
case RESET_EVENTS:
pr_debug("%s:received RESET EVENTS\n", __func__);
audio->enabled = 0;
audio->stopped = 1;
audio->event_abort = 1;
audio->reset_event = true;
wake_up(&audio->read_wait);
wake_up(&audio->write_wait);
break;
default:
pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
audio->ac->session, opcode);
break;
}
spin_unlock_irqrestore(&audio->dsp_lock, flags);
}
void audio_in_get_dsp_frames(void *priv,
uint32_t token, uint32_t *payload)
{
struct q6audio_in *audio = (struct q6audio_in *)priv;
uint32_t index;
index = q6asm_get_buf_index_from_token(token);
pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
__func__, audio->ac->session, token, payload[9],
payload[5]);
pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
audio->ac->session, payload[7], payload[6]);
pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
audio->ac->session, payload[8], payload[10]);
pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__,
audio->ac->session, payload[4]);
/* Ensure the index is within max array size: FRAME_NUM */
if (index >= FRAME_NUM) {
pr_err("%s: Invalid index %d\n",
__func__, index);
return;
}
audio->out_frame_info[index][0] = payload[9];
audio->out_frame_info[index][1] = payload[5];
/* statistics of read */
atomic_add(payload[4], &audio->in_bytes);
atomic_add(payload[9], &audio->in_samples);
if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
atomic_inc(&audio->out_count);
wake_up(&audio->read_wait);
}
}

Melihat File

@@ -0,0 +1,223 @@
/* Copyright (c) 2012-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils_aio.h"
void q6_audio_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv)
{
struct q6audio_aio *audio = (struct q6audio_aio *)priv;
pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2:
case ASM_DATA_EVENT_READ_DONE_V2:
case ASM_DATA_EVENT_RENDERED_EOS:
case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
case RESET_EVENTS:
audio_aio_cb(opcode, token, payload, audio);
break;
default:
pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
break;
}
}
void audio_aio_cb(uint32_t opcode, uint32_t token,
uint32_t *payload, void *priv/*struct q6audio_aio *audio*/)
{
struct q6audio_aio *audio = (struct q6audio_aio *)priv;
union msm_audio_event_payload e_payload;
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2:
pr_debug("%s[%pK]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
__func__, audio, token);
audio_aio_async_write_ack(audio, token, payload);
break;
case ASM_DATA_EVENT_READ_DONE_V2:
pr_debug("%s[%pK]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
__func__, audio, token);
audio_aio_async_read_ack(audio, token, payload);
break;
case ASM_DATA_EVENT_RENDERED_EOS:
/* EOS Handle */
pr_debug("%s[%pK]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
if (audio->feedback) { /* Non-Tunnel mode */
audio->eos_rsp = 1;
/* propagate input EOS i/p buffer,
* after receiving DSP acknowledgment
*/
if (audio->eos_flag &&
(audio->eos_write_payload.aio_buf.buf_addr)) {
audio_aio_post_event(audio,
AUDIO_EVENT_WRITE_DONE,
audio->eos_write_payload);
memset(&audio->eos_write_payload, 0,
sizeof(union msm_audio_event_payload));
audio->eos_flag = 0;
}
} else { /* Tunnel mode */
audio->eos_rsp = 1;
wake_up(&audio->write_wait);
wake_up(&audio->cmd_wait);
}
break;
case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
pr_debug("%s[%pK]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
__func__, audio, payload[0], payload[1], opcode);
break;
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
__func__, audio, payload[0],
payload[1], payload[2], payload[3]);
pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
__func__, audio, audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
audio->pcm_cfg.sample_rate = payload[0];
audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
break;
case RESET_EVENTS:
pr_err("%s: Received opcode:0x%x\n", __func__, opcode);
audio->stopped = 1;
audio->reset_event = true;
wake_up(&audio->event_wait);
break;
default:
break;
}
}
void extract_meta_out_info(struct q6audio_aio *audio,
struct audio_aio_buffer_node *buf_node, int dir)
{
struct dec_meta_out *meta_data = buf_node->kvaddr;
uint32_t temp;
if (dir) { /* input buffer - Write */
if (audio->buf_cfg.meta_info_enable)
memcpy(&buf_node->meta_info.meta_in,
(char *)buf_node->kvaddr, sizeof(struct dec_meta_in));
else
memset(&buf_node->meta_info.meta_in,
0, sizeof(struct dec_meta_in));
pr_debug("%s[%pK]:i/p: msw_ts %d lsw_ts %d nflags 0x%8x\n",
__func__, audio,
buf_node->meta_info.meta_in.ntimestamp.highpart,
buf_node->meta_info.meta_in.ntimestamp.lowpart,
buf_node->meta_info.meta_in.nflags);
} else { /* output buffer - Read */
memcpy((char *)buf_node->kvaddr,
&buf_node->meta_info.meta_out,
sizeof(struct dec_meta_out));
meta_data->meta_out_dsp[0].nflags = 0x00000000;
temp = meta_data->meta_out_dsp[0].msw_ts;
meta_data->meta_out_dsp[0].msw_ts =
meta_data->meta_out_dsp[0].lsw_ts;
meta_data->meta_out_dsp[0].lsw_ts = temp;
pr_debug("%s[%pK]:o/p: msw_ts %d lsw_ts %d nflags 0x%8x, num_frames = %d\n",
__func__, audio,
((struct dec_meta_out *)buf_node->kvaddr)->
meta_out_dsp[0].msw_ts,
((struct dec_meta_out *)buf_node->kvaddr)->
meta_out_dsp[0].lsw_ts,
((struct dec_meta_out *)buf_node->kvaddr)->
meta_out_dsp[0].nflags,
((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames);
}
}
/* Read buffer from DSP / Handle Ack from DSP */
void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
uint32_t *payload)
{
unsigned long flags;
union msm_audio_event_payload event_payload;
struct audio_aio_buffer_node *filled_buf;
pr_debug("%s\n", __func__);
/* No active flush in progress */
if (audio->rflush)
return;
/* Statistics of read */
atomic_add(payload[4], &audio->in_bytes);
atomic_add(payload[9], &audio->in_samples);
spin_lock_irqsave(&audio->dsp_lock, flags);
if (list_empty(&audio->in_queue)) {
spin_unlock_irqrestore(&audio->dsp_lock, flags);
pr_warn("%s unexpected ack from dsp\n", __func__);
return;
}
filled_buf = list_first_entry(&audio->in_queue,
struct audio_aio_buffer_node, list);
pr_debug("%s token: 0x[%x], filled_buf->token: 0x[%x]",
__func__, token, filled_buf->token);
if (token == (filled_buf->token)) {
list_del(&filled_buf->list);
spin_unlock_irqrestore(&audio->dsp_lock, flags);
event_payload.aio_buf = filled_buf->buf;
/* Read done Buffer due to flush/normal condition
* after EOS event, so append EOS buffer
*/
if (audio->eos_rsp == 0x1) {
event_payload.aio_buf.data_len =
insert_eos_buf(audio, filled_buf);
/* Reset flag back to indicate eos intimated */
audio->eos_rsp = 0;
} else {
filled_buf->meta_info.meta_out.num_of_frames
= payload[9];
event_payload.aio_buf.data_len = payload[4]
+ payload[5] + sizeof(struct dec_meta_out);
pr_debug("%s[%pK]:nr of frames 0x%8x len=%d\n",
__func__, audio,
filled_buf->meta_info.meta_out.num_of_frames,
event_payload.aio_buf.data_len);
extract_meta_out_info(audio, filled_buf, 0);
audio->eos_rsp = 0;
}
pr_debug("%s, posting read done to the app here\n", __func__);
audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
event_payload);
kfree(filled_buf);
} else {
pr_err("%s[%pK]:expected=%x ret=%x\n",
__func__, audio, filled_buf->token, token);
spin_unlock_irqrestore(&audio->dsp_lock, flags);
}
}

Melihat File

@@ -0,0 +1,410 @@
/* Copyright (c) 2010-2017, 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_qcp.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
/* Buffer with meta*/
#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
/* Maximum 10 frames in buffer with meta */
#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10))
static long qcelp_in_ioctl_shared(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
int cnt = 0;
switch (cmd) {
case AUDIO_START: {
struct msm_audio_qcelp_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
audio->ac->session, audio->buf_alloc);
if (audio->enabled == 1) {
pr_info("%s:AUDIO_START already over\n", __func__);
rc = 0;
break;
}
rc = audio_in_buf_alloc(audio);
if (rc < 0) {
pr_err("%s:session id %d: buffer allocation failed\n",
__func__, audio->ac->session);
break;
}
/* reduced_rate_level, rate_modulation_cmd set to zero
* currently not configurable from user space
*/
rc = q6asm_enc_cfg_blk_qcelp(audio->ac,
audio->buf_cfg.frames_per_buf,
enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate, 0, 0);
if (rc < 0) {
pr_err("%s:session id %d: cmd qcelp media format block failed\n",
__func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
rc = q6asm_media_format_block_pcm(audio->ac,
audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
if (rc < 0) {
pr_err("%s:session id %d: media format block failed\n",
__func__, audio->ac->session);
break;
}
}
pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
audio->ac->session, audio->enabled);
rc = audio_in_enable(audio);
if (!rc) {
audio->enabled = 1;
} else {
audio->enabled = 0;
pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
__func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
q6asm_read(audio->ac); /* Push buffer to DSP */
rc = 0;
pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
__func__, audio->ac->session, audio->enabled);
break;
}
case AUDIO_STOP: {
pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
__func__, audio->ac->session,
rc);
break;
}
break;
}
case AUDIO_SET_QCELP_ENC_CONFIG: {
struct msm_audio_qcelp_enc_config *cfg;
struct msm_audio_qcelp_enc_config *enc_cfg;
enc_cfg = audio->enc_cfg;
cfg = (struct msm_audio_qcelp_enc_config *)arg;
if (cfg == NULL) {
pr_err("%s: NULL config pointer\n", __func__);
rc = -EINVAL;
break;
}
if (cfg->min_bit_rate > 4 ||
cfg->min_bit_rate < 1) {
pr_err("%s:session id %d: invalid min bitrate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
if (cfg->max_bit_rate > 4 ||
cfg->max_bit_rate < 1) {
pr_err("%s:session id %d: invalid max bitrate\n",
__func__, audio->ac->session);
rc = -EINVAL;
break;
}
enc_cfg->cdma_rate = cfg->cdma_rate;
enc_cfg->min_bit_rate = cfg->min_bit_rate;
enc_cfg->max_bit_rate = cfg->max_bit_rate;
pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
__func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
static long qcelp_in_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = qcelp_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_QCELP_ENC_CONFIG: {
if (copy_to_user((void *)arg, audio->enc_cfg,
sizeof(struct msm_audio_qcelp_enc_config))) {
pr_err(
"%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_QCELP_ENC_CONFIG: {
struct msm_audio_qcelp_enc_config cfg;
if (copy_from_user(&cfg, (void *) arg,
sizeof(cfg))) {
pr_err(
"%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG failed",
__func__);
rc = -EFAULT;
break;
}
rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. Rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#ifdef CONFIG_COMPAT
struct msm_audio_qcelp_enc_config32 {
u32 cdma_rate;
u32 min_bit_rate;
u32 max_bit_rate;
};
enum {
AUDIO_SET_QCELP_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
0, struct msm_audio_qcelp_enc_config32),
AUDIO_GET_QCELP_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
1, struct msm_audio_qcelp_enc_config32)
};
static long qcelp_in_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct q6audio_in *audio = file->private_data;
int rc = 0;
switch (cmd) {
case AUDIO_START:
case AUDIO_STOP: {
rc = qcelp_in_ioctl_shared(file, cmd, arg);
break;
}
case AUDIO_GET_QCELP_ENC_CONFIG_32: {
struct msm_audio_qcelp_enc_config32 cfg_32;
struct msm_audio_qcelp_enc_config *enc_cfg;
memset(&cfg_32, 0, sizeof(cfg_32));
enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg;
cfg_32.cdma_rate = enc_cfg->cdma_rate;
cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
if (copy_to_user((void *)arg, &cfg_32,
sizeof(cfg_32))) {
pr_err("%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
}
break;
}
case AUDIO_SET_QCELP_ENC_CONFIG_32: {
struct msm_audio_qcelp_enc_config32 cfg_32;
struct msm_audio_qcelp_enc_config cfg;
if (copy_from_user(&cfg_32, (void *) arg,
sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG_32 failed\n",
__func__);
rc = -EFAULT;
break;
}
cfg.cdma_rate = cfg_32.cdma_rate;
cfg.min_bit_rate = cfg_32.min_bit_rate;
cfg.max_bit_rate = cfg_32.max_bit_rate;
cmd = AUDIO_SET_QCELP_ENC_CONFIG;
rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
if (rc)
pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. rc= %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
}
#else
#define qcelp_in_compat_ioctl NULL
#endif
static int qcelp_in_open(struct inode *inode, struct file *file)
{
struct q6audio_in *audio = NULL;
struct msm_audio_qcelp_enc_config *enc_cfg;
int rc = 0;
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL)
return -ENOMEM;
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
kfree(audio);
return -ENOMEM;
}
enc_cfg = audio->enc_cfg;
mutex_init(&audio->lock);
mutex_init(&audio->read_lock);
mutex_init(&audio->write_lock);
spin_lock_init(&audio->dsp_lock);
init_waitqueue_head(&audio->read_wait);
init_waitqueue_head(&audio->write_wait);
/* Settings will be re-config at AUDIO_SET_CONFIG,
* but at least we need to have initial config
*/
audio->str_cfg.buffer_size = FRAME_SIZE;
audio->str_cfg.buffer_count = FRAME_NUM;
audio->min_frame_size = 35;
audio->max_frames_per_buf = 10;
audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
enc_cfg->min_bit_rate = 4;
enc_cfg->max_bit_rate = 4;
audio->pcm_cfg.channel_count = 1;
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
pr_err("%s: Could not allocate memory for audio client\n",
__func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
}
/* open qcelp encoder in T/NT mode */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = NON_TUNNEL_MODE;
rc = q6asm_open_read_write(audio->ac, FORMAT_V13K,
FORMAT_LINEAR_PCM);
if (rc < 0) {
pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: NT mode encoder success\n", __func__,
audio->ac->session);
} else if (!(file->f_mode & FMODE_WRITE) &&
(file->f_mode & FMODE_READ)) {
audio->feedback = TUNNEL_MODE;
rc = q6asm_open_read(audio->ac, FORMAT_V13K);
if (rc < 0) {
pr_err("%s:session id %d: T mode Open failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
__func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
pr_info("%s:session id %d: T mode encoder success\n", __func__,
audio->ac->session);
} else {
pr_err("%s:session id %d: Unexpected mode\n", __func__,
audio->ac->session);
rc = -EACCES;
goto fail;
}
audio->opened = 1;
audio->reset_event = false;
atomic_set(&audio->in_count, PCM_BUF_COUNT);
atomic_set(&audio->out_count, 0x00);
audio->enc_compat_ioctl = qcelp_in_compat_ioctl;
audio->enc_ioctl = qcelp_in_ioctl;
file->private_data = audio;
pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
return 0;
fail:
q6asm_audio_client_free(audio->ac);
kfree(audio->enc_cfg);
kfree(audio);
return rc;
}
static const struct file_operations audio_in_fops = {
.owner = THIS_MODULE,
.open = qcelp_in_open,
.release = audio_in_release,
.read = audio_in_read,
.write = audio_in_write,
.unlocked_ioctl = audio_in_ioctl,
.compat_ioctl = audio_in_compat_ioctl
};
struct miscdevice audio_qcelp_in_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_qcelp_in",
.fops = &audio_in_fops,
};
static int __init qcelp_in_init(void)
{
return misc_register(&audio_qcelp_in_misc);
}
device_initcall(qcelp_in_init);

Melihat File

@@ -0,0 +1,2 @@
ccflags-y := -I$(src)/..
obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,130 @@
/* Copyright (c) 2011-2014, 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 __Q6_USM_H__
#define __Q6_USM_H__
#include <linux/qdsp6v2/apr_us.h>
#define Q6USM_EVENT_UNDEF 0
#define Q6USM_EVENT_READ_DONE 1
#define Q6USM_EVENT_WRITE_DONE 2
#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3
/* cyclic buffer with 1 gap support */
#define USM_MIN_BUF_CNT 3
#define FORMAT_USPS_EPOS 0x00000000
#define FORMAT_USRAW 0x00000001
#define FORMAT_USPROX 0x00000002
#define FORMAT_USGES_SYNC 0x00000003
#define FORMAT_USRAW_SYNC 0x00000004
#define INVALID_FORMAT 0xffffffff
#define IN 0x000
#define OUT 0x001
#define USM_WRONG_TOKEN 0xffffffff
#define USM_UNDEF_TOKEN 0xfffffffe
#define CMD_CLOSE 0x0004
/* bit 0:1 represents priority of stream */
#define STREAM_PRIORITY_NORMAL 0x0000
#define STREAM_PRIORITY_LOW 0x0001
#define STREAM_PRIORITY_HIGH 0x0002
/* bit 4 represents META enable of encoded data buffer */
#define BUFFER_META_ENABLE 0x0010
struct us_port_data {
dma_addr_t phys;
/* cyclic region of buffers with 1 gap */
void *data;
/* number of buffers in the region */
uint32_t buf_cnt;
/* size of buffer */
size_t buf_size;
/* write index */
uint32_t dsp_buf;
/* read index */
uint32_t cpu_buf;
/* expected token from dsp */
uint32_t expected_token;
/* read or write locks */
struct mutex lock;
spinlock_t dsp_lock;
/* ION memory handle */
struct ion_handle *handle;
/* ION memory client */
struct ion_client *client;
/* extended parameters, related to q6 variants */
void *ext;
/* physical address of parameter buffer */
dma_addr_t param_phys;
/* buffer which stores the parameter data */
void *param_buf;
/* size of parameter buffer */
uint32_t param_buf_size;
/* parameter buffer memory handle */
void *param_buf_mem_handle;
/* ION memory handle for parameter buffer */
struct ion_handle *param_handle;
/* ION memory client for parameter buffer */
struct ion_client *param_client;
};
struct us_client {
int session;
/* idx:1 out port, 0: in port*/
struct us_port_data port[2];
struct apr_svc *apr;
struct mutex cmd_lock;
atomic_t cmd_state;
atomic_t eos_state;
wait_queue_head_t cmd_wait;
void (*cb)(uint32_t, uint32_t, uint32_t *, void *);
void *priv;
};
int q6usm_run(struct us_client *usc, uint32_t flags,
uint32_t msw_ts, uint32_t lsw_ts);
int q6usm_cmd(struct us_client *usc, int cmd);
int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc,
unsigned int bufsz, unsigned int bufcnt);
int q6usm_us_param_buf_alloc(unsigned int dir, struct us_client *usc,
unsigned int bufsz);
int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
int q6usm_read(struct us_client *usc, uint32_t read_ind);
struct us_client *q6usm_us_client_alloc(
void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
void *priv);
int q6usm_open_read(struct us_client *usc, uint32_t format);
void q6usm_us_client_free(struct us_client *usc);
uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc,
struct vm_area_struct *vms);
int q6usm_open_write(struct us_client *usc, uint32_t format);
int q6usm_write(struct us_client *usc, uint32_t write_ind);
bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region);
int q6usm_set_us_detection(struct us_client *usc,
struct usm_session_cmd_detect_info *detect_info,
uint16_t detect_info_size);
int q6usm_set_us_stream_param(int dir, struct us_client *usc,
uint32_t module_id, uint32_t param_id, uint32_t buf_size);
int q6usm_get_us_stream_param(int dir, struct us_client *usc,
uint32_t module_id, uint32_t param_id, uint32_t buf_size);
#endif /* __Q6_USM_H__ */

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,422 @@
/* Copyright (c) 2012-2013, 2016-2017 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/sched.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/syscalls.h>
#include "usfcdev.h"
#define UNDEF_ID 0xffffffff
#define SLOT_CMD_ID 0
#define MAX_RETRIES 10
enum usdev_event_status {
USFCDEV_EVENT_ENABLED,
USFCDEV_EVENT_DISABLING,
USFCDEV_EVENT_DISABLED,
};
struct usfcdev_event {
bool (*match_cb)(uint16_t, struct input_dev *dev);
bool registered_event;
bool interleaved;
enum usdev_event_status event_status;
};
static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM];
struct usfcdev_input_command {
unsigned int type;
unsigned int code;
unsigned int value;
};
static long s_usf_pid;
static bool usfcdev_filter(struct input_handle *handle,
unsigned int type, unsigned int code, int value);
static bool usfcdev_match(struct input_handler *handler,
struct input_dev *dev);
static int usfcdev_connect(struct input_handler *handler,
struct input_dev *dev,
const struct input_device_id *id);
static void usfcdev_disconnect(struct input_handle *handle);
static const struct input_device_id usfc_tsc_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
/* assumption: ABS_X & ABS_Y are in the same long */
.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) |
BIT_MASK(ABS_Y) },
},
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
/* assumption: MT_.._X & MT_.._Y are in the same long */
.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
BIT_MASK(ABS_MT_POSITION_X) |
BIT_MASK(ABS_MT_POSITION_Y) },
},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, usfc_tsc_ids);
static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = {
{ /* TSC handler */
.filter = usfcdev_filter,
.match = usfcdev_match,
.connect = usfcdev_connect,
.disconnect = usfcdev_disconnect,
/* .minor can be used as index in the container, */
/* because .fops isn't supported */
.minor = TSC_EVENT_TYPE_IND,
.name = "usfc_tsc_handler",
.id_table = usfc_tsc_ids,
},
};
/*
* For each event type, there are a number conflicting devices (handles)
* The first registered device (primary) is real TSC device; it's mandatory
* Optionally, later registered devices are simulated ones.
* They are dynamically managed
* The primary device's handles are stored in the below static array
*/
static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = {
{ /* TSC handle */
.handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND],
.name = "usfc_tsc_handle",
},
};
static struct usfcdev_input_command initial_clear_cmds[] = {
{EV_ABS, ABS_PRESSURE, 0},
{EV_KEY, BTN_TOUCH, 0},
};
static struct usfcdev_input_command slot_clear_cmds[] = {
{EV_ABS, ABS_MT_SLOT, 0},
{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
};
static struct usfcdev_input_command no_filter_cmds[] = {
{EV_ABS, ABS_MT_SLOT, 0},
{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
{EV_SYN, SYN_REPORT, 0},
};
static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev)
{
bool rc = false;
int ind = handler->minor;
pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind);
if (s_usfcdev_events[ind].registered_event &&
s_usfcdev_events[ind].match_cb) {
rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev);
pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc);
}
return rc;
}
static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
int ret = 0;
uint16_t ind = handler->minor;
struct input_handle *usfc_handle = NULL;
if (s_usfc_primary_handles[ind].dev == NULL) {
pr_debug("%s: primary device; ind=%d\n",
__func__,
ind);
usfc_handle = &s_usfc_primary_handles[ind];
} else {
pr_debug("%s: secondary device; ind=%d\n",
__func__,
ind);
usfc_handle = kzalloc(sizeof(struct input_handle),
GFP_KERNEL);
if (!usfc_handle)
return -ENOMEM;
usfc_handle->handler = &s_usfc_handlers[ind];
usfc_handle->name = s_usfc_primary_handles[ind].name;
}
usfc_handle->dev = dev;
ret = input_register_handle(usfc_handle);
pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n",
__func__,
dev->name,
ind,
usfc_handle->dev);
if (ret)
pr_err("%s: input_register_handle[%d] failed: ret=%d\n",
__func__,
ind,
ret);
else {
ret = input_open_device(usfc_handle);
if (ret) {
pr_err("%s: input_open_device[%d] failed: ret=%d\n",
__func__,
ind,
ret);
input_unregister_handle(usfc_handle);
} else
pr_debug("%s: device[%d] is opened\n",
__func__,
ind);
}
return ret;
}
static void usfcdev_disconnect(struct input_handle *handle)
{
int ind = handle->handler->minor;
input_close_device(handle);
input_unregister_handle(handle);
pr_debug("%s: handle[%d], name=[%s] is disconnected\n",
__func__,
ind,
handle->dev->name);
if (s_usfc_primary_handles[ind].dev == handle->dev)
s_usfc_primary_handles[ind].dev = NULL;
else
kfree(handle);
}
static bool usfcdev_filter(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
uint16_t i = 0;
uint16_t ind = (uint16_t)handle->handler->minor;
bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED);
if (s_usf_pid == sys_getpid()) {
/* Pass events from usfcdev driver */
rc = false;
pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d",
__func__,
ind,
type,
code,
value);
} else if (s_usfcdev_events[ind].event_status ==
USFCDEV_EVENT_DISABLING) {
uint32_t u_value = value;
s_usfcdev_events[ind].interleaved = true;
/* Pass events for freeing slots from TSC driver */
for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) {
if ((no_filter_cmds[i].type == type) &&
(no_filter_cmds[i].code == code) &&
(no_filter_cmds[i].value <= u_value)) {
rc = false;
pr_debug("%s: no_filter_cmds[%d]; %d",
__func__,
i,
no_filter_cmds[i].value);
break;
}
}
}
return rc;
}
bool usfcdev_register(
uint16_t event_type_ind,
bool (*match_cb)(uint16_t, struct input_dev *dev))
{
int ret = 0;
bool rc = false;
if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) {
pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n",
__func__,
event_type_ind,
match_cb);
return false;
}
if (s_usfcdev_events[event_type_ind].registered_event) {
pr_info("%s: handler[%d] was already registered\n",
__func__,
event_type_ind);
return true;
}
s_usfcdev_events[event_type_ind].registered_event = true;
s_usfcdev_events[event_type_ind].match_cb = match_cb;
s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED;
ret = input_register_handler(&s_usfc_handlers[event_type_ind]);
if (!ret) {
rc = true;
pr_debug("%s: handler[%d] was registered\n",
__func__,
event_type_ind);
} else {
s_usfcdev_events[event_type_ind].registered_event = false;
s_usfcdev_events[event_type_ind].match_cb = NULL;
pr_err("%s: handler[%d] registration failed: ret=%d\n",
__func__,
event_type_ind,
ret);
}
return rc;
}
void usfcdev_unregister(uint16_t event_type_ind)
{
if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
pr_err("%s: wrong input: event_type_ind=%d\n",
__func__,
event_type_ind);
return;
}
if (s_usfcdev_events[event_type_ind].registered_event) {
input_unregister_handler(&s_usfc_handlers[event_type_ind]);
pr_debug("%s: handler[%d] was unregistered\n",
__func__,
event_type_ind);
s_usfcdev_events[event_type_ind].registered_event = false;
s_usfcdev_events[event_type_ind].match_cb = NULL;
s_usfcdev_events[event_type_ind].event_status =
USFCDEV_EVENT_ENABLED;
}
}
static inline void usfcdev_send_cmd(
struct input_dev *dev,
struct usfcdev_input_command cmd)
{
input_event(dev, cmd.type, cmd.code, cmd.value);
}
static void usfcdev_clean_dev(uint16_t event_type_ind)
{
struct input_dev *dev = NULL;
int i;
int j;
int retries = 0;
if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
pr_err("%s: wrong input: event_type_ind=%d\n",
__func__,
event_type_ind);
return;
}
/* Only primary device must exist */
dev = s_usfc_primary_handles[event_type_ind].dev;
if (dev == NULL) {
pr_err("%s: NULL primary device\n",
__func__);
return;
}
for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++)
usfcdev_send_cmd(dev, initial_clear_cmds[i]);
input_sync(dev);
/* Send commands to free all slots */
for (i = 0; i < dev->mt->num_slots; i++) {
s_usfcdev_events[event_type_ind].interleaved = false;
if (input_mt_get_value(&dev->mt->slots[i],
ABS_MT_TRACKING_ID) < 0) {
pr_debug("%s: skipping slot %d",
__func__, i);
continue;
}
slot_clear_cmds[SLOT_CMD_ID].value = i;
for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++)
usfcdev_send_cmd(dev, slot_clear_cmds[j]);
if (s_usfcdev_events[event_type_ind].interleaved) {
pr_debug("%s: interleaved(%d): slot(%d)",
__func__, i, dev->mt->slot);
if (retries++ < MAX_RETRIES) {
--i;
continue;
}
pr_warn("%s: index(%d) reached max retires",
__func__, i);
}
retries = 0;
input_sync(dev);
}
}
bool usfcdev_set_filter(uint16_t event_type_ind, bool filter)
{
bool rc = true;
if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
pr_err("%s: wrong input: event_type_ind=%d\n",
__func__,
event_type_ind);
return false;
}
if (s_usfcdev_events[event_type_ind].registered_event) {
pr_debug("%s: event_type[%d]; filter=%d\n",
__func__,
event_type_ind,
filter
);
if (filter) {
s_usfcdev_events[event_type_ind].event_status =
USFCDEV_EVENT_DISABLING;
s_usf_pid = sys_getpid();
usfcdev_clean_dev(event_type_ind);
s_usfcdev_events[event_type_ind].event_status =
USFCDEV_EVENT_DISABLED;
} else
s_usfcdev_events[event_type_ind].event_status =
USFCDEV_EVENT_ENABLED;
} else {
pr_err("%s: event_type[%d] isn't registered\n",
__func__,
event_type_ind);
rc = false;
}
return rc;
}
static int __init usfcdev_init(void)
{
return 0;
}
device_initcall(usfcdev_init);
MODULE_DESCRIPTION("Handle of events from devices, conflicting with USF");

Melihat File

@@ -0,0 +1,28 @@
/* Copyright (c) 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.
*
*/
#ifndef __USFCDEV_H__
#define __USFCDEV_H__
#include <linux/input.h>
/* TSC event type index in the containers of the handlers & handles */
#define TSC_EVENT_TYPE_IND 0
/* Number of supported event types to be filtered */
#define MAX_EVENT_TYPE_NUM 1
bool usfcdev_register(
uint16_t event_type_ind,
bool (*match_cb)(uint16_t, struct input_dev *dev));
void usfcdev_unregister(uint16_t event_type_ind);
bool usfcdev_set_filter(uint16_t event_type_ind, bool filter);
#endif /* __USFCDEV_H__ */

2
drivers/pinctrl/Makefile Normal file
Melihat File

@@ -0,0 +1,2 @@
obj-y += qcom/

1
drivers/pinctrl/core.h Symbolic link
Melihat File

@@ -0,0 +1 @@
../../../../drivers/pinctrl/core.h

Melihat File

@@ -0,0 +1 @@
../../../../drivers/pinctrl/pinctrl-utils.h

Melihat File

@@ -0,0 +1,3 @@
obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o
obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o

Melihat File

@@ -0,0 +1,647 @@
/*
* Copyright (c) 2016-2017, 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/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/qdsp6v2/audio_notifier.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "../core.h"
#include "../pinctrl-utils.h"
#define LPI_ADDRESS_SIZE 0xC000
#define LPI_GPIO_REG_VAL_CTL 0x00
#define LPI_GPIO_REG_DIR_CTL 0x04
#define LPI_GPIO_REG_PULL_SHIFT 0x0
#define LPI_GPIO_REG_PULL_MASK 0x3
#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
#define LPI_GPIO_REG_FUNCTION_MASK 0x3C
#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0
#define LPI_GPIO_REG_OE_SHIFT 0x9
#define LPI_GPIO_REG_OE_MASK 0x200
#define LPI_GPIO_REG_DIR_SHIFT 0x1
#define LPI_GPIO_REG_DIR_MASK 0x2
#define LPI_GPIO_BIAS_DISABLE 0x0
#define LPI_GPIO_PULL_DOWN 0x1
#define LPI_GPIO_KEEPER 0x2
#define LPI_GPIO_PULL_UP 0x3
#define LPI_GPIO_FUNC_GPIO "gpio"
#define LPI_GPIO_FUNC_FUNC1 "func1"
#define LPI_GPIO_FUNC_FUNC2 "func2"
#define LPI_GPIO_FUNC_FUNC3 "func3"
#define LPI_GPIO_FUNC_FUNC4 "func4"
#define LPI_GPIO_FUNC_FUNC5 "func5"
static bool lpi_dev_up;
/* The index of each function in lpi_gpio_functions[] array */
enum lpi_gpio_func_index {
LPI_GPIO_FUNC_INDEX_GPIO = 0x00,
LPI_GPIO_FUNC_INDEX_FUNC1 = 0x01,
LPI_GPIO_FUNC_INDEX_FUNC2 = 0x02,
LPI_GPIO_FUNC_INDEX_FUNC3 = 0x03,
LPI_GPIO_FUNC_INDEX_FUNC4 = 0x04,
LPI_GPIO_FUNC_INDEX_FUNC5 = 0x05,
};
/**
* struct lpi_gpio_pad - keep current GPIO settings
* @offset: Nth GPIO in supported GPIOs.
* @output_enabled: Set to true if GPIO output logic is enabled.
* @value: value of a pin
* @base: Address base of LPI GPIO PAD.
* @pullup: Constant current which flow through GPIO output buffer.
* @strength: No, Low, Medium, High
* @function: See lpi_gpio_functions[]
*/
struct lpi_gpio_pad {
u16 offset;
bool output_enabled;
bool value;
char __iomem *base;
unsigned int pullup;
unsigned int strength;
unsigned int function;
};
struct lpi_gpio_state {
struct device *dev;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
char __iomem *base;
};
static const char *const lpi_gpio_groups[] = {
"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
"gpio29", "gpio30", "gpio31",
};
static const u32 lpi_offset[] = {
0x00000000,
0x00001000,
0x00002000,
0x00002010,
0x00003000,
0x00003010,
0x00004000,
0x00004010,
0x00005000,
0x00005010,
0x00005020,
0x00005030,
0x00006000,
0x00006010,
0x00007000,
0x00007010,
0x00005040,
0x00005050,
0x00008000,
0x00008010,
0x00008020,
0x00008030,
0x00008040,
0x00008050,
0x00008060,
0x00008070,
0x00009000,
0x00009010,
0x0000A000,
0x0000A010,
0x0000B000,
0x0000B010,
};
static const char *const lpi_gpio_functions[] = {
[LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO,
[LPI_GPIO_FUNC_INDEX_FUNC1] = LPI_GPIO_FUNC_FUNC1,
[LPI_GPIO_FUNC_INDEX_FUNC2] = LPI_GPIO_FUNC_FUNC2,
[LPI_GPIO_FUNC_INDEX_FUNC3] = LPI_GPIO_FUNC_FUNC3,
[LPI_GPIO_FUNC_INDEX_FUNC4] = LPI_GPIO_FUNC_FUNC4,
[LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5,
};
static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
{
int ret;
if (!lpi_dev_up) {
pr_err_ratelimited("%s: ADSP is down due to SSR, return\n",
__func__);
return 0;
}
ret = ioread32(pad->base + pad->offset + addr);
if (ret < 0)
pr_err("%s: read 0x%x failed\n", __func__, addr);
return ret;
}
static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr,
unsigned int val)
{
if (!lpi_dev_up) {
pr_err_ratelimited("%s: ADSP is down due to SSR, return\n",
__func__);
return 0;
}
iowrite32(val, pad->base + pad->offset + addr);
return 0;
}
static int lpi_gpio_get_groups_count(struct pinctrl_dev *pctldev)
{
/* Every PIN is a group */
return pctldev->desc->npins;
}
static const char *lpi_gpio_get_group_name(struct pinctrl_dev *pctldev,
unsigned int pin)
{
return pctldev->desc->pins[pin].name;
}
static int lpi_gpio_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int pin,
const unsigned int **pins,
unsigned int *num_pins)
{
*pins = &pctldev->desc->pins[pin].number;
*num_pins = 1;
return 0;
}
static const struct pinctrl_ops lpi_gpio_pinctrl_ops = {
.get_groups_count = lpi_gpio_get_groups_count,
.get_group_name = lpi_gpio_get_group_name,
.get_group_pins = lpi_gpio_get_group_pins,
.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
.dt_free_map = pinctrl_utils_free_map,
};
static int lpi_gpio_get_functions_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(lpi_gpio_functions);
}
static const char *lpi_gpio_get_function_name(struct pinctrl_dev *pctldev,
unsigned int function)
{
return lpi_gpio_functions[function];
}
static int lpi_gpio_get_function_groups(struct pinctrl_dev *pctldev,
unsigned int function,
const char *const **groups,
unsigned *const num_qgroups)
{
*groups = lpi_gpio_groups;
*num_qgroups = pctldev->desc->npins;
return 0;
}
static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
unsigned int pin)
{
struct lpi_gpio_pad *pad;
unsigned int val;
pad = pctldev->desc->pins[pin].drv_data;
pad->function = function;
val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
val &= ~(LPI_GPIO_REG_FUNCTION_MASK);
val |= pad->function << LPI_GPIO_REG_FUNCTION_SHIFT;
lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val);
return 0;
}
static const struct pinmux_ops lpi_gpio_pinmux_ops = {
.get_functions_count = lpi_gpio_get_functions_count,
.get_function_name = lpi_gpio_get_function_name,
.get_function_groups = lpi_gpio_get_function_groups,
.set_mux = lpi_gpio_set_mux,
};
static int lpi_config_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
unsigned int param = pinconf_to_config_param(*config);
struct lpi_gpio_pad *pad;
unsigned int arg;
pad = pctldev->desc->pins[pin].drv_data;
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
arg = pad->pullup = LPI_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
arg = pad->pullup == LPI_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_BUS_HOLD:
arg = pad->pullup = LPI_GPIO_KEEPER;
break;
case PIN_CONFIG_BIAS_PULL_UP:
arg = pad->pullup == LPI_GPIO_PULL_UP;
break;
case PIN_CONFIG_INPUT_ENABLE:
case PIN_CONFIG_OUTPUT:
arg = pad->output_enabled;
break;
default:
return -EINVAL;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static unsigned int lpi_drive_to_regval(u32 arg)
{
return (arg/2 - 1);
}
static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int nconfs)
{
struct lpi_gpio_pad *pad;
unsigned int param, arg;
int i, ret = 0, val;
pad = pctldev->desc->pins[pin].drv_data;
for (i = 0; i < nconfs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
dev_dbg(pctldev->dev, "%s: param: %d arg: %d pin: %d\n",
__func__, param, arg, pin);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
pad->pullup = LPI_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
pad->pullup = LPI_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_BUS_HOLD:
pad->pullup = LPI_GPIO_KEEPER;
break;
case PIN_CONFIG_BIAS_PULL_UP:
pad->pullup = LPI_GPIO_PULL_UP;
break;
case PIN_CONFIG_INPUT_ENABLE:
pad->output_enabled = false;
break;
case PIN_CONFIG_OUTPUT:
pad->output_enabled = true;
pad->value = arg;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
pad->strength = arg;
break;
default:
ret = -EINVAL;
goto done;
}
}
val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK |
LPI_GPIO_REG_OE_MASK);
val |= pad->pullup << LPI_GPIO_REG_PULL_SHIFT;
val |= lpi_drive_to_regval(pad->strength) <<
LPI_GPIO_REG_OUT_STRENGTH_SHIFT;
if (pad->output_enabled)
val |= pad->value << LPI_GPIO_REG_OE_SHIFT;
lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val);
lpi_gpio_write(pad, LPI_GPIO_REG_DIR_CTL,
pad->output_enabled << LPI_GPIO_REG_DIR_SHIFT);
done:
return ret;
}
static const struct pinconf_ops lpi_gpio_pinconf_ops = {
.is_generic = true,
.pin_config_group_get = lpi_config_get,
.pin_config_group_set = lpi_config_set,
};
static int lpi_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
struct lpi_gpio_state *state = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
return lpi_config_set(state->ctrl, pin, &config, 1);
}
static int lpi_gpio_direction_output(struct gpio_chip *chip,
unsigned int pin, int val)
{
struct lpi_gpio_state *state = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
return lpi_config_set(state->ctrl, pin, &config, 1);
}
static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin)
{
struct lpi_gpio_state *state = gpiochip_get_data(chip);
struct lpi_gpio_pad *pad;
int value;
pad = state->ctrl->desc->pins[pin].drv_data;
value = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
return value;
}
static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
{
struct lpi_gpio_state *state = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
lpi_config_set(state->ctrl, pin, &config, 1);
}
static int lpi_notifier_service_cb(struct notifier_block *this,
unsigned long opcode, void *ptr)
{
static bool initial_boot = true;
pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
switch (opcode) {
case AUDIO_NOTIFIER_SERVICE_DOWN:
if (initial_boot) {
initial_boot = false;
break;
}
lpi_dev_up = false;
break;
case AUDIO_NOTIFIER_SERVICE_UP:
if (initial_boot)
initial_boot = false;
lpi_dev_up = true;
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block service_nb = {
.notifier_call = lpi_notifier_service_cb,
.priority = -INT_MAX,
};
#ifdef CONFIG_DEBUG_FS
#include <linux/seq_file.h>
static unsigned int lpi_regval_to_drive(u32 val)
{
return (val + 1) * 2;
}
static void lpi_gpio_dbg_show_one(struct seq_file *s,
struct pinctrl_dev *pctldev,
struct gpio_chip *chip,
unsigned int offset,
unsigned int gpio)
{
struct pinctrl_pin_desc pindesc;
struct lpi_gpio_pad *pad;
unsigned int func;
int is_out;
int drive;
int pull;
u32 ctl_reg;
static const char * const pulls[] = {
"no pull",
"pull down",
"keeper",
"pull up"
};
pctldev = pctldev ? : to_gpio_state(chip)->ctrl;
pindesc = pctldev->desc->pins[offset];
pad = pctldev->desc->pins[offset].drv_data;
ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_DIR_CTL);
is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT;
ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
func = (ctl_reg & LPI_GPIO_REG_FUNCTION_MASK) >>
LPI_GPIO_REG_FUNCTION_SHIFT;
drive = (ctl_reg & LPI_GPIO_REG_OUT_STRENGTH_MASK) >>
LPI_GPIO_REG_OUT_STRENGTH_SHIFT;
pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT;
seq_printf(s, " %-8s: %-3s %d",
pindesc.name, is_out ? "out" : "in", func);
seq_printf(s, " %dmA", lpi_regval_to_drive(drive));
seq_printf(s, " %s", pulls[pull]);
}
static void lpi_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
unsigned int gpio = chip->base;
unsigned int i;
for (i = 0; i < chip->ngpio; i++, gpio++) {
lpi_gpio_dbg_show_one(s, NULL, chip, i, gpio);
seq_puts(s, "\n");
}
}
#else
#define lpi_gpio_dbg_show NULL
#endif
static const struct gpio_chip lpi_gpio_template = {
.direction_input = lpi_gpio_direction_input,
.direction_output = lpi_gpio_direction_output,
.get = lpi_gpio_get,
.set = lpi_gpio_set,
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
.dbg_show = lpi_gpio_dbg_show,
};
static int lpi_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pinctrl_pin_desc *pindesc;
struct pinctrl_desc *pctrldesc;
struct lpi_gpio_pad *pad, *pads;
struct lpi_gpio_state *state;
int ret, npins, i;
char __iomem *lpi_base;
u32 reg;
ret = of_property_read_u32(dev->of_node, "reg", &reg);
if (ret < 0) {
dev_err(dev, "missing base address\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins);
if (ret < 0)
return ret;
WARN_ON(npins > ARRAY_SIZE(lpi_gpio_groups));
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
platform_set_drvdata(pdev, state);
state->dev = &pdev->dev;
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc)
return -ENOMEM;
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
if (!pads)
return -ENOMEM;
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
if (!pctrldesc)
return -ENOMEM;
pctrldesc->pctlops = &lpi_gpio_pinctrl_ops;
pctrldesc->pmxops = &lpi_gpio_pinmux_ops;
pctrldesc->confops = &lpi_gpio_pinconf_ops;
pctrldesc->owner = THIS_MODULE;
pctrldesc->name = dev_name(dev);
pctrldesc->pins = pindesc;
pctrldesc->npins = npins;
lpi_base = devm_ioremap(dev, reg, LPI_ADDRESS_SIZE);
if (lpi_base == NULL) {
dev_err(dev, "%s devm_ioremap failed\n", __func__);
return -ENOMEM;
}
state->base = lpi_base;
for (i = 0; i < npins; i++, pindesc++) {
pad = &pads[i];
pindesc->drv_data = pad;
pindesc->number = i;
pindesc->name = lpi_gpio_groups[i];
pad->base = lpi_base;
pad->offset = lpi_offset[i];
}
state->chip = lpi_gpio_template;
state->chip.parent = dev;
state->chip.base = -1;
state->chip.ngpio = npins;
state->chip.label = dev_name(dev);
state->chip.of_gpio_n_cells = 2;
state->chip.can_sleep = false;
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
if (IS_ERR(state->ctrl))
return PTR_ERR(state->ctrl);
ret = gpiochip_add_data(&state->chip, state);
if (ret) {
dev_err(state->dev, "can't add gpio chip\n");
goto err_chip;
}
ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
if (ret) {
dev_err(dev, "failed to add pin range\n");
goto err_range;
}
lpi_dev_up = true;
ret = audio_notifier_register("lpi_tlmm", AUDIO_NOTIFIER_ADSP_DOMAIN,
&service_nb);
if (ret < 0) {
pr_err("%s: Audio notifier register failed ret = %d\n",
__func__, ret);
goto err_range;
}
return 0;
err_range:
gpiochip_remove(&state->chip);
err_chip:
return ret;
}
static int lpi_pinctrl_remove(struct platform_device *pdev)
{
struct lpi_gpio_state *state = platform_get_drvdata(pdev);
gpiochip_remove(&state->chip);
return 0;
}
static const struct of_device_id lpi_pinctrl_of_match[] = {
{ .compatible = "qcom,lpi-pinctrl" }, /* Generic */
{ },
};
MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
static struct platform_driver lpi_pinctrl_driver = {
.driver = {
.name = "qcom-lpi-pinctrl",
.of_match_table = lpi_pinctrl_of_match,
},
.probe = lpi_pinctrl_probe,
.remove = lpi_pinctrl_remove,
};
module_platform_driver(lpi_pinctrl_driver);
MODULE_DESCRIPTION("QTI LPI GPIO pin control driver");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 2016-2017, 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/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mfd/wcd934x/registers.h>
#include "../core.h"
#include "../pinctrl-utils.h"
#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE
#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA
#define WCD_GPIO_PULL_UP 1
#define WCD_GPIO_PULL_DOWN 2
#define WCD_GPIO_BIAS_DISABLE 3
#define WCD_GPIO_STRING_LEN 20
/**
* struct wcd_gpio_pad - keep current GPIO settings
* @offset: offset of gpio.
* @is_valid: Set to false, when GPIO in high Z state.
* @value: value of a pin
* @output_enabled: Set to true if GPIO is output and false if it is input
* @pullup: Constant current which flow through GPIO output buffer.
* @strength: Drive strength of a pin
*/
struct wcd_gpio_pad {
u16 offset;
bool is_valid;
bool value;
bool output_enabled;
unsigned int pullup;
unsigned int strength;
};
struct wcd_gpio_priv {
struct device *dev;
struct regmap *map;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
};
static int wcd_gpio_read(struct wcd_gpio_priv *priv_data,
struct wcd_gpio_pad *pad, unsigned int addr)
{
unsigned int val;
int ret;
ret = regmap_read(priv_data->map, addr, &val);
if (ret < 0)
dev_err(priv_data->dev, "%s: read 0x%x failed\n",
__func__, addr);
else
ret = (val >> pad->offset);
return ret;
}
static int wcd_gpio_write(struct wcd_gpio_priv *priv_data,
struct wcd_gpio_pad *pad, unsigned int addr,
unsigned int val)
{
int ret;
ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset),
val << pad->offset);
if (ret < 0)
dev_err(priv_data->dev, "write 0x%x failed\n", addr);
return ret;
}
static int wcd_get_groups_count(struct pinctrl_dev *pctldev)
{
return pctldev->desc->npins;
}
static const char *wcd_get_group_name(struct pinctrl_dev *pctldev,
unsigned int pin)
{
return pctldev->desc->pins[pin].name;
}
static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin,
const unsigned int **pins, unsigned int *num_pins)
{
*pins = &pctldev->desc->pins[pin].number;
*num_pins = 1;
return 0;
}
static const struct pinctrl_ops wcd_pinctrl_ops = {
.get_groups_count = wcd_get_groups_count,
.get_group_name = wcd_get_group_name,
.get_group_pins = wcd_get_group_pins,
.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
.dt_free_map = pinctrl_utils_free_map,
};
static int wcd_config_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
unsigned int param = pinconf_to_config_param(*config);
struct wcd_gpio_pad *pad;
unsigned int arg;
pad = pctldev->desc->pins[pin].drv_data;
switch (param) {
case PIN_CONFIG_BIAS_PULL_DOWN:
arg = pad->pullup == WCD_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_DISABLE:
arg = pad->pullup = WCD_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_UP:
arg = pad->pullup == WCD_GPIO_PULL_UP;
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
arg = !pad->is_valid;
break;
case PIN_CONFIG_INPUT_ENABLE:
arg = pad->output_enabled;
break;
case PIN_CONFIG_OUTPUT:
arg = pad->value;
break;
default:
return -EINVAL;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int nconfs)
{
struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev);
struct wcd_gpio_pad *pad;
unsigned int param, arg;
int i, ret;
pad = pctldev->desc->pins[pin].drv_data;
for (i = 0; i < nconfs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
dev_dbg(priv_data->dev, "%s: param: %d arg: %d",
__func__, param, arg);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
pad->pullup = WCD_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_UP:
pad->pullup = WCD_GPIO_PULL_UP;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
pad->pullup = WCD_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
pad->is_valid = false;
break;
case PIN_CONFIG_INPUT_ENABLE:
pad->output_enabled = false;
break;
case PIN_CONFIG_OUTPUT:
pad->output_enabled = true;
pad->value = arg;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
pad->strength = arg;
break;
default:
ret = -EINVAL;
goto done;
}
}
if (pad->output_enabled) {
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
pad->output_enabled);
if (ret < 0)
goto done;
ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL,
pad->value);
} else
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
pad->output_enabled);
done:
return ret;
}
static const struct pinconf_ops wcd_pinconf_ops = {
.is_generic = true,
.pin_config_group_get = wcd_config_get,
.pin_config_group_set = wcd_config_set,
};
static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static int wcd_gpio_direction_output(struct gpio_chip *chip,
unsigned int pin, int val)
{
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
{
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
struct wcd_gpio_pad *pad;
int value;
pad = priv_data->ctrl->desc->pins[pin].drv_data;
if (!pad->is_valid)
return -EINVAL;
value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL);
return value;
}
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
{
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static const struct gpio_chip wcd_gpio_chip = {
.direction_input = wcd_gpio_direction_input,
.direction_output = wcd_gpio_direction_output,
.get = wcd_gpio_get,
.set = wcd_gpio_set,
};
static int wcd_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pinctrl_pin_desc *pindesc;
struct pinctrl_desc *pctrldesc;
struct wcd_gpio_pad *pad, *pads;
struct wcd_gpio_priv *priv_data;
int ret, i, j;
u32 npins;
char **name;
ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins);
if (ret) {
dev_err(dev, "%s: Looking up %s property in node %s failed\n",
__func__, "qcom,num-gpios", dev->of_node->full_name);
ret = -EINVAL;
goto err_priv_alloc;
}
if (!npins) {
dev_err(dev, "%s: no.of pins are 0\n", __func__);
ret = -EINVAL;
goto err_priv_alloc;
}
priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
if (!priv_data) {
ret = -ENOMEM;
goto err_priv_alloc;
}
priv_data->dev = dev;
priv_data->map = dev_get_regmap(dev->parent, NULL);
if (!priv_data->map) {
dev_err(dev, "%s: failed to get regmap\n", __func__);
ret = -EINVAL;
goto err_regmap;
}
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc) {
ret = -ENOMEM;
goto err_pinsec_alloc;
}
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
if (!pads) {
ret = -ENOMEM;
goto err_pads_alloc;
}
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
if (!pctrldesc) {
ret = -ENOMEM;
goto err_pinctrl_alloc;
}
pctrldesc->pctlops = &wcd_pinctrl_ops;
pctrldesc->confops = &wcd_pinconf_ops;
pctrldesc->owner = THIS_MODULE;
pctrldesc->name = dev_name(dev);
pctrldesc->pins = pindesc;
pctrldesc->npins = npins;
name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_name_alloc;
}
for (i = 0; i < npins; i++, pindesc++) {
name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN,
GFP_KERNEL);
if (!name[i]) {
ret = -ENOMEM;
goto err_pin;
}
pad = &pads[i];
pindesc->drv_data = pad;
pindesc->number = i;
snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1));
pindesc->name = name[i];
pad->offset = i;
pad->is_valid = true;
}
priv_data->chip = wcd_gpio_chip;
priv_data->chip.parent = dev;
priv_data->chip.base = -1;
priv_data->chip.ngpio = npins;
priv_data->chip.label = dev_name(dev);
priv_data->chip.of_gpio_n_cells = 2;
priv_data->chip.can_sleep = false;
priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data);
if (IS_ERR(priv_data->ctrl)) {
dev_err(dev, "%s: failed to register to pinctrl\n", __func__);
ret = PTR_ERR(priv_data->ctrl);
goto err_pin;
}
ret = gpiochip_add_data(&priv_data->chip, priv_data);
if (ret) {
dev_err(dev, "%s: can't add gpio chip\n", __func__);
goto err_pin;
}
ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0,
npins);
if (ret) {
dev_err(dev, "%s: failed to add pin range\n", __func__);
goto err_range;
}
platform_set_drvdata(pdev, priv_data);
return 0;
err_range:
gpiochip_remove(&priv_data->chip);
err_pin:
for (j = 0; j < i; j++)
devm_kfree(dev, name[j]);
devm_kfree(dev, name);
err_name_alloc:
devm_kfree(dev, pctrldesc);
err_pinctrl_alloc:
devm_kfree(dev, pads);
err_pads_alloc:
devm_kfree(dev, pindesc);
err_pinsec_alloc:
err_regmap:
devm_kfree(dev, priv_data);
err_priv_alloc:
return ret;
}
static int wcd_pinctrl_remove(struct platform_device *pdev)
{
struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev);
gpiochip_remove(&priv_data->chip);
return 0;
}
static const struct of_device_id wcd_pinctrl_of_match[] = {
{ .compatible = "qcom,wcd-pinctrl" },
{ },
};
MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match);
static struct platform_driver wcd_pinctrl_driver = {
.driver = {
.name = "qcom-wcd-pinctrl",
.of_match_table = wcd_pinctrl_of_match,
},
.probe = wcd_pinctrl_probe,
.remove = wcd_pinctrl_remove,
};
module_platform_driver(wcd_pinctrl_driver);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver");
MODULE_LICENSE("GPL v2");

5
drivers/soc/Makefile Normal file
Melihat File

@@ -0,0 +1,5 @@
#
# Makefile for the Linux Kernel SOC specific device drivers.
#
obj-y += qcom/

Melihat File

@@ -0,0 +1 @@
obj-y += qdsp6v2/

Melihat File

@@ -0,0 +1,8 @@
obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o
obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o
obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o
obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o
obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o

Melihat File

@@ -0,0 +1,316 @@
/*
* Copyright (c) 2012-2014, 2017, 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/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
#include <linux/workqueue.h>
#include <soc/qcom/subsystem_restart.h>
#define Q6_PIL_GET_DELAY_MS 100
#define BOOT_CMD 1
#define IMAGE_UNLOAD_CMD 0
static ssize_t adsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
struct adsp_loader_private {
void *pil_h;
struct kobject *boot_adsp_obj;
struct attribute_group *attr_group;
};
static struct kobj_attribute adsp_boot_attribute =
__ATTR(boot, 0220, NULL, adsp_boot_store);
static struct attribute *attrs[] = {
&adsp_boot_attribute.attr,
NULL,
};
static struct work_struct adsp_ldr_work;
static struct platform_device *adsp_private;
static void adsp_loader_unload(struct platform_device *pdev);
static void adsp_load_fw(struct work_struct *adsp_ldr_work)
{
struct platform_device *pdev = adsp_private;
struct adsp_loader_private *priv = NULL;
const char *adsp_dt = "qcom,adsp-state";
int rc = 0;
u32 adsp_state;
const char *img_name;
if (!pdev) {
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
goto fail;
}
if (!pdev->dev.of_node) {
dev_err(&pdev->dev,
"%s: Device tree information missing\n", __func__);
goto fail;
}
rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
if (rc) {
dev_err(&pdev->dev,
"%s: ADSP state = %x\n", __func__, adsp_state);
goto fail;
}
rc = of_property_read_string(pdev->dev.of_node,
"qcom,proc-img-to-load",
&img_name);
if (rc) {
dev_dbg(&pdev->dev,
"%s: loading default image ADSP\n", __func__);
goto load_adsp;
}
if (!strcmp(img_name, "modem")) {
/* adsp_state always returns "0". So load modem image based on
* apr_modem_state to prevent loading of image twice
*/
adsp_state = apr_get_modem_state();
if (adsp_state == APR_SUBSYS_DOWN) {
priv = platform_get_drvdata(pdev);
if (!priv) {
dev_err(&pdev->dev,
" %s: Private data get failed\n", __func__);
goto fail;
}
priv->pil_h = subsystem_get("modem");
if (IS_ERR(priv->pil_h)) {
dev_err(&pdev->dev, "%s: pil get failed,\n",
__func__);
goto fail;
}
/* Set the state of the ADSP in APR driver */
apr_set_modem_state(APR_SUBSYS_LOADED);
} else if (adsp_state == APR_SUBSYS_LOADED) {
dev_dbg(&pdev->dev,
"%s: MDSP state = %x\n", __func__, adsp_state);
}
dev_dbg(&pdev->dev, "%s: Q6/MDSP image is loaded\n", __func__);
return;
}
load_adsp:
{
adsp_state = apr_get_q6_state();
if (adsp_state == APR_SUBSYS_DOWN) {
priv = platform_get_drvdata(pdev);
if (!priv) {
dev_err(&pdev->dev,
" %s: Private data get failed\n", __func__);
goto fail;
}
priv->pil_h = subsystem_get("adsp");
if (IS_ERR(priv->pil_h)) {
dev_err(&pdev->dev, "%s: pil get failed,\n",
__func__);
goto fail;
}
/* Set the state of the ADSP in APR driver */
apr_set_q6_state(APR_SUBSYS_LOADED);
} else if (adsp_state == APR_SUBSYS_LOADED) {
dev_dbg(&pdev->dev,
"%s: ADSP state = %x\n", __func__, adsp_state);
}
dev_dbg(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__);
return;
}
fail:
dev_err(&pdev->dev, "%s: Q6 image loading failed\n", __func__);
}
static void adsp_loader_do(struct platform_device *pdev)
{
schedule_work(&adsp_ldr_work);
}
static ssize_t adsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf,
size_t count)
{
int boot = 0;
if (sscanf(buf, "%du", &boot) != 1) {
pr_err("%s: failed to read boot info from string\n", __func__);
return -EINVAL;
}
if (boot == BOOT_CMD) {
pr_debug("%s: going to call adsp_loader_do\n", __func__);
adsp_loader_do(adsp_private);
} else if (boot == IMAGE_UNLOAD_CMD) {
pr_debug("%s: going to call adsp_unloader\n", __func__);
adsp_loader_unload(adsp_private);
}
return count;
}
static void adsp_loader_unload(struct platform_device *pdev)
{
struct adsp_loader_private *priv = NULL;
priv = platform_get_drvdata(pdev);
if (!priv)
return;
if (priv->pil_h) {
dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__);
subsystem_put(priv->pil_h);
priv->pil_h = NULL;
}
}
static int adsp_loader_init_sysfs(struct platform_device *pdev)
{
int ret = -EINVAL;
struct adsp_loader_private *priv = NULL;
adsp_private = NULL;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
return ret;
}
platform_set_drvdata(pdev, priv);
priv->pil_h = NULL;
priv->boot_adsp_obj = NULL;
priv->attr_group = devm_kzalloc(&pdev->dev,
sizeof(*(priv->attr_group)),
GFP_KERNEL);
if (!priv->attr_group) {
ret = -ENOMEM;
goto error_return;
}
priv->attr_group->attrs = attrs;
priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj);
if (!priv->boot_adsp_obj) {
dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
__func__);
ret = -ENOMEM;
goto error_return;
}
ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group);
if (ret) {
dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
__func__, ret);
goto error_return;
}
adsp_private = pdev;
return 0;
error_return:
if (priv->boot_adsp_obj) {
kobject_del(priv->boot_adsp_obj);
priv->boot_adsp_obj = NULL;
}
return ret;
}
static int adsp_loader_remove(struct platform_device *pdev)
{
struct adsp_loader_private *priv = NULL;
priv = platform_get_drvdata(pdev);
if (!priv)
return 0;
if (priv->pil_h) {
subsystem_put(priv->pil_h);
priv->pil_h = NULL;
}
if (priv->boot_adsp_obj) {
sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group);
kobject_del(priv->boot_adsp_obj);
priv->boot_adsp_obj = NULL;
}
return 0;
}
static int adsp_loader_probe(struct platform_device *pdev)
{
int ret = adsp_loader_init_sysfs(pdev);
if (ret != 0) {
dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
return ret;
}
INIT_WORK(&adsp_ldr_work, adsp_load_fw);
return 0;
}
static const struct of_device_id adsp_loader_dt_match[] = {
{ .compatible = "qcom,adsp-loader" },
{ }
};
MODULE_DEVICE_TABLE(of, adsp_loader_dt_match);
static struct platform_driver adsp_loader_driver = {
.driver = {
.name = "adsp-loader",
.owner = THIS_MODULE,
.of_match_table = adsp_loader_dt_match,
},
.probe = adsp_loader_probe,
.remove = adsp_loader_remove,
};
static int __init adsp_loader_init(void)
{
return platform_driver_register(&adsp_loader_driver);
}
module_init(adsp_loader_init);
static void __exit adsp_loader_exit(void)
{
platform_driver_unregister(&adsp_loader_driver);
}
module_exit(adsp_loader_exit);
MODULE_DESCRIPTION("ADSP Loader module");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -0,0 +1,976 @@
/* Copyright (c) 2010-2014, 2016-2017 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/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/scm.h>
#include <sound/apr_audio-v2.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
#include <linux/qdsp6v2/audio_notifier.h>
#include <linux/ipc_logging.h>
#define APR_PKT_IPC_LOG_PAGE_CNT 2
static struct apr_q6 q6;
static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
static void *apr_pkt_ctx;
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
static bool is_modem_up;
static bool is_initial_boot;
/* Subsystem restart: QDSP6 data, functions */
static struct workqueue_struct *apr_reset_workqueue;
static void apr_reset_deregister(struct work_struct *work);
static void dispatch_event(unsigned long code, uint16_t proc);
struct apr_reset_work {
void *handle;
struct work_struct work;
};
static bool apr_cf_debug;
#ifdef CONFIG_DEBUG_FS
static struct dentry *debugfs_apr_debug;
static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char cmd;
if (copy_from_user(&cmd, ubuf, 1))
return -EFAULT;
apr_cf_debug = (cmd == '1') ? true : false;
return cnt;
}
static const struct file_operations apr_debug_ops = {
.write = apr_debug_write,
};
#endif
#define APR_PKT_INFO(x...) \
do { \
if (apr_pkt_ctx) \
ipc_log_string(apr_pkt_ctx, "<APR>: "x); \
} while (0)
struct apr_svc_table {
char name[64];
int idx;
int id;
int client_id;
};
static const struct apr_svc_table svc_tbl_qdsp6[] = {
{
.name = "AFE",
.idx = 0,
.id = APR_SVC_AFE,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "ASM",
.idx = 1,
.id = APR_SVC_ASM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "ADM",
.idx = 2,
.id = APR_SVC_ADM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CORE",
.idx = 3,
.id = APR_SVC_ADSP_CORE,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "TEST",
.idx = 4,
.id = APR_SVC_TEST_CLIENT,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "MVM",
.idx = 5,
.id = APR_SVC_ADSP_MVM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CVS",
.idx = 6,
.id = APR_SVC_ADSP_CVS,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CVP",
.idx = 7,
.id = APR_SVC_ADSP_CVP,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "USM",
.idx = 8,
.id = APR_SVC_USM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "VIDC",
.idx = 9,
.id = APR_SVC_VIDC,
},
{
.name = "LSM",
.idx = 10,
.id = APR_SVC_LSM,
.client_id = APR_CLIENT_AUDIO,
},
};
static struct apr_svc_table svc_tbl_voice[] = {
{
.name = "VSM",
.idx = 0,
.id = APR_SVC_VSM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "VPM",
.idx = 1,
.id = APR_SVC_VPM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "MVS",
.idx = 2,
.id = APR_SVC_MVS,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "MVM",
.idx = 3,
.id = APR_SVC_MVM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "CVS",
.idx = 4,
.id = APR_SVC_CVS,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "CVP",
.idx = 5,
.id = APR_SVC_CVP,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "SRD",
.idx = 6,
.id = APR_SVC_SRD,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "TEST",
.idx = 7,
.id = APR_SVC_TEST_CLIENT,
.client_id = APR_CLIENT_VOICE,
},
};
enum apr_subsys_state apr_get_modem_state(void)
{
return atomic_read(&q6.modem_state);
}
void apr_set_modem_state(enum apr_subsys_state state)
{
atomic_set(&q6.modem_state, state);
}
enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
enum apr_subsys_state new)
{
return atomic_cmpxchg(&q6.modem_state, prev, new);
}
static void apr_modem_down(unsigned long opcode)
{
apr_set_modem_state(APR_SUBSYS_DOWN);
dispatch_event(opcode, APR_DEST_MODEM);
}
static void apr_modem_up(void)
{
if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
APR_SUBSYS_DOWN)
wake_up(&modem_wait);
is_modem_up = 1;
}
enum apr_subsys_state apr_get_q6_state(void)
{
return atomic_read(&q6.q6_state);
}
EXPORT_SYMBOL(apr_get_q6_state);
int apr_set_q6_state(enum apr_subsys_state state)
{
pr_debug("%s: setting adsp state %d\n", __func__, state);
if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
return -EINVAL;
atomic_set(&q6.q6_state, state);
return 0;
}
EXPORT_SYMBOL(apr_set_q6_state);
enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
enum apr_subsys_state new)
{
return atomic_cmpxchg(&q6.q6_state, prev, new);
}
static void apr_adsp_down(unsigned long opcode)
{
apr_set_q6_state(APR_SUBSYS_DOWN);
dispatch_event(opcode, APR_DEST_QDSP6);
}
static void apr_adsp_up(void)
{
if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
APR_SUBSYS_DOWN)
wake_up(&dsp_wait);
}
int apr_wait_for_device_up(int dest_id)
{
int rc = -1;
if (dest_id == APR_DEST_MODEM)
rc = wait_event_interruptible_timeout(modem_wait,
(apr_get_modem_state() == APR_SUBSYS_UP),
(1 * HZ));
else if (dest_id == APR_DEST_QDSP6)
rc = wait_event_interruptible_timeout(dsp_wait,
(apr_get_q6_state() == APR_SUBSYS_UP),
(1 * HZ));
else
pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
/* returns left time */
return rc;
}
int apr_load_adsp_image(void)
{
int rc = 0;
mutex_lock(&q6.lock);
if (apr_get_q6_state() == APR_SUBSYS_UP) {
q6.pil = subsystem_get("adsp");
if (IS_ERR(q6.pil)) {
rc = PTR_ERR(q6.pil);
pr_err("APR: Unable to load q6 image, error:%d\n", rc);
} else {
apr_set_q6_state(APR_SUBSYS_LOADED);
pr_debug("APR: Image is loaded, stated\n");
}
} else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
pr_debug("APR: q6 image already loaded\n");
} else {
pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
}
mutex_unlock(&q6.lock);
return rc;
}
struct apr_client *apr_get_client(int dest_id, int client_id)
{
return &client[dest_id][client_id];
}
int apr_send_pkt(void *handle, uint32_t *buf)
{
struct apr_svc *svc = handle;
struct apr_client *clnt;
struct apr_hdr *hdr;
uint16_t dest_id;
uint16_t client_id;
uint16_t w_len;
int rc;
unsigned long flags;
if (!handle || !buf) {
pr_err("APR: Wrong parameters\n");
return -EINVAL;
}
if (svc->need_reset) {
pr_err("apr: send_pkt service need reset\n");
return -ENETRESET;
}
if ((svc->dest_id == APR_DEST_QDSP6) &&
(apr_get_q6_state() != APR_SUBSYS_LOADED)) {
pr_err("%s: Still dsp is not Up\n", __func__);
return -ENETRESET;
} else if ((svc->dest_id == APR_DEST_MODEM) &&
(apr_get_modem_state() == APR_SUBSYS_DOWN)) {
pr_err("apr: Still Modem is not Up\n");
return -ENETRESET;
}
spin_lock_irqsave(&svc->w_lock, flags);
dest_id = svc->dest_id;
client_id = svc->client_id;
clnt = &client[dest_id][client_id];
if (!client[dest_id][client_id].handle) {
pr_err("APR: Still service is not yet opened\n");
spin_unlock_irqrestore(&svc->w_lock, flags);
return -EINVAL;
}
hdr = (struct apr_hdr *)buf;
hdr->src_domain = APR_DOMAIN_APPS;
hdr->src_svc = svc->id;
hdr->dest_domain = svc->dest_domain;
hdr->dest_svc = svc->id;
if (unlikely(apr_cf_debug)) {
APR_PKT_INFO(
"Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
(hdr->src_domain << 8) | hdr->src_svc,
(hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
hdr->token);
}
rc = apr_tal_write(clnt->handle, buf,
(struct apr_pkt_priv *)&svc->pkt_owner,
hdr->pkt_size);
if (rc >= 0) {
w_len = rc;
if (w_len != hdr->pkt_size) {
pr_err("%s: Unable to write whole APR pkt successfully: %d\n",
__func__, rc);
rc = -EINVAL;
}
} else {
pr_err("%s: Write APR pkt failed with error %d\n",
__func__, rc);
}
spin_unlock_irqrestore(&svc->w_lock, flags);
return rc;
}
int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg)
{
struct apr_svc *svc = (struct apr_svc *)handle;
uint16_t dest_id;
uint16_t client_id;
struct apr_client *clnt;
if (!handle) {
pr_err("%s: Invalid handle\n", __func__);
return -EINVAL;
}
if (svc->need_reset) {
pr_err("%s: service need reset\n", __func__);
return -ENETRESET;
}
svc->pkt_owner = cfg->pkt_owner;
dest_id = svc->dest_id;
client_id = svc->client_id;
clnt = &client[dest_id][client_id];
return apr_tal_rx_intents_config(clnt->handle,
cfg->intents.num_of_intents, cfg->intents.size);
}
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv)
{
struct apr_client *clnt;
int client_id = 0;
int svc_idx = 0;
int svc_id = 0;
int dest_id = 0;
int domain_id = 0;
int temp_port = 0;
struct apr_svc *svc = NULL;
int rc = 0;
bool can_open_channel = true;
if (!dest || !svc_name || !svc_fn)
return NULL;
if (!strcmp(dest, "ADSP"))
domain_id = APR_DOMAIN_ADSP;
else if (!strcmp(dest, "MODEM")) {
/* Don't request for SMD channels if destination is MODEM,
* as these channels are no longer used and these clients
* are to listen only for MODEM SSR events
*/
can_open_channel = false;
domain_id = APR_DOMAIN_MODEM;
} else {
pr_err("APR: wrong destination\n");
goto done;
}
dest_id = apr_get_dest_id(dest);
if (dest_id == APR_DEST_QDSP6) {
if (apr_get_q6_state() != APR_SUBSYS_LOADED) {
pr_err("%s: adsp not up\n", __func__);
return NULL;
}
pr_debug("%s: adsp Up\n", __func__);
} else if (dest_id == APR_DEST_MODEM) {
if (apr_get_modem_state() == APR_SUBSYS_DOWN) {
if (is_modem_up) {
pr_err("%s: modem shutdown due to SSR, ret",
__func__);
return NULL;
}
pr_debug("%s: Wait for modem to bootup\n", __func__);
rc = apr_wait_for_device_up(APR_DEST_MODEM);
if (rc == 0) {
pr_err("%s: Modem is not Up\n", __func__);
return NULL;
}
}
pr_debug("%s: modem Up\n", __func__);
}
if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) {
pr_err("%s: apr_get_svc failed\n", __func__);
goto done;
}
clnt = &client[dest_id][client_id];
mutex_lock(&clnt->m_lock);
if (!clnt->handle && can_open_channel) {
clnt->handle = apr_tal_open(client_id, dest_id,
APR_DL_SMD, apr_cb_func, NULL);
if (!clnt->handle) {
svc = NULL;
pr_err("APR: Unable to open handle\n");
mutex_unlock(&clnt->m_lock);
goto done;
}
}
mutex_unlock(&clnt->m_lock);
svc = &clnt->svc[svc_idx];
mutex_lock(&svc->m_lock);
clnt->id = client_id;
if (svc->need_reset) {
mutex_unlock(&svc->m_lock);
pr_err("APR: Service needs reset\n");
goto done;
}
svc->id = svc_id;
svc->dest_id = dest_id;
svc->client_id = client_id;
svc->dest_domain = domain_id;
svc->pkt_owner = APR_PKT_OWNER_DRIVER;
if (src_port != 0xFFFFFFFF) {
temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
pr_debug("port = %d t_port = %d\n", src_port, temp_port);
if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
pr_err("APR: temp_port out of bounds\n");
mutex_unlock(&svc->m_lock);
return NULL;
}
if (!svc->svc_cnt)
clnt->svc_cnt++;
svc->port_cnt++;
svc->port_fn[temp_port] = svc_fn;
svc->port_priv[temp_port] = priv;
svc->svc_cnt++;
} else {
if (!svc->fn) {
if (!svc->svc_cnt)
clnt->svc_cnt++;
svc->fn = svc_fn;
svc->priv = priv;
svc->svc_cnt++;
}
}
mutex_unlock(&svc->m_lock);
done:
return svc;
}
void apr_cb_func(void *buf, int len, void *priv)
{
struct apr_client_data data;
struct apr_client *apr_client;
struct apr_svc *c_svc;
struct apr_hdr *hdr;
uint16_t hdr_size;
uint16_t msg_type;
uint16_t ver;
uint16_t src;
uint16_t svc;
uint16_t clnt;
int i;
int temp_port = 0;
uint32_t *ptr;
pr_debug("APR2: len = %d\n", len);
ptr = buf;
pr_debug("\n*****************\n");
for (i = 0; i < len/4; i++)
pr_debug("%x ", ptr[i]);
pr_debug("\n");
pr_debug("\n*****************\n");
if (!buf || len <= APR_HDR_SIZE) {
pr_err("APR: Improper apr pkt received:%pK %d\n", buf, len);
return;
}
hdr = buf;
ver = hdr->hdr_field;
ver = (ver & 0x000F);
if (ver > APR_PKT_VER + 1) {
pr_err("APR: Wrong version: %d\n", ver);
return;
}
hdr_size = hdr->hdr_field;
hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
if (hdr_size < APR_HDR_SIZE) {
pr_err("APR: Wrong hdr size:%d\n", hdr_size);
return;
}
if (hdr->pkt_size < APR_HDR_SIZE) {
pr_err("APR: Wrong paket size\n");
return;
}
msg_type = hdr->hdr_field;
msg_type = (msg_type >> 0x08) & 0x0003;
if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
pr_err("APR: Wrong message type: %d\n", msg_type);
return;
}
if (hdr->src_domain >= APR_DOMAIN_MAX ||
hdr->dest_domain >= APR_DOMAIN_MAX ||
hdr->src_svc >= APR_SVC_MAX ||
hdr->dest_svc >= APR_SVC_MAX) {
pr_err("APR: Wrong APR header\n");
return;
}
svc = hdr->dest_svc;
if (hdr->src_domain == APR_DOMAIN_MODEM) {
if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
svc == APR_SVC_TEST_CLIENT)
clnt = APR_CLIENT_VOICE;
else {
pr_err("APR: Wrong svc :%d\n", svc);
return;
}
} else if (hdr->src_domain == APR_DOMAIN_ADSP) {
if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
svc == APR_SVC_USM ||
svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
svc == APR_SVC_LSM)
clnt = APR_CLIENT_AUDIO;
else if (svc == APR_SVC_VIDC)
clnt = APR_CLIENT_AUDIO;
else {
pr_err("APR: Wrong svc :%d\n", svc);
return;
}
} else {
pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
return;
}
src = apr_get_data_src(hdr);
if (src == APR_DEST_MAX)
return;
pr_debug("src =%d clnt = %d\n", src, clnt);
apr_client = &client[src][clnt];
for (i = 0; i < APR_SVC_MAX; i++)
if (apr_client->svc[i].id == svc) {
pr_debug("%d\n", apr_client->svc[i].id);
c_svc = &apr_client->svc[i];
break;
}
if (i == APR_SVC_MAX) {
pr_err("APR: service is not registered\n");
return;
}
pr_debug("svc_idx = %d\n", i);
pr_debug("%x %x %x %pK %pK\n", c_svc->id, c_svc->dest_id,
c_svc->client_id, c_svc->fn, c_svc->priv);
data.payload_size = hdr->pkt_size - hdr_size;
data.opcode = hdr->opcode;
data.src = src;
data.src_port = hdr->src_port;
data.dest_port = hdr->dest_port;
data.token = hdr->token;
data.msg_type = msg_type;
data.payload = NULL;
if (data.payload_size > 0)
data.payload = (char *)hdr + hdr_size;
if (unlikely(apr_cf_debug)) {
if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) {
uint32_t *ptr = data.payload;
APR_PKT_INFO(
"Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]",
(hdr->src_domain << 8) | hdr->src_svc,
(hdr->dest_domain << 8) | hdr->dest_svc,
hdr->opcode, hdr->token, ptr[1]);
} else {
APR_PKT_INFO(
"Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
(hdr->src_domain << 8) | hdr->src_svc,
(hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
hdr->token);
}
}
temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF);
pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
if (c_svc->port_cnt && c_svc->port_fn[temp_port])
c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]);
else if (c_svc->fn)
c_svc->fn(&data, c_svc->priv);
else
pr_err("APR: Rxed a packet for NULL callback\n");
}
int apr_get_svc(const char *svc_name, int domain_id, int *client_id,
int *svc_idx, int *svc_id)
{
int i;
int size;
struct apr_svc_table *tbl;
int ret = 0;
if (domain_id == APR_DOMAIN_ADSP) {
tbl = (struct apr_svc_table *)&svc_tbl_qdsp6;
size = ARRAY_SIZE(svc_tbl_qdsp6);
} else {
tbl = (struct apr_svc_table *)&svc_tbl_voice;
size = ARRAY_SIZE(svc_tbl_voice);
}
for (i = 0; i < size; i++) {
if (!strcmp(svc_name, tbl[i].name)) {
*client_id = tbl[i].client_id;
*svc_idx = tbl[i].idx;
*svc_id = tbl[i].id;
break;
}
}
pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n",
__func__, svc_name, *client_id, domain_id);
if (i == size) {
pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
ret = -EINVAL;
}
return ret;
}
static void apr_reset_deregister(struct work_struct *work)
{
struct apr_svc *handle = NULL;
struct apr_reset_work *apr_reset =
container_of(work, struct apr_reset_work, work);
handle = apr_reset->handle;
pr_debug("%s:handle[%pK]\n", __func__, handle);
apr_deregister(handle);
kfree(apr_reset);
}
int apr_deregister(void *handle)
{
struct apr_svc *svc = handle;
struct apr_client *clnt;
uint16_t dest_id;
uint16_t client_id;
if (!handle)
return -EINVAL;
mutex_lock(&svc->m_lock);
if (!svc->svc_cnt) {
pr_err("%s: svc already deregistered. svc = %pK\n",
__func__, svc);
mutex_unlock(&svc->m_lock);
return -EINVAL;
}
dest_id = svc->dest_id;
client_id = svc->client_id;
clnt = &client[dest_id][client_id];
if (svc->svc_cnt > 0) {
if (svc->port_cnt)
svc->port_cnt--;
svc->svc_cnt--;
if (!svc->svc_cnt) {
client[dest_id][client_id].svc_cnt--;
pr_debug("%s: service is reset %pK\n", __func__, svc);
}
}
if (!svc->svc_cnt) {
svc->priv = NULL;
svc->id = 0;
svc->fn = NULL;
svc->dest_id = 0;
svc->client_id = 0;
svc->need_reset = 0x0;
}
if (client[dest_id][client_id].handle &&
!client[dest_id][client_id].svc_cnt) {
apr_tal_close(client[dest_id][client_id].handle);
client[dest_id][client_id].handle = NULL;
}
mutex_unlock(&svc->m_lock);
return 0;
}
void apr_reset(void *handle)
{
struct apr_reset_work *apr_reset_worker = NULL;
if (!handle)
return;
pr_debug("%s: handle[%pK]\n", __func__, handle);
if (apr_reset_workqueue == NULL) {
pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
return;
}
apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
GFP_ATOMIC);
if (apr_reset_worker == NULL) {
pr_err("%s: mem failure\n", __func__);
return;
}
apr_reset_worker->handle = handle;
INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
queue_work(apr_reset_workqueue, &apr_reset_worker->work);
}
/* Dispatch the Reset events to Modem and audio clients */
static void dispatch_event(unsigned long code, uint16_t proc)
{
struct apr_client *apr_client;
struct apr_client_data data;
struct apr_svc *svc;
uint16_t clnt;
int i, j;
data.opcode = RESET_EVENTS;
data.reset_event = code;
/* Service domain can be different from the processor */
data.reset_proc = apr_get_reset_domain(proc);
clnt = APR_CLIENT_AUDIO;
apr_client = &client[proc][clnt];
for (i = 0; i < APR_SVC_MAX; i++) {
mutex_lock(&apr_client->svc[i].m_lock);
if (apr_client->svc[i].fn) {
apr_client->svc[i].need_reset = 0x1;
apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
}
if (apr_client->svc[i].port_cnt) {
svc = &(apr_client->svc[i]);
svc->need_reset = 0x1;
for (j = 0; j < APR_MAX_PORTS; j++)
if (svc->port_fn[j])
svc->port_fn[j](&data,
svc->port_priv[j]);
}
mutex_unlock(&apr_client->svc[i].m_lock);
}
clnt = APR_CLIENT_VOICE;
apr_client = &client[proc][clnt];
for (i = 0; i < APR_SVC_MAX; i++) {
mutex_lock(&apr_client->svc[i].m_lock);
if (apr_client->svc[i].fn) {
apr_client->svc[i].need_reset = 0x1;
apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
}
if (apr_client->svc[i].port_cnt) {
svc = &(apr_client->svc[i]);
svc->need_reset = 0x1;
for (j = 0; j < APR_MAX_PORTS; j++)
if (svc->port_fn[j])
svc->port_fn[j](&data,
svc->port_priv[j]);
}
mutex_unlock(&apr_client->svc[i].m_lock);
}
}
static int apr_notifier_service_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
struct audio_notifier_cb_data *cb_data = data;
if (cb_data == NULL) {
pr_err("%s: Callback data is NULL!\n", __func__);
goto done;
}
pr_debug("%s: Service opcode 0x%lx, domain %d\n",
__func__, opcode, cb_data->domain);
switch (opcode) {
case AUDIO_NOTIFIER_SERVICE_DOWN:
/*
* Use flag to ignore down notifications during
* initial boot. There is no benefit from error
* recovery notifications during initial boot
* up since everything is expected to be down.
*/
if (is_initial_boot) {
is_initial_boot = false;
break;
}
if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
apr_modem_down(opcode);
else
apr_adsp_down(opcode);
break;
case AUDIO_NOTIFIER_SERVICE_UP:
is_initial_boot = false;
if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
apr_modem_up();
else
apr_adsp_up();
break;
default:
break;
}
done:
return NOTIFY_OK;
}
static struct notifier_block adsp_service_nb = {
.notifier_call = apr_notifier_service_cb,
.priority = 0,
};
static struct notifier_block modem_service_nb = {
.notifier_call = apr_notifier_service_cb,
.priority = 0,
};
static int __init apr_init(void)
{
int i, j, k;
for (i = 0; i < APR_DEST_MAX; i++)
for (j = 0; j < APR_CLIENT_MAX; j++) {
mutex_init(&client[i][j].m_lock);
for (k = 0; k < APR_SVC_MAX; k++) {
mutex_init(&client[i][j].svc[k].m_lock);
spin_lock_init(&client[i][j].svc[k].w_lock);
}
}
apr_set_subsys_state();
mutex_init(&q6.lock);
apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
if (!apr_reset_workqueue)
return -ENOMEM;
apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT,
"apr", 0);
if (!apr_pkt_ctx)
pr_err("%s: Unable to create ipc log context\n", __func__);
is_initial_boot = true;
subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
&adsp_service_nb);
subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
&modem_service_nb);
return 0;
}
device_initcall(apr_init);
static int __init apr_late_init(void)
{
int ret = 0;
init_waitqueue_head(&dsp_wait);
init_waitqueue_head(&modem_wait);
return ret;
}
late_initcall(apr_late_init);
#ifdef CONFIG_DEBUG_FS
static int __init apr_debug_init(void)
{
debugfs_apr_debug = debugfs_create_file("msm_apr_debug",
S_IFREG | 0444, NULL, NULL,
&apr_debug_ops);
return 0;
}
device_initcall(apr_debug_init);
#endif

Melihat File

@@ -0,0 +1,451 @@
/*
* Copyright (c) 2016-2017 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/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <soc/qcom/glink.h>
#include <linux/qdsp6v2/apr_tal.h>
#define APR_MAXIMUM_NUM_OF_RETRIES 2
struct apr_tx_buf {
struct apr_pkt_priv pkt_priv;
char buf[APR_MAX_BUF];
};
struct link_state {
uint32_t dest;
void *handle;
enum glink_link_state link_state;
wait_queue_head_t wait;
};
static struct link_state link_state[APR_DEST_MAX];
static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = {
{
"apr_audio_svc",
"apr_voice_svc",
},
{
"apr_audio_svc",
"apr_voice_svc",
},
};
static struct apr_svc_ch_dev
apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
static struct apr_tx_buf *apr_alloc_buf(int len)
{
if (len > APR_MAX_BUF) {
pr_err("%s: buf too large [%d]\n", __func__, len);
return ERR_PTR(-EINVAL);
}
return kzalloc(sizeof(struct apr_tx_buf), GFP_ATOMIC);
}
static void apr_free_buf(const void *ptr)
{
struct apr_pkt_priv *apr_pkt_priv = (struct apr_pkt_priv *)ptr;
struct apr_tx_buf *tx_buf;
if (!apr_pkt_priv) {
pr_err("%s: Invalid apr_pkt_priv\n", __func__);
return;
}
if (apr_pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) {
tx_buf = container_of((void *)apr_pkt_priv,
struct apr_tx_buf, pkt_priv);
pr_debug("%s: Freeing buffer %pK", __func__, tx_buf);
kfree(tx_buf);
}
}
static int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
struct apr_pkt_priv *pkt_priv, int len)
{
int rc = 0;
unsigned long flags;
spin_lock_irqsave(&apr_ch->w_lock, flags);
rc = glink_tx(apr_ch->handle, pkt_priv, data, len, GLINK_TX_ATOMIC);
spin_unlock_irqrestore(&apr_ch->w_lock, flags);
if (rc)
pr_err("%s: glink_tx failed, rc[%d]\n", __func__, rc);
else
rc = len;
return rc;
}
int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
struct apr_pkt_priv *pkt_priv, int len)
{
int rc = 0, retries = 0;
void *pkt_data = NULL;
struct apr_tx_buf *tx_buf = NULL;
struct apr_pkt_priv *pkt_priv_ptr = pkt_priv;
if (!apr_ch->handle || !pkt_priv)
return -EINVAL;
if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) {
tx_buf = apr_alloc_buf(len);
if (IS_ERR_OR_NULL(tx_buf)) {
rc = -EINVAL;
goto exit;
}
memcpy(tx_buf->buf, data, len);
memcpy(&tx_buf->pkt_priv, pkt_priv, sizeof(tx_buf->pkt_priv));
pkt_priv_ptr = &tx_buf->pkt_priv;
pkt_data = tx_buf->buf;
} else {
pkt_data = data;
}
do {
if (rc == -EAGAIN)
udelay(50);
rc = __apr_tal_write(apr_ch, pkt_data, pkt_priv_ptr, len);
} while (rc == -EAGAIN && retries++ < APR_MAXIMUM_NUM_OF_RETRIES);
if (rc < 0) {
pr_err("%s: Unable to send the packet, rc:%d\n", __func__, rc);
if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER)
kfree(tx_buf);
}
exit:
return rc;
}
void apr_tal_notify_rx(void *handle, const void *priv, const void *pkt_priv,
const void *ptr, size_t size)
{
struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
unsigned long flags;
if (!apr_ch || !ptr) {
pr_err("%s: Invalid apr_ch or ptr\n", __func__);
return;
}
pr_debug("%s: Rx packet received\n", __func__);
spin_lock_irqsave(&apr_ch->r_lock, flags);
if (apr_ch->func)
apr_ch->func((void *)ptr, size, (void *)pkt_priv);
spin_unlock_irqrestore(&apr_ch->r_lock, flags);
glink_rx_done(apr_ch->handle, ptr, true);
}
static void apr_tal_notify_tx_abort(void *handle, const void *priv,
const void *pkt_priv)
{
pr_debug("%s: tx_abort received for pkt_priv:%pK\n",
__func__, pkt_priv);
apr_free_buf(pkt_priv);
}
void apr_tal_notify_tx_done(void *handle, const void *priv,
const void *pkt_priv, const void *ptr)
{
pr_debug("%s: tx_done received for pkt_priv:%pK\n",
__func__, pkt_priv);
apr_free_buf(pkt_priv);
}
bool apr_tal_notify_rx_intent_req(void *handle, const void *priv,
size_t req_size)
{
struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
if (!apr_ch) {
pr_err("%s: Invalid apr_ch\n", __func__);
return false;
}
pr_err("%s: No rx intents queued, unable to receive\n", __func__);
return false;
}
static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv,
size_t size)
{
struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
if (!apr_ch) {
pr_err("%s: Invalid apr_ch\n", __func__);
return;
}
/*
* This is to make sure that the far end has queued at least one intent
* before we attmpt any IPC. A simple bool flag is used here instead of
* a counter, as the far end is required to guarantee intent
* availability for all use cases once the channel is fully opened.
*/
pr_debug("%s: remote queued an intent\n", __func__);
apr_ch->if_remote_intent_ready = true;
wake_up(&apr_ch->wait);
}
void apr_tal_notify_state(void *handle, const void *priv, unsigned int event)
{
struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
if (!apr_ch) {
pr_err("%s: Invalid apr_ch\n", __func__);
return;
}
apr_ch->channel_state = event;
pr_info("%s: Channel state[%d]\n", __func__, event);
if (event == GLINK_CONNECTED)
wake_up(&apr_ch->wait);
}
int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch,
int num_of_intents, uint32_t size)
{
int i;
int rc;
if (!apr_ch || !num_of_intents || !size) {
pr_err("%s: Invalid parameter\n", __func__);
return -EINVAL;
}
for (i = 0; i < num_of_intents; i++) {
rc = glink_queue_rx_intent(apr_ch->handle, apr_ch, size);
if (rc) {
pr_err("%s: Failed to queue rx intent, iteration[%d]\n",
__func__, i);
break;
}
}
return rc;
}
struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl,
apr_svc_cb_fn func, void *priv)
{
int rc;
struct glink_open_config open_cfg;
struct apr_svc_ch_dev *apr_ch;
if ((clnt >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
(dl >= APR_DL_MAX)) {
pr_err("%s: Invalid params, clnt:%d, dest:%d, dl:%d\n",
__func__, clnt, dest, dl);
return NULL;
}
apr_ch = &apr_svc_ch[dl][dest][clnt];
mutex_lock(&apr_ch->m_lock);
if (apr_ch->handle) {
pr_err("%s: This channel is already opened\n", __func__);
rc = -EBUSY;
goto unlock;
}
if (link_state[dest].link_state != GLINK_LINK_STATE_UP) {
rc = wait_event_timeout(link_state[dest].wait,
link_state[dest].link_state == GLINK_LINK_STATE_UP,
msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
if (rc == 0) {
pr_err("%s: Open timeout, dest:%d\n", __func__, dest);
rc = -ETIMEDOUT;
goto unlock;
}
pr_debug("%s: Wakeup done, dest:%d\n", __func__, dest);
}
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.options = GLINK_OPT_INITIAL_XPORT;
if (dest == APR_DEST_MODEM)
open_cfg.edge = "mpss";
else
open_cfg.edge = "lpass";
open_cfg.name = svc_names[dest][clnt];
open_cfg.notify_rx = apr_tal_notify_rx;
open_cfg.notify_tx_done = apr_tal_notify_tx_done;
open_cfg.notify_state = apr_tal_notify_state;
open_cfg.notify_rx_intent_req = apr_tal_notify_rx_intent_req;
open_cfg.notify_remote_rx_intent = apr_tal_notify_remote_rx_intent;
open_cfg.notify_tx_abort = apr_tal_notify_tx_abort;
open_cfg.priv = apr_ch;
open_cfg.transport = "smem";
apr_ch->channel_state = GLINK_REMOTE_DISCONNECTED;
apr_ch->handle = glink_open(&open_cfg);
if (IS_ERR_OR_NULL(apr_ch->handle)) {
pr_err("%s: glink_open failed %s\n", __func__,
svc_names[dest][clnt]);
apr_ch->handle = NULL;
rc = -EINVAL;
goto unlock;
}
rc = wait_event_timeout(apr_ch->wait,
(apr_ch->channel_state == GLINK_CONNECTED), 5 * HZ);
if (rc == 0) {
pr_err("%s: TIMEOUT for OPEN event\n", __func__);
rc = -ETIMEDOUT;
goto close_link;
}
/*
* Remote intent is not required for GLINK <--> SMD IPC, so this is
* designed not to fail the open call.
*/
rc = wait_event_timeout(apr_ch->wait,
apr_ch->if_remote_intent_ready, 5 * HZ);
if (rc == 0)
pr_err("%s: TIMEOUT for remote intent readiness\n", __func__);
rc = apr_tal_rx_intents_config(apr_ch, APR_DEFAULT_NUM_OF_INTENTS,
APR_MAX_BUF);
if (rc) {
pr_err("%s: Unable to queue intents\n", __func__);
goto close_link;
}
apr_ch->func = func;
apr_ch->priv = priv;
close_link:
if (rc) {
glink_close(apr_ch->handle);
apr_ch->handle = NULL;
}
unlock:
mutex_unlock(&apr_ch->m_lock);
return rc ? NULL : apr_ch;
}
int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
{
int rc;
if (!apr_ch || !apr_ch->handle) {
rc = -EINVAL;
goto exit;
}
mutex_lock(&apr_ch->m_lock);
rc = glink_close(apr_ch->handle);
apr_ch->handle = NULL;
apr_ch->func = NULL;
apr_ch->priv = NULL;
apr_ch->if_remote_intent_ready = false;
mutex_unlock(&apr_ch->m_lock);
exit:
return rc;
}
static void apr_tal_link_state_cb(struct glink_link_state_cb_info *cb_info,
void *priv)
{
uint32_t dest;
if (!cb_info) {
pr_err("%s: Invalid cb_info\n", __func__);
return;
}
if (!strcmp(cb_info->edge, "mpss"))
dest = APR_DEST_MODEM;
else if (!strcmp(cb_info->edge, "lpass"))
dest = APR_DEST_QDSP6;
else {
pr_err("%s:Unknown edge[%s]\n", __func__, cb_info->edge);
return;
}
pr_info("%s: edge[%s] link state[%d]\n", __func__, cb_info->edge,
cb_info->link_state);
link_state[dest].link_state = cb_info->link_state;
if (link_state[dest].link_state == GLINK_LINK_STATE_UP)
wake_up(&link_state[dest].wait);
}
static struct glink_link_info mpss_link_info = {
.transport = "smem",
.edge = "mpss",
.glink_link_state_notif_cb = apr_tal_link_state_cb,
};
static struct glink_link_info lpass_link_info = {
.transport = "smem",
.edge = "lpass",
.glink_link_state_notif_cb = apr_tal_link_state_cb,
};
static int __init apr_tal_init(void)
{
int i, j, k;
for (i = 0; i < APR_DL_MAX; i++) {
for (j = 0; j < APR_DEST_MAX; j++) {
for (k = 0; k < APR_CLIENT_MAX; k++) {
init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
spin_lock_init(&apr_svc_ch[i][j][k].w_lock);
spin_lock_init(&apr_svc_ch[i][j][k].r_lock);
mutex_init(&apr_svc_ch[i][j][k].m_lock);
}
}
}
for (i = 0; i < APR_DEST_MAX; i++)
init_waitqueue_head(&link_state[i].wait);
link_state[APR_DEST_MODEM].link_state = GLINK_LINK_STATE_DOWN;
link_state[APR_DEST_MODEM].handle =
glink_register_link_state_cb(&mpss_link_info, NULL);
if (!link_state[APR_DEST_MODEM].handle)
pr_err("%s: Unable to register mpss link state\n", __func__);
link_state[APR_DEST_QDSP6].link_state = GLINK_LINK_STATE_DOWN;
link_state[APR_DEST_QDSP6].handle =
glink_register_link_state_cb(&lpass_link_info, NULL);
if (!link_state[APR_DEST_QDSP6].handle)
pr_err("%s: Unable to register lpass link state\n", __func__);
return 0;
}
device_initcall(apr_tal_init);

Melihat File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2012-2017 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/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
#include <linux/qdsp6v2/audio_notifier.h>
enum apr_subsys_state apr_get_subsys_state(void)
{
return apr_get_q6_state();
}
void apr_set_subsys_state(void)
{
apr_set_q6_state(APR_SUBSYS_DOWN);
apr_set_modem_state(APR_SUBSYS_UP);
}
uint16_t apr_get_data_src(struct apr_hdr *hdr)
{
if (hdr->src_domain == APR_DOMAIN_MODEM)
return APR_DEST_MODEM;
else if (hdr->src_domain == APR_DOMAIN_ADSP)
return APR_DEST_QDSP6;
pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
return APR_DEST_MAX; /*RETURN INVALID VALUE*/
}
int apr_get_dest_id(char *dest)
{
if (!strcmp(dest, "ADSP"))
return APR_DEST_QDSP6;
else
return APR_DEST_MODEM;
}
void subsys_notif_register(char *client_name, int domain,
struct notifier_block *nb)
{
int ret;
ret = audio_notifier_register(client_name, domain, nb);
if (ret < 0)
pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
__func__, domain, ret);
}
uint16_t apr_get_reset_domain(uint16_t proc)
{
return proc;
}

Melihat File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2013-2016, 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/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
#include <linux/qdsp6v2/audio_notifier.h>
#define DEST_ID APR_DEST_MODEM
enum apr_subsys_state apr_get_subsys_state(void)
{
return apr_get_modem_state();
}
void apr_set_subsys_state(void)
{
apr_set_modem_state(APR_SUBSYS_DOWN);
}
uint16_t apr_get_data_src(struct apr_hdr *hdr)
{
return DEST_ID;
}
int apr_get_dest_id(char *dest)
{
return DEST_ID;
}
void subsys_notif_register(char *client_name, int domain,
struct notifier_block *nb)
{
int ret;
if (domain != AUDIO_NOTIFIER_MODEM_DOMAIN) {
pr_debug("%s: Unused domain %d not registering with notifier\n",
__func__, domain);
return;
}
ret = audio_notifier_register(client_name, domain, nb);
if (ret < 0)
pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
__func__, domain, ret);
}
uint16_t apr_get_reset_domain(uint16_t proc)
{
return APR_DEST_QDSP6;
}

Melihat File

@@ -0,0 +1,636 @@
/* Copyright (c) 2016-2017, 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/module.h>
#include <linux/slab.h>
#include <linux/qdsp6v2/audio_pdr.h>
#include <linux/qdsp6v2/audio_ssr.h>
#include <linux/qdsp6v2/audio_notifier.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/service-notifier.h>
/* Audio states internal to notifier. Client */
/* used states defined in audio_notifier.h */
/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
#define NO_SERVICE -2
#define UNINIT_SERVICE -1
/*
* Used for each client registered with audio notifier
*/
struct client_data {
struct list_head list;
/* Notifier block given by client */
struct notifier_block *nb;
char client_name[20];
int service;
int domain;
};
/*
* Used for each service and domain combination
* Tracks information specific to the underlying
* service.
*/
struct service_info {
const char name[20];
int domain_id;
int state;
void *handle;
/* Notifier block registered to service */
struct notifier_block *nb;
/* Used to determine when to register and deregister service */
int num_of_clients;
/* List of all clients registered to the service and domain */
struct srcu_notifier_head client_nb_list;
};
static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static struct notifier_block notifier_ssr_adsp_nb = {
.notifier_call = audio_notifer_ssr_adsp_cb,
.priority = 0,
};
static struct notifier_block notifier_ssr_modem_nb = {
.notifier_call = audio_notifer_ssr_modem_cb,
.priority = 0,
};
static struct notifier_block notifier_pdr_adsp_nb = {
.notifier_call = audio_notifer_pdr_adsp_cb,
.priority = 0,
};
static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
[AUDIO_NOTIFIER_MAX_DOMAINS] = {
{{
.name = "SSR_ADSP",
.domain_id = AUDIO_SSR_DOMAIN_ADSP,
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
.nb = &notifier_ssr_adsp_nb
},
{
.name = "SSR_MODEM",
.domain_id = AUDIO_SSR_DOMAIN_MODEM,
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
.nb = &notifier_ssr_modem_nb
} },
{{
.name = "PDR_ADSP",
.domain_id = AUDIO_PDR_DOMAIN_ADSP,
.state = UNINIT_SERVICE,
.nb = &notifier_pdr_adsp_nb
},
{ /* PDR MODEM service not enabled */
.name = "INVALID",
.state = NO_SERVICE,
.nb = NULL
} }
};
/* Master list of all audio notifier clients */
struct list_head client_list;
struct mutex notifier_mutex;
static int audio_notifer_get_default_service(int domain)
{
int service = NO_SERVICE;
/* initial service to connect per domain */
switch (domain) {
case AUDIO_NOTIFIER_ADSP_DOMAIN:
service = AUDIO_NOTIFIER_PDR_SERVICE;
break;
case AUDIO_NOTIFIER_MODEM_DOMAIN:
service = AUDIO_NOTIFIER_SSR_SERVICE;
break;
}
return service;
}
static void audio_notifer_disable_service(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
service_data[service][i].state = NO_SERVICE;
}
static bool audio_notifer_is_service_enabled(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
if (service_data[service][i].state != NO_SERVICE)
return true;
return false;
}
static void audio_notifer_init_service(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
if (service_data[service][i].state == UNINIT_SERVICE)
service_data[service][i].state =
AUDIO_NOTIFIER_SERVICE_DOWN;
}
}
static int audio_notifer_reg_service(int service, int domain)
{
void *handle;
int ret = 0;
int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
handle = audio_ssr_register(
service_data[service][domain].domain_id,
service_data[service][domain].nb);
break;
case AUDIO_NOTIFIER_PDR_SERVICE:
handle = audio_pdr_service_register(
service_data[service][domain].domain_id,
service_data[service][domain].nb, &curr_state);
if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
curr_state = AUDIO_NOTIFIER_SERVICE_UP;
else
curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
break;
default:
pr_err("%s: Invalid service %d\n",
__func__, service);
ret = -EINVAL;
goto done;
}
if (IS_ERR_OR_NULL(handle)) {
pr_err("%s: handle is incorrect for service %s\n",
__func__, service_data[service][domain].name);
ret = -EINVAL;
goto done;
}
service_data[service][domain].state = curr_state;
service_data[service][domain].handle = handle;
pr_info("%s: service %s is in use\n",
__func__, service_data[service][domain].name);
pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
__func__, service_data[service][domain].name,
service_data[service][domain].state,
service_data[service][domain].handle);
done:
return ret;
}
static int audio_notifer_dereg_service(int service, int domain)
{
int ret;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
ret = audio_ssr_deregister(
service_data[service][domain].handle,
service_data[service][domain].nb);
break;
case AUDIO_NOTIFIER_PDR_SERVICE:
ret = audio_pdr_service_deregister(
service_data[service][domain].handle,
service_data[service][domain].nb);
break;
default:
pr_err("%s: Invalid service %d\n",
__func__, service);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: deregister failed for service %s, ret %d\n",
__func__, service_data[service][domain].name, ret);
goto done;
}
pr_debug("%s: service %s with handle 0x%pK deregistered\n",
__func__, service_data[service][domain].name,
service_data[service][domain].handle);
service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
service_data[service][domain].handle = NULL;
done:
return ret;
}
static int audio_notifer_reg_client_service(struct client_data *client_data,
int service)
{
int ret = 0;
int domain = client_data->domain;
struct audio_notifier_cb_data data;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
case AUDIO_NOTIFIER_PDR_SERVICE:
if (service_data[service][domain].num_of_clients == 0)
ret = audio_notifer_reg_service(service, domain);
break;
default:
pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
__func__, client_data->client_name, service, domain);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: service registration failed on service %s for client %s\n",
__func__, service_data[service][domain].name,
client_data->client_name);
goto done;
}
client_data->service = service;
srcu_notifier_chain_register(
&service_data[service][domain].client_nb_list,
client_data->nb);
service_data[service][domain].num_of_clients++;
pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
__func__, client_data->client_name,
service_data[service][domain].name,
service_data[service][domain].state);
/*
* PDR registration returns current state
* Force callback of client with current state for PDR
*/
if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
data.service = service;
data.domain = domain;
(void)client_data->nb->notifier_call(client_data->nb,
service_data[service][domain].state, &data);
}
done:
return ret;
}
static int audio_notifer_reg_client(struct client_data *client_data)
{
int ret = 0;
int service;
int domain = client_data->domain;
service = audio_notifer_get_default_service(domain);
if (service < 0) {
pr_err("%s: service %d is incorrect\n", __func__, service);
ret = -EINVAL;
goto done;
}
/* Search through services to find a valid one to register client on. */
for (; service >= 0; service--) {
/* If a service is not initialized, wait for it to come up. */
if (service_data[service][domain].state == UNINIT_SERVICE)
goto done;
/* Skip unsupported service and domain combinations. */
if (service_data[service][domain].state < 0)
continue;
/* Only register clients who have not acquired a service. */
if (client_data->service != NO_SERVICE)
continue;
/*
* Only register clients, who have not acquired a service, on
* the best available service for their domain. Uninitialized
* services will try to register all of their clients after
* they initialize correctly or will disable their service and
* register clients on the next best avaialable service.
*/
pr_debug("%s: register client %s on service %s",
__func__, client_data->client_name,
service_data[service][domain].name);
ret = audio_notifer_reg_client_service(client_data, service);
if (ret < 0)
pr_err("%s: client %s failed to register on service %s",
__func__, client_data->client_name,
service_data[service][domain].name);
}
done:
return ret;
}
static int audio_notifer_dereg_client(struct client_data *client_data)
{
int ret = 0;
int service = client_data->service;
int domain = client_data->domain;
switch (client_data->service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
case AUDIO_NOTIFIER_PDR_SERVICE:
if (service_data[service][domain].num_of_clients == 1)
ret = audio_notifer_dereg_service(service, domain);
break;
case NO_SERVICE:
goto done;
default:
pr_err("%s: Invalid service for client %s, service %d\n",
__func__, client_data->client_name,
client_data->service);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
__func__, client_data->client_name,
service_data[service][domain].name, ret);
goto done;
}
ret = srcu_notifier_chain_unregister(&service_data[service][domain].
client_nb_list, client_data->nb);
if (ret < 0) {
pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
__func__, ret);
goto done;
}
pr_debug("%s: deregistered client %s on service %s\n",
__func__, client_data->client_name,
service_data[service][domain].name);
client_data->service = NO_SERVICE;
if (service_data[service][domain].num_of_clients > 0)
service_data[service][domain].num_of_clients--;
done:
return ret;
}
static void audio_notifer_reg_all_clients(void)
{
struct list_head *ptr, *next;
struct client_data *client_data;
int ret;
list_for_each_safe(ptr, next, &client_list) {
client_data = list_entry(ptr, struct client_data, list);
ret = audio_notifer_reg_client(client_data);
if (ret < 0)
pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n",
__func__, client_data->client_name,
ret);
}
}
static int audio_notifer_pdr_callback(struct notifier_block *this,
unsigned long opcode, void *data)
{
pr_debug("%s: Audio PDR framework state 0x%lx\n",
__func__, opcode);
mutex_lock(&notifier_mutex);
if (opcode == AUDIO_PDR_FRAMEWORK_DOWN)
audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
else
audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
audio_notifer_reg_all_clients();
mutex_unlock(&notifier_mutex);
return 0;
}
static struct notifier_block pdr_nb = {
.notifier_call = audio_notifer_pdr_callback,
.priority = 0,
};
static int audio_notifer_convert_opcode(unsigned long opcode,
unsigned long *notifier_opcode)
{
int ret = 0;
switch (opcode) {
case SUBSYS_BEFORE_SHUTDOWN:
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
break;
case SUBSYS_AFTER_POWERUP:
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
break;
default:
pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
ret = -EINVAL;
}
return ret;
}
static int audio_notifer_service_cb(unsigned long opcode,
int service, int domain)
{
int ret = 0;
unsigned long notifier_opcode;
struct audio_notifier_cb_data data;
if (audio_notifer_convert_opcode(opcode, &notifier_opcode) < 0)
goto done;
data.service = service;
data.domain = domain;
pr_debug("%s: service %s, opcode 0x%lx\n",
__func__, service_data[service][domain].name, notifier_opcode);
mutex_lock(&notifier_mutex);
service_data[service][domain].state = notifier_opcode;
ret = srcu_notifier_call_chain(&service_data[service][domain].
client_nb_list, notifier_opcode, &data);
if (ret < 0)
pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
__func__, ret, service_data[service][domain].name,
notifier_opcode);
mutex_unlock(&notifier_mutex);
done:
return NOTIFY_OK;
}
static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_PDR_SERVICE,
AUDIO_NOTIFIER_ADSP_DOMAIN);
}
static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
if (opcode == SUBSYS_BEFORE_SHUTDOWN)
audio_ssr_send_nmi(data);
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_SSR_SERVICE,
AUDIO_NOTIFIER_ADSP_DOMAIN);
}
static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_SSR_SERVICE,
AUDIO_NOTIFIER_MODEM_DOMAIN);
}
int audio_notifier_deregister(char *client_name)
{
int ret = 0;
int ret2;
struct list_head *ptr, *next;
struct client_data *client_data;
if (client_name == NULL) {
pr_err("%s: client_name is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
mutex_lock(&notifier_mutex);
list_for_each_safe(ptr, next, &client_list) {
client_data = list_entry(ptr, struct client_data, list);
if (!strcmp(client_name, client_data->client_name)) {
ret2 = audio_notifer_dereg_client(client_data);
if (ret2 < 0) {
pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d",
__func__, ret2, client_data->service,
client_data->domain);
ret = ret2;
continue;
}
list_del(&client_data->list);
kfree(client_data);
}
}
mutex_unlock(&notifier_mutex);
done:
return ret;
}
EXPORT_SYMBOL(audio_notifier_deregister);
int audio_notifier_register(char *client_name, int domain,
struct notifier_block *nb)
{
int ret;
struct client_data *client_data;
if (client_name == NULL) {
pr_err("%s: client_name is NULL\n", __func__);
ret = -EINVAL;
goto done;
} else if (nb == NULL) {
pr_err("%s: Notifier block is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
if (client_data == NULL) {
ret = -ENOMEM;
goto done;
}
INIT_LIST_HEAD(&client_data->list);
client_data->nb = nb;
strlcpy(client_data->client_name, client_name,
sizeof(client_data->client_name));
client_data->service = NO_SERVICE;
client_data->domain = domain;
mutex_lock(&notifier_mutex);
ret = audio_notifer_reg_client(client_data);
if (ret < 0) {
mutex_unlock(&notifier_mutex);
pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n",
__func__, client_data->client_name,
ret);
kfree(client_data);
goto done;
}
list_add_tail(&client_data->list, &client_list);
mutex_unlock(&notifier_mutex);
done:
return ret;
}
EXPORT_SYMBOL(audio_notifier_register);
static int __init audio_notifier_subsys_init(void)
{
int i, j;
mutex_init(&notifier_mutex);
INIT_LIST_HEAD(&client_list);
for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
if (service_data[i][j].state <= NO_SERVICE)
continue;
srcu_init_notifier_head(
&service_data[i][j].client_nb_list);
}
}
return 0;
}
subsys_initcall(audio_notifier_subsys_init);
static int __init audio_notifier_init(void)
{
int ret;
ret = audio_pdr_register(&pdr_nb);
if (ret < 0) {
pr_debug("%s: PDR register failed, ret = %d, disable service\n",
__func__, ret);
audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
}
/* Do not return error since PDR enablement is not critical */
return 0;
}
module_init(audio_notifier_init);
static int __init audio_notifier_late_init(void)
{
/*
* If pdr registration failed, register clients on next service
* Do in late init to ensure that SSR subsystem is initialized
*/
mutex_lock(&notifier_mutex);
if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
audio_notifer_reg_all_clients();
mutex_unlock(&notifier_mutex);
return 0;
}
late_initcall(audio_notifier_late_init);

Melihat File

@@ -0,0 +1,147 @@
/* Copyright (c) 2016-2017, 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/module.h>
#include <linux/slab.h>
#include <linux/qdsp6v2/audio_pdr.h>
#include <soc/qcom/service-locator.h>
#include <soc/qcom/service-notifier.h>
static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = {
{ /* AUDIO_PDR_DOMAIN_ADSP */
.client_name = "audio_pdr_adsp",
.service_name = "avs/audio"
}
};
struct srcu_notifier_head audio_pdr_cb_list;
static int audio_pdr_locator_callback(struct notifier_block *this,
unsigned long opcode, void *data)
{
unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN;
if (opcode == LOCATOR_DOWN) {
pr_debug("%s: Service %s is down!", __func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
service_name);
goto done;
}
memcpy(&audio_pdr_services, data,
sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]));
if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) {
pr_debug("%s: Service %s, returned total domains %d, ",
__func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
total_domains);
pdr_state = AUDIO_PDR_FRAMEWORK_UP;
goto done;
} else
pr_err("%s: Service %s returned invalid total domains %d",
__func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
total_domains);
done:
srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL);
return NOTIFY_OK;
}
static struct notifier_block audio_pdr_locator_nb = {
.notifier_call = audio_pdr_locator_callback,
.priority = 0,
};
int audio_pdr_register(struct notifier_block *nb)
{
if (nb == NULL) {
pr_err("%s: Notifier block is NULL\n", __func__);
return -EINVAL;
}
return srcu_notifier_chain_register(&audio_pdr_cb_list, nb);
}
EXPORT_SYMBOL(audio_pdr_register);
void *audio_pdr_service_register(int domain_id,
struct notifier_block *nb, int *curr_state)
{
void *handle;
if ((domain_id < 0) ||
(domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
return ERR_PTR(-EINVAL);
}
handle = service_notif_register_notifier(
audio_pdr_services[domain_id].domain_list[0].name,
audio_pdr_services[domain_id].domain_list[0].instance_id,
nb, curr_state);
if (IS_ERR_OR_NULL(handle)) {
pr_err("%s: Failed to register for service %s, instance %d\n",
__func__,
audio_pdr_services[domain_id].domain_list[0].name,
audio_pdr_services[domain_id].domain_list[0].
instance_id);
}
return handle;
}
EXPORT_SYMBOL(audio_pdr_service_register);
int audio_pdr_service_deregister(void *service_handle,
struct notifier_block *nb)
{
int ret;
if (service_handle == NULL) {
pr_err("%s: service handle is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
ret = service_notif_unregister_notifier(
service_handle, nb);
if (ret < 0)
pr_err("%s: Failed to deregister service ret %d\n",
__func__, ret);
done:
return ret;
}
EXPORT_SYMBOL(audio_pdr_service_deregister);
static int __init audio_pdr_subsys_init(void)
{
srcu_init_notifier_head(&audio_pdr_cb_list);
return 0;
}
subsys_initcall(audio_pdr_subsys_init);
static int __init audio_pdr_late_init(void)
{
int ret;
ret = get_service_location(
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
&audio_pdr_locator_nb);
if (ret < 0) {
pr_err("%s get_service_location failed ret %d\n",
__func__, ret);
srcu_notifier_call_chain(&audio_pdr_cb_list,
AUDIO_PDR_FRAMEWORK_DOWN, NULL);
}
return ret;
}
late_initcall(audio_pdr_late_init);

Melihat File

@@ -0,0 +1,66 @@
/* Copyright (c) 2016, 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/module.h>
#include <linux/qdsp6v2/audio_ssr.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
#define SCM_Q6_NMI_CMD 0x1
static char *audio_ssr_domains[] = {
"adsp",
"modem"
};
void *audio_ssr_register(int domain_id, struct notifier_block *nb)
{
if ((domain_id < 0) ||
(domain_id >= AUDIO_SSR_DOMAIN_MAX)) {
pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
return ERR_PTR(-EINVAL);
}
return subsys_notif_register_notifier(
audio_ssr_domains[domain_id], nb);
}
EXPORT_SYMBOL(audio_ssr_register);
int audio_ssr_deregister(void *handle, struct notifier_block *nb)
{
return subsys_notif_unregister_notifier(handle, nb);
}
EXPORT_SYMBOL(audio_ssr_deregister);
void audio_ssr_send_nmi(void *ssr_cb_data)
{
struct notif_data *data = (struct notif_data *)ssr_cb_data;
struct scm_desc desc;
if (data && data->crashed) {
/* Send NMI to QDSP6 via an SCM call. */
if (!is_scm_armv8()) {
scm_call_atomic1(SCM_SVC_UTIL,
SCM_Q6_NMI_CMD, 0x1);
} else {
desc.args[0] = 0x1;
desc.arginfo = SCM_ARGS(1);
scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL,
SCM_Q6_NMI_CMD), &desc);
}
/* The write should go through before q6 is shutdown */
mb();
pr_debug("%s: Q6 NMI was sent.\n", __func__);
}
}
EXPORT_SYMBOL(audio_ssr_send_nmi);

Melihat File

@@ -0,0 +1,280 @@
/*
* Copyright (c) 2012-2014, 2017, 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/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
#include <linux/workqueue.h>
#include <soc/qcom/subsystem_restart.h>
#define BOOT_CMD 1
#define IMAGE_UNLOAD_CMD 0
#define CDSP_SUBSYS_DOWN 0
#define CDSP_SUBSYS_LOADED 1
static ssize_t cdsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
struct cdsp_loader_private {
void *pil_h;
struct kobject *boot_cdsp_obj;
struct attribute_group *attr_group;
};
static struct kobj_attribute cdsp_boot_attribute =
__ATTR(boot, 0220, NULL, cdsp_boot_store);
static struct attribute *attrs[] = {
&cdsp_boot_attribute.attr,
NULL,
};
static u32 cdsp_state = CDSP_SUBSYS_DOWN;
static struct platform_device *cdsp_private;
static struct work_struct cdsp_ldr_work;
static void cdsp_loader_unload(struct platform_device *pdev);
static void cdsp_load_fw(struct work_struct *cdsp_ldr_work)
{
struct platform_device *pdev = cdsp_private;
struct cdsp_loader_private *priv = NULL;
int rc = 0;
const char *img_name;
if (!pdev) {
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
goto fail;
}
if (!pdev->dev.of_node) {
dev_err(&pdev->dev,
"%s: Device tree information missing\n", __func__);
goto fail;
}
rc = of_property_read_string(pdev->dev.of_node,
"qcom,proc-img-to-load",
&img_name);
if (rc)
goto fail;
if (!strcmp(img_name, "cdsp")) {
/* cdsp_state always returns "0".*/
if (cdsp_state == CDSP_SUBSYS_DOWN) {
priv = platform_get_drvdata(pdev);
if (!priv) {
dev_err(&pdev->dev,
" %s: Private data get failed\n", __func__);
goto fail;
}
priv->pil_h = subsystem_get("cdsp");
if (IS_ERR(priv->pil_h)) {
dev_err(&pdev->dev, "%s: pil get failed,\n",
__func__);
goto fail;
}
/* Set the state of the CDSP.*/
cdsp_state = CDSP_SUBSYS_LOADED;
} else if (cdsp_state == CDSP_SUBSYS_LOADED) {
dev_dbg(&pdev->dev,
"%s: CDSP state = %x\n", __func__, cdsp_state);
}
dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__);
return;
}
fail:
dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__);
}
static void cdsp_loader_do(struct platform_device *pdev)
{
schedule_work(&cdsp_ldr_work);
}
static ssize_t cdsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf,
size_t count)
{
int boot = 0, ret = 0;
ret = sscanf(buf, "%du", &boot);
if (ret != 1)
pr_debug("%s: invalid arguments for cdsp_loader.\n", __func__);
if (boot == BOOT_CMD) {
pr_debug("%s: going to call cdsp_loader_do\n", __func__);
cdsp_loader_do(cdsp_private);
} else if (boot == IMAGE_UNLOAD_CMD) {
pr_debug("%s: going to call cdsp_unloader\n", __func__);
cdsp_loader_unload(cdsp_private);
}
return count;
}
static void cdsp_loader_unload(struct platform_device *pdev)
{
struct cdsp_loader_private *priv = NULL;
priv = platform_get_drvdata(pdev);
if (!priv)
return;
if (priv->pil_h) {
dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__);
subsystem_put(priv->pil_h);
priv->pil_h = NULL;
}
}
static int cdsp_loader_init_sysfs(struct platform_device *pdev)
{
int ret = -EINVAL;
struct cdsp_loader_private *priv = NULL;
cdsp_private = NULL;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
return ret;
}
platform_set_drvdata(pdev, priv);
priv->pil_h = NULL;
priv->boot_cdsp_obj = NULL;
priv->attr_group = devm_kzalloc(&pdev->dev,
sizeof(*(priv->attr_group)),
GFP_KERNEL);
if (!priv->attr_group) {
dev_err(&pdev->dev, "%s: malloc attr_group failed\n",
__func__);
ret = -ENOMEM;
goto error_return;
}
priv->attr_group->attrs = attrs;
priv->boot_cdsp_obj = kobject_create_and_add("boot_cdsp", kernel_kobj);
if (!priv->boot_cdsp_obj) {
dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
__func__);
ret = -ENOMEM;
goto error_return;
}
ret = sysfs_create_group(priv->boot_cdsp_obj, priv->attr_group);
if (ret) {
dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
__func__, ret);
goto error_return;
}
cdsp_private = pdev;
return 0;
error_return:
if (priv->boot_cdsp_obj) {
kobject_del(priv->boot_cdsp_obj);
priv->boot_cdsp_obj = NULL;
}
return ret;
}
static int cdsp_loader_remove(struct platform_device *pdev)
{
struct cdsp_loader_private *priv = NULL;
priv = platform_get_drvdata(pdev);
if (!priv)
return 0;
if (priv->pil_h) {
subsystem_put(priv->pil_h);
priv->pil_h = NULL;
}
if (priv->boot_cdsp_obj) {
sysfs_remove_group(priv->boot_cdsp_obj, priv->attr_group);
kobject_del(priv->boot_cdsp_obj);
priv->boot_cdsp_obj = NULL;
}
return 0;
}
static int cdsp_loader_probe(struct platform_device *pdev)
{
int ret = cdsp_loader_init_sysfs(pdev);
if (ret != 0) {
dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
return ret;
}
INIT_WORK(&cdsp_ldr_work, cdsp_load_fw);
return 0;
}
static const struct of_device_id cdsp_loader_dt_match[] = {
{ .compatible = "qcom,cdsp-loader" },
{ }
};
MODULE_DEVICE_TABLE(of, cdsp_loader_dt_match);
static struct platform_driver cdsp_loader_driver = {
.driver = {
.name = "cdsp-loader",
.owner = THIS_MODULE,
.of_match_table = cdsp_loader_dt_match,
},
.probe = cdsp_loader_probe,
.remove = cdsp_loader_remove,
};
static int __init cdsp_loader_init(void)
{
return platform_driver_register(&cdsp_loader_driver);
}
module_init(cdsp_loader_init);
static void __exit cdsp_loader_exit(void)
{
platform_driver_unregister(&cdsp_loader_driver);
}
module_exit(cdsp_loader_exit);
MODULE_DESCRIPTION("CDSP Loader module");
MODULE_LICENSE("GPL v2");

Melihat File

@@ -0,0 +1,853 @@
/*
* Copyright (c) 2013-2017, 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/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/dma-buf.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/of_device.h>
#include <linux/msm_audio_ion.h>
#include <linux/export.h>
#include <asm/dma-iommu.h>
#define MSM_AUDIO_ION_PROBED (1 << 0)
#define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \
alloc_data->table->sgl->dma_address
#define MSM_AUDIO_ION_VA_START 0x10000000
#define MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF
#define MSM_AUDIO_SMMU_SID_OFFSET 32
struct msm_audio_ion_private {
bool smmu_enabled;
bool audioheap_enabled;
struct device *cb_dev;
struct dma_iommu_mapping *mapping;
u8 device_status;
struct list_head alloc_list;
struct mutex list_mutex;
u64 smmu_sid_bits;
u32 smmu_version;
};
struct msm_audio_alloc_data {
struct ion_client *client;
struct ion_handle *handle;
size_t len;
struct dma_buf *dma_buf;
struct dma_buf_attachment *attach;
struct sg_table *table;
struct list_head list;
};
static struct msm_audio_ion_private msm_audio_ion_data = {0,};
static int msm_audio_ion_get_phys(struct ion_client *client,
struct ion_handle *handle,
ion_phys_addr_t *addr, size_t *len);
static int msm_audio_dma_buf_map(struct ion_client *client,
struct ion_handle *handle,
ion_phys_addr_t *addr, size_t *len);
static int msm_audio_dma_buf_unmap(struct ion_client *client,
struct ion_handle *handle);
static void msm_audio_ion_add_allocation(
struct msm_audio_ion_private *msm_audio_ion_data,
struct msm_audio_alloc_data *alloc_data)
{
/*
* Since these APIs can be invoked by multiple
* clients, there is need to make sure the list
* of allocations is always protected
*/
mutex_lock(&(msm_audio_ion_data->list_mutex));
list_add_tail(&(alloc_data->list),
&(msm_audio_ion_data->alloc_list));
mutex_unlock(&(msm_audio_ion_data->list_mutex));
}
int msm_audio_ion_alloc(const char *name, struct ion_client **client,
struct ion_handle **handle, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
{
int rc = -EINVAL;
unsigned long err_ion_ptr = 0;
if ((msm_audio_ion_data.smmu_enabled == true) &&
!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
pr_debug("%s:probe is not done, deferred\n", __func__);
return -EPROBE_DEFER;
}
if (!name || !client || !handle || !paddr || !vaddr
|| !bufsz || !pa_len) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
*client = msm_audio_ion_client_create(name);
if (IS_ERR_OR_NULL((void *)(*client))) {
pr_err("%s: ION create client for AUDIO failed\n", __func__);
goto err;
}
*handle = ion_alloc(*client, bufsz, SZ_4K,
ION_HEAP(ION_AUDIO_HEAP_ID), 0);
if (IS_ERR_OR_NULL((void *) (*handle))) {
if (msm_audio_ion_data.smmu_enabled == true) {
pr_debug("system heap is used");
msm_audio_ion_data.audioheap_enabled = 0;
*handle = ion_alloc(*client, bufsz, SZ_4K,
ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
}
if (IS_ERR_OR_NULL((void *) (*handle))) {
if (IS_ERR((void *)(*handle)))
err_ion_ptr = PTR_ERR((int *)(*handle));
pr_err("%s:ION alloc fail err ptr=%ld, smmu_enabled=%d\n",
__func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled);
rc = -ENOMEM;
goto err_ion_client;
}
} else {
pr_debug("audio heap is used");
msm_audio_ion_data.audioheap_enabled = 1;
}
rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
if (rc) {
pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
__func__, rc);
goto err_ion_handle;
}
*vaddr = ion_map_kernel(*client, *handle);
if (IS_ERR_OR_NULL((void *)*vaddr)) {
pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
goto err_ion_handle;
}
pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
*vaddr, bufsz);
if (bufsz != 0) {
pr_debug("%s: memset to 0 %pK %zd\n", __func__, *vaddr, bufsz);
memset((void *)*vaddr, 0, bufsz);
}
return rc;
err_ion_handle:
ion_free(*client, *handle);
err_ion_client:
msm_audio_ion_client_destroy(*client);
*handle = NULL;
*client = NULL;
err:
return rc;
}
EXPORT_SYMBOL(msm_audio_ion_alloc);
int msm_audio_ion_import(const char *name, struct ion_client **client,
struct ion_handle **handle, int fd,
unsigned long *ionflag, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
{
int rc = 0;
if ((msm_audio_ion_data.smmu_enabled == true) &&
!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
pr_debug("%s:probe is not done, deferred\n", __func__);
return -EPROBE_DEFER;
}
if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
pr_err("%s: Invalid params\n", __func__);
rc = -EINVAL;
goto err;
}
*client = msm_audio_ion_client_create(name);
if (IS_ERR_OR_NULL((void *)(*client))) {
pr_err("%s: ION create client for AUDIO failed\n", __func__);
rc = -EINVAL;
goto err;
}
/* name should be audio_acdb_client or Audio_Dec_Client,
* bufsz should be 0 and fd shouldn't be 0 as of now
*/
*handle = ion_import_dma_buf_fd(*client, fd);
pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
name, fd, *handle);
if (IS_ERR_OR_NULL((void *) (*handle))) {
pr_err("%s: ion import dma buffer failed\n",
__func__);
rc = -EINVAL;
goto err_destroy_client;
}
if (ionflag != NULL) {
rc = ion_handle_get_flags(*client, *handle, ionflag);
if (rc) {
pr_err("%s: could not get flags for the handle\n",
__func__);
goto err_ion_handle;
}
}
rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
if (rc) {
pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
__func__, rc);
goto err_ion_handle;
}
*vaddr = ion_map_kernel(*client, *handle);
if (IS_ERR_OR_NULL((void *)*vaddr)) {
pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
rc = -ENOMEM;
goto err_ion_handle;
}
pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
*vaddr, bufsz);
return 0;
err_ion_handle:
ion_free(*client, *handle);
err_destroy_client:
msm_audio_ion_client_destroy(*client);
*client = NULL;
*handle = NULL;
err:
return rc;
}
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
{
if (!client || !handle) {
pr_err("%s Invalid params\n", __func__);
return -EINVAL;
}
if (msm_audio_ion_data.smmu_enabled)
msm_audio_dma_buf_unmap(client, handle);
ion_unmap_kernel(client, handle);
ion_free(client, handle);
msm_audio_ion_client_destroy(client);
return 0;
}
EXPORT_SYMBOL(msm_audio_ion_free);
int msm_audio_ion_mmap(struct audio_buffer *ab,
struct vm_area_struct *vma)
{
struct sg_table *table;
unsigned long addr = vma->vm_start;
unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
struct scatterlist *sg;
unsigned int i;
struct page *page;
int ret;
pr_debug("%s\n", __func__);
table = ion_sg_table(ab->client, ab->handle);
if (IS_ERR(table)) {
pr_err("%s: Unable to get sg_table from ion: %ld\n",
__func__, PTR_ERR(table));
return PTR_ERR(table);
} else if (!table) {
pr_err("%s: sg_list is NULL\n", __func__);
return -EINVAL;
}
/* uncached */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
/* We need to check if a page is associated with this sg list because:
* If the allocation came from a carveout we currently don't have
* pages associated with carved out memory. This might change in the
* future and we can remove this check and the else statement.
*/
page = sg_page(table->sgl);
if (page) {
pr_debug("%s: page is NOT null\n", __func__);
for_each_sg(table->sgl, sg, table->nents, i) {
unsigned long remainder = vma->vm_end - addr;
unsigned long len = sg->length;
page = sg_page(sg);
if (offset >= len) {
offset -= len;
continue;
} else if (offset) {
page += offset / PAGE_SIZE;
len -= offset;
offset = 0;
}
len = min(len, remainder);
pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%lu\n",
vma, (unsigned int)addr, len,
(unsigned int)vma->vm_start,
(unsigned int)vma->vm_end,
(unsigned long)vma->vm_page_prot.pgprot);
remap_pfn_range(vma, addr, page_to_pfn(page), len,
vma->vm_page_prot);
addr += len;
if (addr >= vma->vm_end)
return 0;
}
} else {
ion_phys_addr_t phys_addr;
size_t phys_len;
size_t va_len = 0;
pr_debug("%s: page is NULL\n", __func__);
ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
if (ret) {
pr_err("%s: Unable to get phys address from ION buffer: %d\n"
, __func__, ret);
return ret;
}
pr_debug("phys=%pKK len=%zd\n", &phys_addr, phys_len);
pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%lu\n",
vma, (unsigned int)vma->vm_start,
(unsigned int)vma->vm_end, vma->vm_pgoff,
(unsigned long)vma->vm_page_prot.pgprot);
va_len = vma->vm_end - vma->vm_start;
if ((offset > phys_len) || (va_len > phys_len-offset)) {
pr_err("wrong offset size %ld, lens= %zd, va_len=%zd\n",
offset, phys_len, va_len);
return -EINVAL;
}
ret = remap_pfn_range(vma, vma->vm_start,
__phys_to_pfn(phys_addr) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
return 0;
}
bool msm_audio_ion_is_smmu_available(void)
{
return msm_audio_ion_data.smmu_enabled;
}
/* move to static section again */
struct ion_client *msm_audio_ion_client_create(const char *name)
{
struct ion_client *pclient = NULL;
pclient = msm_ion_client_create(name);
return pclient;
}
void msm_audio_ion_client_destroy(struct ion_client *client)
{
pr_debug("%s: client = %pK smmu_enabled = %d\n", __func__,
client, msm_audio_ion_data.smmu_enabled);
ion_client_destroy(client);
}
int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
struct ion_handle **handle, int fd,
unsigned long *ionflag, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
{
int rc = 0;
if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
pr_err("%s: Invalid params\n", __func__);
rc = -EINVAL;
goto err;
}
/* client is already created for legacy and given
* name should be audio_acdb_client or Audio_Dec_Client,
* bufsz should be 0 and fd shouldn't be 0 as of now
*/
*handle = ion_import_dma_buf_fd(client, fd);
pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
name, fd, *handle);
if (IS_ERR_OR_NULL((void *)(*handle))) {
pr_err("%s: ion import dma buffer failed\n",
__func__);
rc = -EINVAL;
goto err;
}
if (ionflag != NULL) {
rc = ion_handle_get_flags(client, *handle, ionflag);
if (rc) {
pr_err("%s: could not get flags for the handle\n",
__func__);
rc = -EINVAL;
goto err_ion_handle;
}
}
rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len);
if (rc) {
pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
__func__, rc);
rc = -EINVAL;
goto err_ion_handle;
}
/*Need to add condition SMMU enable or not */
*vaddr = ion_map_kernel(client, *handle);
if (IS_ERR_OR_NULL((void *)*vaddr)) {
pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
rc = -EINVAL;
goto err_ion_handle;
}
if (bufsz != 0)
memset((void *)*vaddr, 0, bufsz);
return 0;
err_ion_handle:
ion_free(client, *handle);
err:
return rc;
}
int msm_audio_ion_free_legacy(struct ion_client *client,
struct ion_handle *handle)
{
if (msm_audio_ion_data.smmu_enabled)
msm_audio_dma_buf_unmap(client, handle);
ion_unmap_kernel(client, handle);
ion_free(client, handle);
/* no client_destrody in legacy*/
return 0;
}
int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
{
unsigned long ionflag = 0;
int rc = 0;
int msm_cache_ops = 0;
if (!abuff) {
pr_err("%s: Invalid params: %pK\n", __func__, abuff);
return -EINVAL;
}
rc = ion_handle_get_flags(abuff->client, abuff->handle,
&ionflag);
if (rc) {
pr_err("ion_handle_get_flags failed: %d\n", rc);
goto cache_op_failed;
}
/* has to be CACHED */
if (ION_IS_CACHED(ionflag)) {
/* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
msm_cache_ops = cache_op;
rc = msm_ion_do_cache_op(abuff->client,
abuff->handle,
(unsigned long *) abuff->data,
(unsigned long)abuff->size,
msm_cache_ops);
if (rc) {
pr_err("cache operation failed %d\n", rc);
goto cache_op_failed;
}
}
cache_op_failed:
return rc;
}
static int msm_audio_dma_buf_map(struct ion_client *client,
struct ion_handle *handle,
ion_phys_addr_t *addr, size_t *len)
{
struct msm_audio_alloc_data *alloc_data;
struct device *cb_dev;
int rc = 0;
cb_dev = msm_audio_ion_data.cb_dev;
/* Data required per buffer mapping */
alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL);
if (!alloc_data)
return -ENOMEM;
/* Get the ION handle size */
ion_handle_get_size(client, handle, len);
alloc_data->client = client;
alloc_data->handle = handle;
alloc_data->len = *len;
/* Get the dma_buf handle from ion_handle */
alloc_data->dma_buf = ion_share_dma_buf(client, handle);
if (IS_ERR(alloc_data->dma_buf)) {
rc = PTR_ERR(alloc_data->dma_buf);
dev_err(cb_dev,
"%s: Fail to get dma_buf handle, rc = %d\n",
__func__, rc);
goto err_dma_buf;
}
/* Attach the dma_buf to context bank device */
alloc_data->attach = dma_buf_attach(alloc_data->dma_buf,
cb_dev);
if (IS_ERR(alloc_data->attach)) {
rc = PTR_ERR(alloc_data->attach);
dev_err(cb_dev,
"%s: Fail to attach dma_buf to CB, rc = %d\n",
__func__, rc);
goto err_attach;
}
/*
* Get the scatter-gather list.
* There is no info as this is a write buffer or
* read buffer, hence the request is bi-directional
* to accommodate both read and write mappings.
*/
alloc_data->table = dma_buf_map_attachment(alloc_data->attach,
DMA_BIDIRECTIONAL);
if (IS_ERR(alloc_data->table)) {
rc = PTR_ERR(alloc_data->table);
dev_err(cb_dev,
"%s: Fail to map attachment, rc = %d\n",
__func__, rc);
goto err_map_attach;
}
rc = dma_map_sg(cb_dev, alloc_data->table->sgl,
alloc_data->table->nents,
DMA_BIDIRECTIONAL);
if (rc != alloc_data->table->nents) {
dev_err(cb_dev,
"%s: Fail to map SG, rc = %d, nents = %d\n",
__func__, rc, alloc_data->table->nents);
goto err_map_sg;
}
/* Make sure not to return rc from dma_map_sg, as it can be nonzero */
rc = 0;
/* physical address from mapping */
*addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data);
msm_audio_ion_add_allocation(&msm_audio_ion_data,
alloc_data);
return rc;
err_map_sg:
dma_buf_unmap_attachment(alloc_data->attach,
alloc_data->table,
DMA_BIDIRECTIONAL);
err_map_attach:
dma_buf_detach(alloc_data->dma_buf,
alloc_data->attach);
err_attach:
dma_buf_put(alloc_data->dma_buf);
err_dma_buf:
kfree(alloc_data);
return rc;
}
static int msm_audio_dma_buf_unmap(struct ion_client *client,
struct ion_handle *handle)
{
int rc = 0;
struct msm_audio_alloc_data *alloc_data = NULL;
struct list_head *ptr, *next;
struct device *cb_dev = msm_audio_ion_data.cb_dev;
bool found = false;
/*
* Though list_for_each_safe is delete safe, lock
* should be explicitly acquired to avoid race condition
* on adding elements to the list.
*/
mutex_lock(&(msm_audio_ion_data.list_mutex));
list_for_each_safe(ptr, next,
&(msm_audio_ion_data.alloc_list)) {
alloc_data = list_entry(ptr, struct msm_audio_alloc_data,
list);
if (alloc_data->handle == handle &&
alloc_data->client == client) {
found = true;
dma_unmap_sg(cb_dev,
alloc_data->table->sgl,
alloc_data->table->nents,
DMA_BIDIRECTIONAL);
dma_buf_unmap_attachment(alloc_data->attach,
alloc_data->table,
DMA_BIDIRECTIONAL);
dma_buf_detach(alloc_data->dma_buf,
alloc_data->attach);
dma_buf_put(alloc_data->dma_buf);
list_del(&(alloc_data->list));
kfree(alloc_data);
break;
}
}
mutex_unlock(&(msm_audio_ion_data.list_mutex));
if (!found) {
dev_err(cb_dev,
"%s: cannot find allocation, ion_handle %pK, ion_client %pK",
__func__, handle, client);
rc = -EINVAL;
}
return rc;
}
static int msm_audio_ion_get_phys(struct ion_client *client,
struct ion_handle *handle,
ion_phys_addr_t *addr, size_t *len)
{
int rc = 0;
pr_debug("%s: smmu_enabled = %d\n", __func__,
msm_audio_ion_data.smmu_enabled);
if (msm_audio_ion_data.smmu_enabled) {
rc = msm_audio_dma_buf_map(client, handle, addr, len);
if (rc) {
pr_err("%s: failed to map DMA buf, err = %d\n",
__func__, rc);
goto err;
}
/* Append the SMMU SID information to the IOVA address */
*addr |= msm_audio_ion_data.smmu_sid_bits;
} else {
rc = ion_phys(client, handle, addr, len);
}
pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc);
err:
return rc;
}
static int msm_audio_smmu_init(struct device *dev)
{
struct dma_iommu_mapping *mapping;
int ret;
mapping = arm_iommu_create_mapping(&platform_bus_type,
MSM_AUDIO_ION_VA_START,
MSM_AUDIO_ION_VA_LEN);
if (IS_ERR(mapping))
return PTR_ERR(mapping);
ret = arm_iommu_attach_device(dev, mapping);
if (ret) {
dev_err(dev, "%s: Attach failed, err = %d\n",
__func__, ret);
goto fail_attach;
}
msm_audio_ion_data.cb_dev = dev;
msm_audio_ion_data.mapping = mapping;
INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list);
mutex_init(&(msm_audio_ion_data.list_mutex));
return 0;
fail_attach:
arm_iommu_release_mapping(mapping);
return ret;
}
static const struct of_device_id msm_audio_ion_dt_match[] = {
{ .compatible = "qcom,msm-audio-ion" },
{ }
};
MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match);
u32 msm_audio_ion_get_smmu_sid_mode32(void)
{
if (msm_audio_ion_data.smmu_enabled)
return upper_32_bits(msm_audio_ion_data.smmu_sid_bits);
else
return 0;
}
u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa)
{
if (sizeof(ion_phys_addr_t) == sizeof(u32))
return msm_audio_ion_get_smmu_sid_mode32();
else
return upper_32_bits(pa);
}
static int msm_audio_ion_probe(struct platform_device *pdev)
{
int rc = 0;
const char *msm_audio_ion_dt = "qcom,smmu-enabled";
const char *msm_audio_ion_smmu = "qcom,smmu-version";
const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask";
bool smmu_enabled;
enum apr_subsys_state q6_state;
struct device *dev = &pdev->dev;
if (dev->of_node == NULL) {
dev_err(dev,
"%s: device tree is not found\n",
__func__);
msm_audio_ion_data.smmu_enabled = 0;
return 0;
}
smmu_enabled = of_property_read_bool(dev->of_node,
msm_audio_ion_dt);
msm_audio_ion_data.smmu_enabled = smmu_enabled;
if (smmu_enabled) {
rc = of_property_read_u32(dev->of_node,
msm_audio_ion_smmu,
&msm_audio_ion_data.smmu_version);
if (rc) {
dev_err(dev,
"%s: qcom,smmu_version missing in DT node\n",
__func__);
return rc;
}
dev_dbg(dev, "%s: SMMU version is (%d)", __func__,
msm_audio_ion_data.smmu_version);
q6_state = apr_get_q6_state();
if (q6_state == APR_SUBSYS_DOWN) {
dev_dbg(dev,
"defering %s, adsp_state %d\n",
__func__, q6_state);
return -EPROBE_DEFER;
}
dev_dbg(dev, "%s: adsp is ready\n", __func__);
}
dev_dbg(dev, "%s: SMMU is %s\n", __func__,
(smmu_enabled) ? "Enabled" : "Disabled");
if (smmu_enabled) {
u64 smmu_sid = 0;
u64 smmu_sid_mask = 0;
struct of_phandle_args iommuspec;
/* Get SMMU SID information from Devicetree */
rc = of_property_read_u64(dev->of_node,
msm_audio_ion_smmu_sid_mask,
&smmu_sid_mask);
if (rc) {
dev_err(dev,
"%s: qcom,smmu-sid-mask missing in DT node, using default\n",
__func__);
smmu_sid_mask = 0xFFFFFFFFFFFFFFFF;
}
rc = of_parse_phandle_with_args(dev->of_node, "iommus",
"#iommu-cells", 0, &iommuspec);
if (rc)
dev_err(dev, "%s: could not get smmu SID, ret = %d\n",
__func__, rc);
else
smmu_sid = (iommuspec.args[0] & smmu_sid_mask);
msm_audio_ion_data.smmu_sid_bits =
smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET;
if (msm_audio_ion_data.smmu_version == 0x2) {
rc = msm_audio_smmu_init(dev);
} else {
dev_err(dev, "%s: smmu version invalid %d\n",
__func__, msm_audio_ion_data.smmu_version);
rc = -EINVAL;
}
if (rc)
dev_err(dev, "%s: smmu init failed, err = %d\n",
__func__, rc);
}
if (!rc)
msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED;
return rc;
}
static int msm_audio_ion_remove(struct platform_device *pdev)
{
struct dma_iommu_mapping *mapping;
struct device *audio_cb_dev;
mapping = msm_audio_ion_data.mapping;
audio_cb_dev = msm_audio_ion_data.cb_dev;
if (audio_cb_dev && mapping) {
arm_iommu_detach_device(audio_cb_dev);
arm_iommu_release_mapping(mapping);
}
msm_audio_ion_data.smmu_enabled = 0;
msm_audio_ion_data.device_status = 0;
return 0;
}
static struct platform_driver msm_audio_ion_driver = {
.driver = {
.name = "msm-audio-ion",
.owner = THIS_MODULE,
.of_match_table = msm_audio_ion_dt_match,
},
.probe = msm_audio_ion_probe,
.remove = msm_audio_ion_remove,
};
static int __init msm_audio_ion_init(void)
{
return platform_driver_register(&msm_audio_ion_driver);
}
module_init(msm_audio_ion_init);
static void __exit msm_audio_ion_exit(void)
{
platform_driver_unregister(&msm_audio_ion_driver);
}
module_exit(msm_audio_ion_exit);
MODULE_DESCRIPTION("MSM Audio ION module");
MODULE_LICENSE("GPL v2");

17
drivers/soundwire/Kconfig Normal file
Melihat File

@@ -0,0 +1,17 @@
#
# SOUNDWIRE driver configuration
#
menuconfig SOUNDWIRE
bool "Soundwire support"
help
Soundwire is a two wire interface for audio to connect
simple peripheral components in mobile devices.
if SOUNDWIRE
config SOUNDWIRE_WCD_CTRL
depends on WCD9XXX_CODEC_CORE
tristate "QTI WCD CODEC Soundwire controller"
default n
help
Select driver for QTI's Soundwire Master Component.
endif

Melihat File

@@ -0,0 +1,5 @@
#
# Makefile for kernel soundwire framework.
#
obj-$(CONFIG_SOUNDWIRE) += soundwire.o
obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o

File diff ditekan karena terlalu besar Load Diff

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,106 @@
/* Copyright (c) 2015-2017, 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 _SWR_WCD_CTRL_H
#define _SWR_WCD_CTRL_H
#include <linux/module.h>
#include <linux/soundwire/swr-wcd.h>
#define SWR_MAX_ROW 0 /* Rows = 48 */
#define SWR_MAX_COL 7 /* Cols = 16 */
#define SWR_MIN_COL 0 /* Cols = 2 */
#define SWR_WCD_NAME "swr-wcd"
#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */
#define SWRM_VERSION_1_0 0x01010000
#define SWRM_VERSION_1_2 0x01030000
#define SWRM_VERSION_1_3 0x01040000
enum {
SWR_MSTR_PAUSE,
SWR_MSTR_RESUME,
SWR_MSTR_UP,
SWR_MSTR_DOWN,
};
enum {
SWR_IRQ_FREE,
SWR_IRQ_REGISTER,
};
enum {
SWR_DAC_PORT,
SWR_COMP_PORT,
SWR_BOOST_PORT,
SWR_VISENSE_PORT,
};
struct usecase {
u8 num_port;
u8 num_ch;
u32 chrate;
};
struct port_params {
u8 si;
u8 off1;
u8 off2;
};
struct swrm_mports {
struct list_head list;
u8 id;
};
struct swr_ctrl_platform_data {
void *handle; /* holds priv data */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
int (*clk)(void *handle, bool enable);
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
};
struct swr_mstr_ctrl {
struct swr_master master;
struct device *dev;
struct resource *supplies;
struct clk *mclk;
struct completion reset;
struct completion broadcast;
struct mutex mlock;
struct mutex reslock;
u8 rcmd_id;
u8 wcmd_id;
void *handle; /* SWR Master handle from client for read and writes */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
int (*clk)(void *handle, bool enable);
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
int version;
int num_enum_slaves;
int slave_status;
struct swr_mstr_port *mstr_port;
struct list_head mport_list;
int state;
struct platform_device *pdev;
int num_rx_chs;
u8 num_cfg_devs;
};
#endif /* _SWR_WCD_CTRL_H */

Melihat File

@@ -0,0 +1,204 @@
/* Copyright (c) 2015, 2017 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 _SWRM_REGISTERS_H
#define _SWRM_REGISTERS_H
#define SWRM_BASE_ADDRESS 0x00
#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS
#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004)
#define SWRM_COMP_CFG_RMSK 0x3
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1
#define SWRM_COMP_CFG_ENABLE_BMSK 0x1
#define SWRM_COMP_CFG_ENABLE_SHFT 0x0
#define SWRM_COMP_SW_RESET (SWRM_BASE_ADDRESS+0x00000008)
#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100)
#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F
#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0
#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000
#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000
#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000
#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200)
#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFD
#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2
#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4
#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20
#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40
#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80
#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100
#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000
#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204)
#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6
#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80
#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10
#define SWRM_INTERRUPT_MAX 0x11
#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208)
#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300)
#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF
#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304)
#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF
#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308)
#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C)
#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00
#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000
#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314)
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0
#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318)
#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500)
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m)
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m)
#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m)
#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x00ff07ff
#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0
#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK 0xff0000
#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT 16
#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK 0xf800
#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT 11
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0
#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044)
#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1
#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0
#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK 0x2
#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT 0x1
#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048)
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11
#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK 0x02
#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C)
#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01
#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090)
#define SWRM_MCP_SLV_STATUS_MASK 0x03
#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \
0x00001124 + \
0x100*(n-1) + \
0x40*m)
#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF
#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000
#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18
#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10
#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08
#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00
/* Soundwire Slave Register definition */
#define SWRS_BASE_ADDRESS 0x00
#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank))
#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x120 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x122 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x124 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x125 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x126 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x127 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \
0x10*m)
#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE_ADDRESS + 0xE0 + \
0x10*m)
#endif /* _SWRM_REGISTERS_H */

2
include/Kbuild Normal file
Melihat File

@@ -0,0 +1,2 @@
# Top-level Makefile calls into asm-$(ARCH)
# List only non-arch directories below

Melihat File

@@ -0,0 +1,24 @@
/*
* 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 _AVTIMER_H
#define _AVTIMER_H
#include <uapi/linux/avtimer.h>
int avcs_core_open(void);
int avcs_core_disable_power_collapse(int disable);/* true or flase */
int avcs_core_query_timer(uint64_t *avtimer_tick);
#endif

Melihat File

@@ -0,0 +1,49 @@
/* Copyright (c) 2016-2017, 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 __MFD_CDC_PINCTRL_H_
#define __MFD_CDC_PINCTRL_H_
#include <linux/types.h>
#include <linux/of.h>
#if IS_ENABLED(CONFIG_MSM_CDC_PINCTRL)
extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np);
extern int msm_cdc_pinctrl_select_active_state(struct device_node *np);
extern bool msm_cdc_pinctrl_get_state(struct device_node *np);
extern int msm_cdc_get_gpio_state(struct device_node *np);
int msm_cdc_pinctrl_drv_init(void);
void msm_cdc_pinctrl_drv_exit(void);
#else
int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
{
return 0;
}
int msm_cdc_pinctrl_select_active_state(struct device_node *np)
{
return 0;
}
int msm_cdc_get_gpio_state(struct device_node *np)
{
return 0;
}
int msm_cdc_pinctrl_drv_init(void)
{
return 0;
}
void msm_cdc_pinctrl_drv_exit(void)
{
}
#endif
#endif

Melihat File

@@ -0,0 +1,48 @@
/* Copyright (c) 2016, 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 __CODEC_POWER_SUPPLY_H__
#define __CODEC_POWER_SUPPLY_H__
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
struct cdc_regulator {
const char *name;
int min_uV;
int max_uV;
int optimum_uA;
bool ondemand;
struct regulator *regulator;
};
extern int msm_cdc_get_power_supplies(struct device *dev,
struct cdc_regulator **cdc_vreg,
int *total_num_supplies);
extern int msm_cdc_disable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_release_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_enable_static_supplies(struct device *dev,
struct regulator_bulk_data *supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
extern int msm_cdc_init_supplies(struct device *dev,
struct regulator_bulk_data **supplies,
struct cdc_regulator *cdc_vreg,
int num_supplies);
#endif

Melihat File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2017, 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 __WCD9335_IRQ_H_
#define __WCD9335_IRQ_H_
enum {
/* INTR_REG 0 */
WCD9335_IRQ_FLL_LOCK_LOSS = 1,
WCD9335_IRQ_HPH_PA_OCPL_FAULT,
WCD9335_IRQ_HPH_PA_OCPR_FAULT,
WCD9335_IRQ_EAR_PA_OCP_FAULT,
WCD9335_IRQ_HPH_PA_CNPL_COMPLETE,
WCD9335_IRQ_HPH_PA_CNPR_COMPLETE,
WCD9335_IRQ_EAR_PA_CNP_COMPLETE,
/* INTR_REG 1 */
WCD9335_IRQ_MBHC_SW_DET,
WCD9335_IRQ_MBHC_ELECT_INS_REM_DET,
WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
WCD9335_IRQ_RESERVED_0,
WCD9335_IRQ_RESERVED_1,
WCD9335_IRQ_RESERVED_2,
/* INTR_REG 2 */
WCD9335_IRQ_LINE_PA1_CNP_COMPLETE,
WCD9335_IRQ_LINE_PA2_CNP_COMPLETE,
WCD9335_IRQ_LINE_PA3_CNP_COMPLETE,
WCD9335_IRQ_LINE_PA4_CNP_COMPLETE,
WCD9335_IRQ_SOUNDWIRE,
WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE,
WCD9335_IRQ_RCO_ERROR,
WCD9335_IRQ_SVA_ERROR,
/* INTR_REG 3 */
WCD9335_IRQ_MAD_AUDIO,
WCD9335_IRQ_MAD_BEACON,
WCD9335_IRQ_MAD_ULTRASOUND,
WCD9335_IRQ_VBAT_ATTACK,
WCD9335_IRQ_VBAT_RESTORE,
WCD9335_IRQ_SVA_OUTBOX1,
WCD9335_IRQ_SVA_OUTBOX2,
WCD9335_NUM_IRQS,
};
#endif

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2017, 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 __WCD934X_IRQ_H_
#define __WCD934X_IRQ_H_
enum {
/* INTR_REG 0 */
WCD934X_IRQ_MISC = 1,
WCD934X_IRQ_HPH_PA_OCPL_FAULT,
WCD934X_IRQ_HPH_PA_OCPR_FAULT,
WCD934X_IRQ_EAR_PA_OCP_FAULT,
WCD934X_IRQ_HPH_PA_CNPL_COMPLETE,
WCD934X_IRQ_HPH_PA_CNPR_COMPLETE,
WCD934X_IRQ_EAR_PA_CNP_COMPLETE,
/* INTR_REG 1 */
WCD934X_IRQ_MBHC_SW_DET,
WCD934X_IRQ_MBHC_ELECT_INS_REM_DET,
WCD934X_IRQ_MBHC_BUTTON_PRESS_DET,
WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET,
WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
WCD934X_IRQ_RESERVED_0,
WCD934X_IRQ_RESERVED_1,
WCD934X_IRQ_RESERVED_2,
/* INTR_REG 2 */
WCD934X_IRQ_LINE_PA1_CNP_COMPLETE,
WCD934X_IRQ_LINE_PA2_CNP_COMPLETE,
WCD934X_IRQ_SLNQ_ANALOG_ERROR,
WCD934X_IRQ_RESERVED_3,
WCD934X_IRQ_SOUNDWIRE,
WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE,
WCD934X_IRQ_RCO_ERROR,
WCD934X_IRQ_CPE_ERROR,
/* INTR_REG 3 */
WCD934X_IRQ_MAD_AUDIO,
WCD934X_IRQ_MAD_BEACON,
WCD934X_IRQ_MAD_ULTRASOUND,
WCD934X_IRQ_VBAT_ATTACK,
WCD934X_IRQ_VBAT_RESTORE,
WCD934X_IRQ_CPE1_INTR,
WCD934X_IRQ_RESERVED_4,
WCD934X_IRQ_SLNQ_DIGITAL,
WCD934X_NUM_IRQS,
};
#endif

File diff ditekan karena terlalu besar Load Diff

Melihat File

@@ -0,0 +1,440 @@
/* Copyright (c) 2011-2017, 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 __MFD_TABLA_CORE_H__
#define __MFD_TABLA_CORE_H__
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
#define WCD9XXX_MAX_IRQ_REGS 4
#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8)
#define WCD9XXX_SLIM_NUM_PORT_REG 3
#define TABLA_VERSION_1_0 0
#define TABLA_VERSION_1_1 1
#define TABLA_VERSION_2_0 2
#define TABLA_IS_1_X(ver) \
(((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0)
#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0)
#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck"
#define SITAR_VERSION_1P0 0
#define SITAR_VERSION_1P1 1
#define SITAR_IS_1P0(ver) \
((ver == SITAR_VERSION_1P0) ? 1 : 0)
#define SITAR_IS_1P1(ver) \
((ver == SITAR_VERSION_1P1) ? 1 : 0)
#define TAIKO_VERSION_1_0 1
#define TAIKO_IS_1_0(ver) \
((ver == TAIKO_VERSION_1_0) ? 1 : 0)
#define TAPAN_VERSION_1_0 0
#define TAPAN_IS_1_0(ver) \
((ver == TAPAN_VERSION_1_0) ? 1 : 0)
#define TOMTOM_VERSION_1_0 1
#define TOMTOM_IS_1_0(ver) \
((ver == TOMTOM_VERSION_1_0) ? 1 : 0)
#define TASHA_VERSION_1_0 0
#define TASHA_VERSION_1_1 1
#define TASHA_VERSION_2_0 2
#define TASHA_IS_1_0(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0)
#define TASHA_IS_1_1(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0)
#define TASHA_IS_2_0(wcd) \
((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0)
/*
* As fine version info cannot be retrieved before tavil probe.
* Define three coarse versions for possible future use before tavil probe.
*/
#define TAVIL_VERSION_1_0 0
#define TAVIL_VERSION_1_1 1
#define TAVIL_VERSION_WCD9340_1_0 2
#define TAVIL_VERSION_WCD9341_1_0 3
#define TAVIL_VERSION_WCD9340_1_1 4
#define TAVIL_VERSION_WCD9341_1_1 5
#define TAVIL_IS_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_1_0 || \
wcd->version == TAVIL_VERSION_WCD9340_1_0 || \
wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_1_1 || \
wcd->version == TAVIL_VERSION_WCD9340_1_1 || \
wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9340_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9341_1_0(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9340_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0)
#define TAVIL_IS_WCD9341_1_1(wcd) \
((wcd->type == WCD934X) ? \
((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
#define IS_CODEC_TYPE(wcd, wcdtype) \
((wcd->type == wcdtype) ? true : false)
#define IS_CODEC_VERSION(wcd, wcdversion) \
((wcd->version == wcdversion) ? true : false)
enum {
CDC_V_1_0,
CDC_V_1_1,
CDC_V_2_0,
};
enum codec_variant {
WCD9XXX,
WCD9330,
WCD9335,
WCD9326,
WCD934X,
};
enum wcd9xxx_slim_slave_addr_type {
WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0,
WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1,
};
enum wcd9xxx_pm_state {
WCD9XXX_PM_SLEEPABLE,
WCD9XXX_PM_AWAKE,
WCD9XXX_PM_ASLEEP,
};
enum {
WCD9XXX_INTR_STATUS_BASE = 0,
WCD9XXX_INTR_CLEAR_BASE,
WCD9XXX_INTR_MASK_BASE,
WCD9XXX_INTR_LEVEL_BASE,
WCD9XXX_INTR_CLR_COMMIT,
WCD9XXX_INTR_REG_MAX,
};
enum wcd9xxx_intf_status {
WCD9XXX_INTERFACE_TYPE_PROBING,
WCD9XXX_INTERFACE_TYPE_SLIMBUS,
WCD9XXX_INTERFACE_TYPE_I2C,
};
enum {
/* INTR_REG 0 */
WCD9XXX_IRQ_SLIMBUS = 0,
WCD9XXX_IRQ_MBHC_REMOVAL,
WCD9XXX_IRQ_MBHC_SHORT_TERM,
WCD9XXX_IRQ_MBHC_PRESS,
WCD9XXX_IRQ_MBHC_RELEASE,
WCD9XXX_IRQ_MBHC_POTENTIAL,
WCD9XXX_IRQ_MBHC_INSERTION,
WCD9XXX_IRQ_BG_PRECHARGE,
/* INTR_REG 1 */
WCD9XXX_IRQ_PA1_STARTUP,
WCD9XXX_IRQ_PA2_STARTUP,
WCD9XXX_IRQ_PA3_STARTUP,
WCD9XXX_IRQ_PA4_STARTUP,
WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP,
WCD9XXX_IRQ_PA5_STARTUP,
WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
/* INTR_REG 2 */
WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_L_PA_STARTUP,
WCD9XXX_IRQ_HPH_R_PA_STARTUP,
WCD9320_IRQ_EAR_PA_STARTUP,
WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP,
WCD9310_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_1,
WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS,
WCD9330_IRQ_MBHC_JACK_SWITCH,
/* INTR_REG 3 */
WCD9XXX_IRQ_MAD_AUDIO,
WCD9XXX_IRQ_MAD_ULTRASOUND,
WCD9XXX_IRQ_MAD_BEACON,
WCD9XXX_IRQ_SPEAKER_CLIPPING,
WCD9320_IRQ_MBHC_JACK_SWITCH,
WCD9306_NUM_IRQS,
WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS,
WCD9XXX_IRQ_VBAT_MONITOR_RELEASE,
WCD9XXX_NUM_IRQS,
/* WCD9330 INTR1_REG 3*/
WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO,
WCD9330_IRQ_MAD_AUDIO,
WCD9330_IRQ_MAD_ULTRASOUND,
WCD9330_IRQ_MAD_BEACON,
WCD9330_IRQ_SPEAKER1_CLIPPING,
WCD9330_IRQ_SPEAKER2_CLIPPING,
WCD9330_IRQ_VBAT_MONITOR_ATTACK,
WCD9330_IRQ_VBAT_MONITOR_RELEASE,
WCD9330_NUM_IRQS,
WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS,
};
enum {
TABLA_NUM_IRQS = WCD9310_NUM_IRQS,
SITAR_NUM_IRQS = WCD9310_NUM_IRQS,
TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
TAPAN_NUM_IRQS = WCD9306_NUM_IRQS,
TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS,
};
struct intr_data {
int intr_num;
bool clear_first;
};
struct wcd9xxx_core_resource {
struct mutex irq_lock;
struct mutex nested_irq_lock;
enum wcd9xxx_pm_state pm_state;
struct mutex pm_lock;
/* pm_wq notifies change of pm_state */
wait_queue_head_t pm_wq;
struct pm_qos_request pm_qos_req;
int wlock_holders;
/* holds the table of interrupts per codec */
const struct intr_data *intr_table;
int intr_table_size;
unsigned int irq_base;
unsigned int irq;
u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS];
u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS];
bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
int num_irqs;
int num_irq_regs;
u16 intr_reg[WCD9XXX_INTR_REG_MAX];
struct regmap *wcd_core_regmap;
/* Pointer to parent container data structure */
void *parent;
struct device *dev;
struct irq_domain *domain;
};
/*
* data structure for Slimbus and I2S channel.
* Some of fields are only used in smilbus mode
*/
struct wcd9xxx_ch {
u32 sph; /* share channel handle - slimbus only */
u32 ch_num; /*
* vitrual channel number, such as 128 -144.
* apply for slimbus only
*/
u16 ch_h; /* chanel handle - slimbus only */
u16 port; /*
* tabla port for RX and TX
* such as 0-9 for TX and 10 -16 for RX
* apply for both i2s and slimbus
*/
u16 shift; /*
* shift bit for RX and TX
* apply for both i2s and slimbus
*/
struct list_head list; /*
* channel link list
* apply for both i2s and slimbus
*/
};
struct wcd9xxx_codec_dai_data {
u32 rate; /* sample rate */
u32 bit_width; /* sit width 16,24,32 */
struct list_head wcd9xxx_ch_list; /* channel list */
u16 grph; /* slimbus group handle */
unsigned long ch_mask;
wait_queue_head_t dai_wait;
bool bus_down_in_recovery;
};
#define WCD9XXX_CH(xport, xshift) \
{.port = xport, .shift = xshift}
enum wcd9xxx_chipid_major {
TABLA_MAJOR = cpu_to_le16(0x100),
SITAR_MAJOR = cpu_to_le16(0x101),
TAIKO_MAJOR = cpu_to_le16(0x102),
TAPAN_MAJOR = cpu_to_le16(0x103),
TOMTOM_MAJOR = cpu_to_le16(0x105),
TASHA_MAJOR = cpu_to_le16(0x0),
TASHA2P0_MAJOR = cpu_to_le16(0x107),
TAVIL_MAJOR = cpu_to_le16(0x108),
};
enum codec_power_states {
WCD_REGION_POWER_COLLAPSE_REMOVE,
WCD_REGION_POWER_COLLAPSE_BEGIN,
WCD_REGION_POWER_DOWN,
};
enum wcd_power_regions {
WCD9XXX_DIG_CORE_REGION_1,
WCD9XXX_MAX_PWR_REGIONS,
};
struct wcd9xxx_codec_type {
u16 id_major;
u16 id_minor;
struct mfd_cell *dev;
int size;
int num_irqs;
int version; /* -1 to retrieve version from chip version register */
enum wcd9xxx_slim_slave_addr_type slim_slave_type;
u16 i2c_chip_status;
const struct intr_data *intr_tbl;
int intr_tbl_size;
u16 intr_reg[WCD9XXX_INTR_REG_MAX];
};
struct wcd9xxx_power_region {
enum codec_power_states power_state;
u16 pwr_collapse_reg_min;
u16 pwr_collapse_reg_max;
};
struct wcd9xxx {
struct device *dev;
struct slim_device *slim;
struct slim_device *slim_slave;
struct mutex io_lock;
struct mutex xfer_lock;
struct mutex reset_lock;
u8 version;
int reset_gpio;
struct device_node *wcd_rst_np;
int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg);
int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data,
size_t count);
int (*dev_down)(struct wcd9xxx *wcd9xxx);
int (*post_reset)(struct wcd9xxx *wcd9xxx);
void *ssr_priv;
bool dev_up;
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
struct wcd9xxx_core_resource core_res;
u16 id_minor;
u16 id_major;
/* Slimbus or I2S port */
u32 num_rx_port;
u32 num_tx_port;
struct wcd9xxx_ch *rx_chs;
struct wcd9xxx_ch *tx_chs;
u32 mclk_rate;
enum codec_variant type;
struct regmap *regmap;
struct wcd9xxx_codec_type *codec_type;
bool prev_pg_valid;
u8 prev_pg;
u8 avoid_cdc_rstlow;
struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS];
};
struct wcd9xxx_reg_val {
unsigned short reg; /* register address */
u8 *buf; /* buffer to be written to reg. addr */
int bytes; /* number of bytes to be written */
};
int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
u8 val);
int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src);
int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx,
u32 bw_ops, bool commit);
int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states,
enum wcd_power_regions);
int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx,
enum wcd_power_regions);
int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg);
int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx,
struct wcd9xxx_reg_val *bulk_reg,
unsigned int size, bool interface);
extern int wcd9xxx_core_res_init(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
int num_irqs, int num_irq_regs, struct regmap *wcd_regmap);
extern void wcd9xxx_core_res_deinit(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_core_res_suspend(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
pm_message_t pmesg);
extern int wcd9xxx_core_res_resume(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_core_irq_init(
struct wcd9xxx_core_resource *wcd9xxx_core_res);
extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res,
unsigned int irq,
unsigned int irq_base);
extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void);
extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status);
extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
struct wcd9xxx_core_resource *wcd9xxx_core_res,
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
struct device_node *parent)
{
return 0;
}
int wcd9xxx_init(void);
void wcd9xxx_exit(void);
#endif

Melihat File

@@ -0,0 +1,197 @@
/* Copyright (c) 2011-2017, 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 __MFD_WCD9XXX_PDATA_H__
#define __MFD_WCD9XXX_PDATA_H__
#include <linux/slimbus/slimbus.h>
#include <linux/mfd/msm-cdc-supply.h>
#define MICBIAS_EXT_BYP_CAP 0x00
#define MICBIAS_NO_EXT_BYP_CAP 0x01
#define SITAR_LDOH_1P95_V 0x0
#define SITAR_LDOH_2P35_V 0x1
#define SITAR_LDOH_2P75_V 0x2
#define SITAR_LDOH_2P85_V 0x3
#define SITAR_CFILT1_SEL 0x0
#define SITAR_CFILT2_SEL 0x1
#define SITAR_CFILT3_SEL 0x2
#define WCD9XXX_LDOH_1P95_V 0x0
#define WCD9XXX_LDOH_2P35_V 0x1
#define WCD9XXX_LDOH_2P75_V 0x2
#define WCD9XXX_LDOH_2P85_V 0x3
#define WCD9XXX_LDOH_3P0_V 0x3
#define TABLA_LDOH_1P95_V 0x0
#define TABLA_LDOH_2P35_V 0x1
#define TABLA_LDOH_2P75_V 0x2
#define TABLA_LDOH_2P85_V 0x3
#define TABLA_CFILT1_SEL 0x0
#define TABLA_CFILT2_SEL 0x1
#define TABLA_CFILT3_SEL 0x2
#define MAX_AMIC_CHANNEL 7
#define TABLA_OCP_300_MA 0x0
#define TABLA_OCP_350_MA 0x2
#define TABLA_OCP_365_MA 0x3
#define TABLA_OCP_150_MA 0x4
#define TABLA_OCP_190_MA 0x6
#define TABLA_OCP_220_MA 0x7
#define TABLA_DCYCLE_255 0x0
#define TABLA_DCYCLE_511 0x1
#define TABLA_DCYCLE_767 0x2
#define TABLA_DCYCLE_1023 0x3
#define TABLA_DCYCLE_1279 0x4
#define TABLA_DCYCLE_1535 0x5
#define TABLA_DCYCLE_1791 0x6
#define TABLA_DCYCLE_2047 0x7
#define TABLA_DCYCLE_2303 0x8
#define TABLA_DCYCLE_2559 0x9
#define TABLA_DCYCLE_2815 0xA
#define TABLA_DCYCLE_3071 0xB
#define TABLA_DCYCLE_3327 0xC
#define TABLA_DCYCLE_3583 0xD
#define TABLA_DCYCLE_3839 0xE
#define TABLA_DCYCLE_4095 0xF
#define WCD9XXX_MCLK_CLK_12P288MHZ 12288000
#define WCD9XXX_MCLK_CLK_9P6HZ 9600000
/* Only valid for 9.6 MHz mclk */
#define WCD9XXX_DMIC_SAMPLE_RATE_600KHZ 600000
#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000
#define WCD9XXX_DMIC_SAMPLE_RATE_3P2MHZ 3200000
#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000
/* Only valid for 12.288 MHz mclk */
#define WCD9XXX_DMIC_SAMPLE_RATE_768KHZ 768000
#define WCD9XXX_DMIC_SAMPLE_RATE_2P048MHZ 2048000
#define WCD9XXX_DMIC_SAMPLE_RATE_3P072MHZ 3072000
#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000
#define WCD9XXX_DMIC_SAMPLE_RATE_6P144MHZ 6144000
#define WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED 0
#define WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED 0
struct wcd9xxx_amic {
/*legacy mode, txfe_enable and txfe_buff take 7 input
* each bit represent the channel / TXFE number
* and numbered as below
* bit 0 = channel 1 / TXFE1_ENABLE / TXFE1_BUFF
* bit 1 = channel 2 / TXFE2_ENABLE / TXFE2_BUFF
* ...
* bit 7 = channel 7 / TXFE7_ENABLE / TXFE7_BUFF
*/
u8 legacy_mode:MAX_AMIC_CHANNEL;
u8 txfe_enable:MAX_AMIC_CHANNEL;
u8 txfe_buff:MAX_AMIC_CHANNEL;
u8 use_pdata:MAX_AMIC_CHANNEL;
};
/* Each micbias can be assigned to one of three cfilters
* Vbatt_min >= .15V + ldoh_v
* ldoh_v >= .15v + cfiltx_mv
* If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv
* If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv
* If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv
* If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv
*/
struct wcd9xxx_micbias_setting {
u8 ldoh_v;
u32 cfilt1_mv; /* in mv */
u32 cfilt2_mv; /* in mv */
u32 cfilt3_mv; /* in mv */
u32 micb1_mv;
u32 micb2_mv;
u32 micb3_mv;
u32 micb4_mv;
/* Different WCD9xxx series codecs may not
* have 4 mic biases. If a codec has fewer
* mic biases, some of these properties will
* not be used.
*/
u8 bias1_cfilt_sel;
u8 bias2_cfilt_sel;
u8 bias3_cfilt_sel;
u8 bias4_cfilt_sel;
u8 bias1_cap_mode;
u8 bias2_cap_mode;
u8 bias3_cap_mode;
u8 bias4_cap_mode;
bool bias2_is_headset_only;
};
struct wcd9xxx_ocp_setting {
unsigned int use_pdata:1; /* 0 - use sys default as recommended */
unsigned int num_attempts:4; /* up to 15 attempts */
unsigned int run_time:4; /* in duty cycle */
unsigned int wait_time:4; /* in duty cycle */
unsigned int hph_ocp_limit:3; /* Headphone OCP current limit */
};
#define WCD9XXX_MAX_REGULATOR 9
/*
* format : TABLA_<POWER_SUPPLY_PIN_NAME>_CUR_MAX
*
* <POWER_SUPPLY_PIN_NAME> from Tabla objective spec
*/
#define WCD9XXX_CDC_VDDA_CP_CUR_MAX 500000
#define WCD9XXX_CDC_VDDA_RX_CUR_MAX 20000
#define WCD9XXX_CDC_VDDA_TX_CUR_MAX 20000
#define WCD9XXX_VDDIO_CDC_CUR_MAX 5000
#define WCD9XXX_VDDD_CDC_D_CUR_MAX 5000
#define WCD9XXX_VDDD_CDC_A_CUR_MAX 5000
#define WCD9XXX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv"
#define WCD9XXX_VDD_SPKDRV2_NAME "cdc-vdd-spkdrv-2"
struct wcd9xxx_regulator {
const char *name;
int min_uV;
int max_uV;
int optimum_uA;
bool ondemand;
struct regulator *regulator;
};
struct wcd9xxx_pdata {
int irq;
int irq_base;
int num_irqs;
int reset_gpio;
struct device_node *wcd_rst_np;
struct wcd9xxx_amic amic_settings;
struct slim_device slimbus_slave_device;
struct wcd9xxx_micbias_setting micbias;
struct wcd9xxx_ocp_setting ocp;
struct cdc_regulator *regulator;
int num_supplies;
u32 mclk_rate;
u32 dmic_sample_rate;
u32 mad_dmic_sample_rate;
u32 ecpp_dmic_sample_rate;
u32 dmic_clk_drv;
u16 use_pinctrl;
};
#endif

Melihat File

@@ -0,0 +1,37 @@
/* Copyright (c) 2016-2017, 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/types.h>
#include <linux/mfd/wcd9xxx/core.h>
#ifndef __MFD_WCD9XXX_IRQ_H
#define __MFD_WCD9XXX_IRQ_H
bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res);
void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res);
void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res);
void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res);
int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq,
irq_handler_t handler, const char *name, void *data);
void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
int irq, void *data);
void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
int irq);
void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *wcd9xxx_res,
int irq);
int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res);
void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res);
int wcd9xxx_irq_drv_init(void);
void wcd9xxx_irq_drv_exit(void);
#endif

Melihat File

@@ -0,0 +1,119 @@
/* Copyright (c) 2012-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 __WCD9310_SLIMSLAVE_H_
#define __WCD9310_SLIMSLAVE_H_
#include <linux/slimbus/slimbus.h>
#include <linux/mfd/wcd9xxx/core.h>
/*
* client is expected to give port ids in the range of
* 1-10 for pre Taiko Tx ports and 1-16 for Taiko
* 1-7 for pre Taiko Rx ports and 1-16 for Tako,
* we need to add offset for getting the absolute slave
* port id before configuring the HW
*/
#define TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 10
#define TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 16
#define SLIM_MAX_TX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
#define TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \
TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
#define TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \
TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
#define TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 7
#define TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 13
#define SLIM_MAX_RX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS
#define SLIM_MAX_REG_ADDR (0x180 + 4 * (SLIM_MAX_RX_PORTS))
#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \
TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS
#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \
TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS
#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 16
#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 31
#define TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 9
#define TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 15
/* below details are taken from SLIMBUS slave SWI */
#define SB_PGD_PORT_BASE 0x000
#define SB_PGD_PORT_CFG_BYTE_ADDR(offset, port_num) \
(SB_PGD_PORT_BASE + offset + (1 * port_num))
#define SB_PGD_TX_PORT_MULTI_CHANNEL_0(port_num) \
(SB_PGD_PORT_BASE + 0x100 + 4*port_num)
#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_START_PORT_ID 0
#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID 7
#define SB_PGD_TX_PORT_MULTI_CHANNEL_1(port_num) \
(SB_PGD_PORT_BASE + 0x101 + 4*port_num)
#define SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID 8
#define SB_PGD_RX_PORT_MULTI_CHANNEL_0(offset, port_num) \
(SB_PGD_PORT_BASE + offset + (4 * port_num))
/* slave port water mark level
* (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
*/
#define SLAVE_PORT_WATER_MARK_6BYTES 0
#define SLAVE_PORT_WATER_MARK_9BYTES 1
#define SLAVE_PORT_WATER_MARK_12BYTES 2
#define SLAVE_PORT_WATER_MARK_15BYTES 3
#define SLAVE_PORT_WATER_MARK_SHIFT 1
#define SLAVE_PORT_ENABLE 1
#define SLAVE_PORT_DISABLE 0
#define WATER_MARK_VAL \
((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
(SLAVE_PORT_ENABLE))
#define BASE_CH_NUM 128
int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
u8 wcd9xxx_pgd_la,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);
int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list,
unsigned int rate, unsigned int bit_width,
u16 *grph);
int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list,
unsigned int rate, unsigned int bit_width,
u16 *grph);
int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph);
int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph);
int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
unsigned int *rx_ch,
unsigned int *tx_ch);
int wcd9xxx_get_slave_port(unsigned int ch_num);
int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph);
int wcd9xxx_rx_vport_validation(u32 port_id,
struct list_head *codec_dai_list);
int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
struct wcd9xxx_codec_dai_data *codec_dai,
u32 num_codec_dais);
#endif /* __WCD9310_SLIMSLAVE_H_ */

Melihat File

@@ -0,0 +1,40 @@
/* Copyright (c) 2016-2017, 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_UTILS_H__
#define __WCD9XXX_UTILS_H__
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include <linux/mfd/wcd9xxx/core.h>
struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev);
int wcd9xxx_bringup(struct device *dev);
int wcd9xxx_bringdown(struct device *dev);
struct regmap *wcd9xxx_regmap_init(struct device *dev,
const struct regmap_config *config);
int wcd9xxx_reset(struct device *dev);
int wcd9xxx_reset_low(struct device *dev);
int wcd9xxx_get_codec_info(struct device *dev);
typedef int (*codec_bringup_fn)(struct wcd9xxx *);
typedef int (*codec_bringdown_fn)(struct wcd9xxx *);
typedef int (*codec_type_fn)(struct wcd9xxx *,
struct wcd9xxx_codec_type *);
codec_bringdown_fn wcd9xxx_bringdown_fn(int type);
codec_bringup_fn wcd9xxx_bringup_fn(int type);
codec_type_fn wcd9xxx_get_codec_info_fn(int type);
#endif

190
include/linux/qdsp6v2/apr.h Normal file
Melihat File

@@ -0,0 +1,190 @@
/* Copyright (c) 2010-2016, 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 __APR_H_
#define __APR_H_
#include <linux/mutex.h>
#include <soc/qcom/subsystem_notif.h>
enum apr_subsys_state {
APR_SUBSYS_DOWN,
APR_SUBSYS_UP,
APR_SUBSYS_LOADED,
};
struct apr_q6 {
void *pil;
atomic_t q6_state;
atomic_t modem_state;
struct mutex lock;
};
struct apr_hdr {
uint16_t hdr_field;
uint16_t pkt_size;
uint8_t src_svc;
uint8_t src_domain;
uint16_t src_port;
uint8_t dest_svc;
uint8_t dest_domain;
uint16_t dest_port;
uint32_t token;
uint32_t opcode;
};
#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len))
#define APR_HDR_FIELD(msg_type, hdr_len, ver)\
(((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF))
#define APR_HDR_SIZE sizeof(struct apr_hdr)
/* Version */
#define APR_PKT_VER 0x0
/* Command and Response Types */
#define APR_MSG_TYPE_EVENT 0x0
#define APR_MSG_TYPE_CMD_RSP 0x1
#define APR_MSG_TYPE_SEQ_CMD 0x2
#define APR_MSG_TYPE_NSEQ_CMD 0x3
#define APR_MSG_TYPE_MAX 0x04
/* APR Basic Response Message */
#define APR_BASIC_RSP_RESULT 0x000110E8
#define APR_RSP_ACCEPTED 0x000100BE
/* Domain IDs */
#define APR_DOMAIN_SIM 0x1
#define APR_DOMAIN_PC 0x2
#define APR_DOMAIN_MODEM 0x3
#define APR_DOMAIN_ADSP 0x4
#define APR_DOMAIN_APPS 0x5
#define APR_DOMAIN_MAX 0x6
/* ADSP service IDs */
#define APR_SVC_TEST_CLIENT 0x2
#define APR_SVC_ADSP_CORE 0x3
#define APR_SVC_AFE 0x4
#define APR_SVC_VSM 0x5
#define APR_SVC_VPM 0x6
#define APR_SVC_ASM 0x7
#define APR_SVC_ADM 0x8
#define APR_SVC_ADSP_MVM 0x09
#define APR_SVC_ADSP_CVS 0x0A
#define APR_SVC_ADSP_CVP 0x0B
#define APR_SVC_USM 0x0C
#define APR_SVC_LSM 0x0D
#define APR_SVC_VIDC 0x16
#define APR_SVC_MAX 0x17
/* Modem Service IDs */
#define APR_SVC_MVS 0x3
#define APR_SVC_MVM 0x4
#define APR_SVC_CVS 0x5
#define APR_SVC_CVP 0x6
#define APR_SVC_SRD 0x7
/* APR Port IDs */
#define APR_MAX_PORTS 0x80
#define APR_NAME_MAX 0x40
#define RESET_EVENTS 0x000130D7
#define LPASS_RESTART_EVENT 0x1000
#define LPASS_RESTART_READY 0x1001
struct apr_client_data {
uint16_t reset_event;
uint16_t reset_proc;
uint16_t payload_size;
uint16_t hdr_len;
uint16_t msg_type;
uint16_t src;
uint16_t dest_svc;
uint16_t src_port;
uint16_t dest_port;
uint32_t token;
uint32_t opcode;
void *payload;
};
typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv);
struct apr_svc {
uint16_t id;
uint16_t dest_id;
uint16_t client_id;
uint16_t dest_domain;
uint8_t rvd;
uint8_t port_cnt;
uint8_t svc_cnt;
uint8_t need_reset;
apr_fn port_fn[APR_MAX_PORTS];
void *port_priv[APR_MAX_PORTS];
apr_fn fn;
void *priv;
struct mutex m_lock;
spinlock_t w_lock;
uint8_t pkt_owner;
};
struct apr_client {
uint8_t id;
uint8_t svc_cnt;
uint8_t rvd;
struct mutex m_lock;
struct apr_svc_ch_dev *handle;
struct apr_svc svc[APR_SVC_MAX];
};
struct apr_rx_intents {
int num_of_intents;
uint32_t size;
};
struct apr_pkt_cfg {
uint8_t pkt_owner;
struct apr_rx_intents intents;
};
int apr_load_adsp_image(void);
struct apr_client *apr_get_client(int dest_id, int client_id);
int apr_wait_for_device_up(int dest_id);
int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
int *svc_idx, int *svc_id);
void apr_cb_func(void *buf, int len, void *priv);
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv);
inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port,
uint16_t msg_type, uint16_t dest_port,
uint32_t token, uint32_t opcode, uint16_t len);
int apr_send_pkt(void *handle, uint32_t *buf);
int apr_deregister(void *handle);
void subsys_notif_register(char *client_name, int domain,
struct notifier_block *nb);
int apr_get_dest_id(char *dest);
uint16_t apr_get_data_src(struct apr_hdr *hdr);
void change_q6_state(int state);
void q6audio_dsp_not_responding(void);
void apr_reset(void *handle);
enum apr_subsys_state apr_get_subsys_state(void);
enum apr_subsys_state apr_get_modem_state(void);
void apr_set_modem_state(enum apr_subsys_state state);
enum apr_subsys_state apr_get_q6_state(void);
int apr_set_q6_state(enum apr_subsys_state state);
void apr_set_subsys_state(void);
const char *apr_get_lpass_subsys_name(void);
uint16_t apr_get_reset_domain(uint16_t proc);
#endif

Melihat File

@@ -0,0 +1,90 @@
/* Copyright (c) 2010-2011, 2016-2017 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 __APR_TAL_H_
#define __APR_TAL_H_
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/uaccess.h>
/* APR Client IDs */
#define APR_CLIENT_AUDIO 0x0
#define APR_CLIENT_VOICE 0x1
#define APR_CLIENT_MAX 0x2
#define APR_DL_SMD 0
#define APR_DL_MAX 1
#define APR_DEST_MODEM 0
#define APR_DEST_QDSP6 1
#define APR_DEST_MAX 2
#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \
defined(CONFIG_MSM_QDSP6_APRV3_GLINK)
#define APR_MAX_BUF 512
#else
#define APR_MAX_BUF 8092
#endif
#define APR_DEFAULT_NUM_OF_INTENTS 20
#define APR_OPEN_TIMEOUT_MS 5000
enum {
/* If client sets the pkt_owner to APR_PKT_OWNER_DRIVER, APR
* driver will allocate a buffer, where the user packet is
* copied into, for each and every single Tx transmission.
* The buffer is thereafter passed to underlying link layer
* and freed upon the notification received from the link layer
* that the packet has been consumed.
*/
APR_PKT_OWNER_DRIVER,
/* If client sets the pkt_owner to APR_PKT_OWNER_CLIENT, APR
* will pass the user packet memory address directly to underlying
* link layer. In this case it is the client's responsibility to
* make sure the packet is intact until being notified that the
* packet has been consumed.
*/
APR_PKT_OWNER_CLIENT,
};
struct apr_pkt_priv {
/* This property is only applicable for APR over Glink.
* It is ignored in APR over SMD cases.
*/
uint8_t pkt_owner;
};
typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv);
struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
uint32_t dl, apr_svc_cb_fn func, void *priv);
int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
struct apr_pkt_priv *pkt_priv, int len);
int apr_tal_close(struct apr_svc_ch_dev *apr_ch);
int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch,
int num_of_intents, uint32_t size);
struct apr_svc_ch_dev {
void *handle;
spinlock_t w_lock;
spinlock_t r_lock;
struct mutex m_lock;
apr_svc_cb_fn func;
wait_queue_head_t wait;
void *priv;
unsigned int channel_state;
bool if_remote_intent_ready;
};
#endif

Some files were not shown because too many files have changed in this diff Show More