mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
modules: cirrus_playback: add Speaker Enhancement
add Cirrus Speaker Enhanement driver as DLKM. Change-Id: Ibad2b4d5f6cac12ce8d6660cb41928ef8562fe10 Signed-off-by: Yoon (Seungyoon) Lee <w22905@motorola.com> Reviewed-on: https://gerrit.mot.com/1215912 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Rav(CR) Vembu <raviv@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
b3a388ffe8
commit
f20a95ede9
5 changed files with 1119 additions and 0 deletions
|
|
@ -0,0 +1,18 @@
|
|||
Cirrus Logic MSM Framework Driver
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "cirrus,msm-cirrus-playback"
|
||||
|
||||
- usecase-names : List of strings representing the various usecases
|
||||
available on a given platform (max of 4).
|
||||
|
||||
- ff-port-id : Port index of feedforward speaker enhancement module
|
||||
|
||||
Example:
|
||||
|
||||
msm-cirrus-playback {
|
||||
compatible = "cirrus,msm-cirrus-playback";
|
||||
usecase-names = "Playback", "Voice", "Headset", "Handsfree";
|
||||
ff-port-id = "16384";
|
||||
};
|
||||
118
include/sound/msm-cirrus-playback.h
Normal file
118
include/sound/msm-cirrus-playback.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* Copyright (c) 2015 Cirrus Logic, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License 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 MSM_CIRRUS_PLAYBACK_H
|
||||
#define MSM_CIRRUS_PLAYBACK_H
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <dsp/apr_audio-v2.h>
|
||||
#include <dsp/q6afe-v2.h>
|
||||
#include <dsp/q6audio-v2.h>
|
||||
#include <uapi/sound/cirrus-playback.h>
|
||||
|
||||
struct afe_custom_crus_set_config_t {
|
||||
struct apr_hdr hdr;
|
||||
struct afe_port_cmd_set_param_v2 param;
|
||||
struct afe_port_param_data_v2 data;
|
||||
} __packed;
|
||||
|
||||
struct afe_custom_crus_get_config_t {
|
||||
struct apr_hdr hdr;
|
||||
struct afe_port_cmd_get_param_v2 param;
|
||||
struct afe_port_param_data_v2 data;
|
||||
} __packed;
|
||||
|
||||
/* Payload struct for getting or setting one integer value from/to the DSP
|
||||
* module
|
||||
*/
|
||||
struct crus_single_data_t {
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
/* Payload struct for getting or setting two integer values from/to the DSP
|
||||
* module
|
||||
*/
|
||||
struct crus_dual_data_t {
|
||||
int32_t data1;
|
||||
int32_t data2;
|
||||
};
|
||||
|
||||
/* Payload struct for getting or setting three integer values from/to the DSP
|
||||
* module
|
||||
*/
|
||||
struct crus_triple_data_t {
|
||||
int32_t data1;
|
||||
int32_t data2;
|
||||
int32_t data3;
|
||||
};
|
||||
|
||||
/* Payload struct for setting the RX and TX use cases */
|
||||
struct crus_rx_run_case_ctrl_t {
|
||||
int32_t value;
|
||||
int32_t status_l;
|
||||
int32_t checksum_l;
|
||||
int32_t z_l;
|
||||
int32_t status_r;
|
||||
int32_t checksum_r;
|
||||
int32_t z_r;
|
||||
int32_t atemp;
|
||||
};
|
||||
|
||||
/* Payload struct for getting calibration result from DSP module */
|
||||
struct cirrus_cal_result_t {
|
||||
int32_t status_l;
|
||||
int32_t checksum_l;
|
||||
int32_t z_l;
|
||||
int32_t status_r;
|
||||
int32_t checksum_r;
|
||||
int32_t z_r;
|
||||
};
|
||||
|
||||
#define APR_CHUNK_SIZE 256
|
||||
#define CONFIG_FILE_SIZE 128
|
||||
#define PAYLOAD_FOLLOWS_CONFIG 4
|
||||
#define MAX_TUNING_CONFIGS 4
|
||||
#define MIN_CHAN_SWAP_SAMPLES 48
|
||||
#define MAX_CHAN_SWAP_SAMPLES 9600
|
||||
|
||||
/* Payload struct for sending an external configuration string to the DSP
|
||||
* module
|
||||
*/
|
||||
struct crus_external_config_t {
|
||||
uint32_t total_size;
|
||||
uint32_t chunk_size;
|
||||
int32_t done;
|
||||
int32_t reserved;
|
||||
int32_t config;
|
||||
char data[APR_CHUNK_SIZE];
|
||||
};
|
||||
|
||||
/* Payload struct for sending an external tuning transition string to the DSP
|
||||
* module
|
||||
*/
|
||||
struct crus_delta_config_t {
|
||||
uint32_t total_size;
|
||||
uint32_t chunk_size;
|
||||
int32_t done;
|
||||
int32_t index;
|
||||
int32_t reserved;
|
||||
int32_t config;
|
||||
char data[APR_CHUNK_SIZE];
|
||||
};
|
||||
|
||||
extern int afe_apr_send_pkt_crus(void *data, int index);
|
||||
extern int crus_afe_set_callback(
|
||||
int32_t (*crus_afe_callback_func)(void *payload, int size));
|
||||
void msm_crus_pb_add_controls(struct snd_soc_platform *platform);
|
||||
|
||||
#endif /* _MSM_CIRRUS_PLAYBACK_H */
|
||||
|
||||
|
|
@ -54,6 +54,12 @@ LOCAL_MODULE_TAGS := optional
|
|||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := cirrus_playback.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := snd_soc_tfa9874.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ ifneq ($(filter m y,$(CONFIG_SND_SOC_CS35L41_SPI)),)
|
|||
else
|
||||
cirrus_cs35l41-objs += cs35l41-i2c.o
|
||||
endif
|
||||
|
||||
cirrus_playback-objs = msm-cirrus-playback.o
|
||||
obj-m += cirrus_playback.o
|
||||
|
||||
snd_soc_tfa9874-objs += tfa98xx.o
|
||||
snd_soc_tfa9874-objs += tfa_container.o
|
||||
snd_soc_tfa9874-objs += tfa_dsp.o
|
||||
|
|
|
|||
973
sound/soc/codecs/msm-cirrus-playback.c
Normal file
973
sound/soc/codecs/msm-cirrus-playback.c
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <sound/msm-cirrus-playback.h>
|
||||
|
||||
#define CRUS_RX_CONFIG "crus_sp_rx%d.bin"
|
||||
|
||||
static struct device *crus_se_device;
|
||||
static atomic_t crus_se_misc_usage_count;
|
||||
|
||||
static struct crus_single_data_t crus_enable;
|
||||
|
||||
static struct crus_se_ioctl_header crus_se_hdr;
|
||||
static uint32_t *crus_se_get_buffer;
|
||||
static atomic_t crus_se_get_param_flag;
|
||||
struct mutex crus_se_get_param_lock;
|
||||
struct mutex crus_se_lock;
|
||||
static int cirrus_se_en;
|
||||
static int cirrus_se_usecase;
|
||||
static int cirrus_ff_load_conf_sel;
|
||||
static int cirrus_ff_delta_sel;
|
||||
static int cirrus_ff_chan_swap_sel;
|
||||
static int cirrus_ff_chan_swap_dur;
|
||||
static int cirrus_ff_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX;
|
||||
|
||||
static int crus_se_usecase_dt_count;
|
||||
static const char *crus_se_usecase_dt_text[MAX_TUNING_CONFIGS];
|
||||
|
||||
int crus_afe_callback(void *payload, int size);
|
||||
|
||||
static void *crus_gen_afe_get_header(int length, int port, int module,
|
||||
int param)
|
||||
{
|
||||
struct afe_custom_crus_get_config_t *config = NULL;
|
||||
int size = sizeof(struct afe_custom_crus_get_config_t);
|
||||
int index = afe_get_port_index(port);
|
||||
uint16_t payload_size = sizeof(struct afe_port_param_data_v2) +
|
||||
length;
|
||||
|
||||
/* Allocate memory for the message */
|
||||
config = kzalloc(size, GFP_KERNEL);
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
/* Set header section */
|
||||
config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
|
||||
config->hdr.pkt_size = size;
|
||||
config->hdr.src_svc = APR_SVC_AFE;
|
||||
config->hdr.src_domain = APR_DOMAIN_APPS;
|
||||
config->hdr.src_port = 0;
|
||||
config->hdr.dest_svc = APR_SVC_AFE;
|
||||
config->hdr.dest_domain = APR_DOMAIN_ADSP;
|
||||
config->hdr.dest_port = 0;
|
||||
config->hdr.token = index;
|
||||
config->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2;
|
||||
|
||||
/* Set param section */
|
||||
config->param.port_id = (uint16_t) port;
|
||||
config->param.payload_address_lsw = 0;
|
||||
config->param.payload_address_msw = 0;
|
||||
config->param.mem_map_handle = 0;
|
||||
config->param.module_id = (uint32_t) module;
|
||||
config->param.param_id = (uint32_t) param;
|
||||
/* max data size of the param_ID/module_ID combination */
|
||||
config->param.payload_size = payload_size;
|
||||
|
||||
/* Set data section */
|
||||
config->data.module_id = (uint32_t) module;
|
||||
config->data.param_id = (uint32_t) param;
|
||||
config->data.reserved = 0; /* Must be set to 0 */
|
||||
/* actual size of the data for the module_ID/param_ID pair */
|
||||
config->data.param_size = length;
|
||||
|
||||
return (void *)config;
|
||||
}
|
||||
|
||||
static void *crus_gen_afe_set_header(int length, int port, int module,
|
||||
int param)
|
||||
{
|
||||
struct afe_custom_crus_set_config_t *config = NULL;
|
||||
int size = sizeof(struct afe_custom_crus_set_config_t) + length;
|
||||
int index = afe_get_port_index(port);
|
||||
uint16_t payload_size = sizeof(struct afe_port_param_data_v2) +
|
||||
length;
|
||||
|
||||
/* Allocate memory for the message */
|
||||
config = kzalloc(size, GFP_KERNEL);
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
/* Set header section */
|
||||
config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
|
||||
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
|
||||
config->hdr.pkt_size = size;
|
||||
config->hdr.src_svc = APR_SVC_AFE;
|
||||
config->hdr.src_domain = APR_DOMAIN_APPS;
|
||||
config->hdr.src_port = 0;
|
||||
config->hdr.dest_svc = APR_SVC_AFE;
|
||||
config->hdr.dest_domain = APR_DOMAIN_ADSP;
|
||||
config->hdr.dest_port = 0;
|
||||
config->hdr.token = index;
|
||||
config->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
|
||||
|
||||
/* Set param section */
|
||||
config->param.port_id = (uint16_t) port;
|
||||
config->param.payload_address_lsw = 0;
|
||||
config->param.payload_address_msw = 0;
|
||||
config->param.mem_map_handle = 0;
|
||||
/* max data size of the param_ID/module_ID combination */
|
||||
config->param.payload_size = payload_size;
|
||||
|
||||
/* Set data section */
|
||||
config->data.module_id = (uint32_t) module;
|
||||
config->data.param_id = (uint32_t) param;
|
||||
config->data.reserved = 0; /* Must be set to 0 */
|
||||
/* actual size of the data for the module_ID/param_ID pair */
|
||||
config->data.param_size = length;
|
||||
|
||||
return (void *)config;
|
||||
}
|
||||
|
||||
static int crus_get_param(int port, int module, int param, int length,
|
||||
void *data)
|
||||
{
|
||||
struct afe_custom_crus_get_config_t *config = NULL;
|
||||
int index = afe_get_port_index(port);
|
||||
int ret = 0, count = 0;
|
||||
|
||||
pr_debug("%s: port = %d module = %d param = 0x%x length = %d\n",
|
||||
__func__, port, module, param, length);
|
||||
|
||||
config = (struct afe_custom_crus_get_config_t *)
|
||||
crus_gen_afe_get_header(length, port, module, param);
|
||||
if (config == NULL) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_debug("%s: Preparing to send apr packet\n", __func__);
|
||||
|
||||
mutex_lock(&crus_se_get_param_lock);
|
||||
atomic_set(&crus_se_get_param_flag, 0);
|
||||
|
||||
crus_se_get_buffer = kzalloc(length + 16, GFP_KERNEL);
|
||||
if (!crus_se_get_buffer) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
mutex_unlock(&crus_se_get_param_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
crus_afe_set_callback(crus_afe_callback);
|
||||
|
||||
ret = afe_apr_send_pkt_crus(config, index);
|
||||
if (ret)
|
||||
pr_err("%s: crus get_param for port %d failed with code %d\n",
|
||||
__func__, port, ret);
|
||||
else
|
||||
pr_debug("%s: crus get_param sent packet with param id 0x%08x to module 0x%08x.\n",
|
||||
__func__, param, module);
|
||||
|
||||
/* Wait for afe callback to populate data */
|
||||
while (!atomic_read(&crus_se_get_param_flag)) {
|
||||
usleep_range(800, 1200);
|
||||
if (count++ >= 1000) {
|
||||
pr_err("%s: AFE callback timeout\n", __func__);
|
||||
atomic_set(&crus_se_get_param_flag, 1);
|
||||
mutex_unlock(&crus_se_get_param_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy from dynamic buffer to return buffer */
|
||||
memcpy((u8 *)data, &crus_se_get_buffer[4], length);
|
||||
|
||||
kfree(crus_se_get_buffer);
|
||||
|
||||
mutex_unlock(&crus_se_get_param_lock);
|
||||
|
||||
kfree(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int crus_set_param(int port, int module, int param, int length,
|
||||
void *data_ptr)
|
||||
{
|
||||
struct afe_custom_crus_set_config_t *config = NULL;
|
||||
int index = afe_get_port_index(port);
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: port = %d module = %d param = 0x%x length = %d\n",
|
||||
__func__, port, module, param, length);
|
||||
|
||||
config = crus_gen_afe_set_header(length, port, module, param);
|
||||
if (config == NULL) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy((u8 *)config + sizeof(struct afe_custom_crus_set_config_t),
|
||||
(u8 *) data_ptr, length);
|
||||
|
||||
pr_debug("%s: Preparing to send apr packet.\n", __func__);
|
||||
|
||||
ret = afe_apr_send_pkt_crus(config, index);
|
||||
if (ret) {
|
||||
pr_err("%s: crus set_param for port %d failed with code %d\n",
|
||||
__func__, port, ret);
|
||||
} else {
|
||||
pr_debug("%s: crus set_param sent packet with param id 0x%08x to module 0x%08x.\n",
|
||||
__func__, param, module);
|
||||
}
|
||||
|
||||
kfree(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int crus_send_config(const char *data, int32_t length,
|
||||
int32_t port, int32_t module)
|
||||
{
|
||||
struct afe_custom_crus_set_config_t *config = NULL;
|
||||
struct crus_external_config_t *payload = NULL;
|
||||
int size = sizeof(struct crus_external_config_t);
|
||||
int ret = 0;
|
||||
int index = afe_get_port_index(port);
|
||||
uint32_t param = 0;
|
||||
int mem_size = 0;
|
||||
int sent = 0;
|
||||
int chars_to_send = 0;
|
||||
|
||||
pr_debug("%s: called with module_id = %x, string length = %d\n",
|
||||
__func__, module, length);
|
||||
|
||||
/* Destination settings for message */
|
||||
if (port == cirrus_ff_port)
|
||||
param = CRUS_PARAM_RX_SET_EXT_CONFIG;
|
||||
else {
|
||||
pr_err("%s: Received invalid port parameter %d\n",
|
||||
__func__, module);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (length > APR_CHUNK_SIZE)
|
||||
mem_size = APR_CHUNK_SIZE;
|
||||
else
|
||||
mem_size = length;
|
||||
|
||||
config = crus_gen_afe_set_header(size, port, module, param);
|
||||
if (config == NULL) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
payload = (struct crus_external_config_t *)((u8 *)config +
|
||||
sizeof(struct afe_custom_crus_set_config_t));
|
||||
payload->total_size = (uint32_t)length;
|
||||
payload->reserved = 0;
|
||||
payload->config = PAYLOAD_FOLLOWS_CONFIG;
|
||||
/* ^ This tells the algorithm to expect array */
|
||||
/* immediately following the header */
|
||||
|
||||
/* Send config string in chunks of APR_CHUNK_SIZE bytes */
|
||||
while (sent < length) {
|
||||
chars_to_send = length - sent;
|
||||
if (chars_to_send > APR_CHUNK_SIZE) {
|
||||
chars_to_send = APR_CHUNK_SIZE;
|
||||
payload->done = 0;
|
||||
} else {
|
||||
payload->done = 1;
|
||||
}
|
||||
|
||||
/* Configure per message parameter settings */
|
||||
memcpy(payload->data, data + sent, chars_to_send);
|
||||
payload->chunk_size = chars_to_send;
|
||||
|
||||
|
||||
/* Send the actual message */
|
||||
pr_debug("%s: Preparing to send apr packet.\n", __func__);
|
||||
ret = afe_apr_send_pkt_crus(config, index);
|
||||
if (ret)
|
||||
pr_err("%s: crus set_param for port %d failed with code %d\n",
|
||||
__func__, port, ret);
|
||||
else
|
||||
pr_debug("%s: crus set_param sent packet with param id 0x%08x to module 0x%08x.\n",
|
||||
__func__, param, module);
|
||||
|
||||
sent += chars_to_send;
|
||||
}
|
||||
|
||||
kfree(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int crus_send_delta(const char *data, uint32_t length)
|
||||
{
|
||||
struct afe_custom_crus_set_config_t *config = NULL;
|
||||
struct crus_delta_config_t *payload = NULL;
|
||||
int size = sizeof(struct crus_delta_config_t);
|
||||
int port = cirrus_ff_port;
|
||||
int param = CRUS_PARAM_RX_SET_DELTA_CONFIG;
|
||||
int module = CIRRUS_SE;
|
||||
int ret = 0;
|
||||
int index = afe_get_port_index(port);
|
||||
int mem_size = 0;
|
||||
int sent = 0;
|
||||
int chars_to_send = 0;
|
||||
|
||||
pr_debug("%s: called with module_id = %x, string length = %d\n",
|
||||
__func__, module, length);
|
||||
|
||||
if (length > APR_CHUNK_SIZE)
|
||||
mem_size = APR_CHUNK_SIZE;
|
||||
else
|
||||
mem_size = length;
|
||||
|
||||
config = crus_gen_afe_set_header(size, port, module, param);
|
||||
if (config == NULL) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
payload = (struct crus_delta_config_t *)((u8 *)config +
|
||||
sizeof(struct afe_custom_crus_set_config_t));
|
||||
payload->total_size = length;
|
||||
payload->index = 0;
|
||||
payload->reserved = 0;
|
||||
payload->config = PAYLOAD_FOLLOWS_CONFIG;
|
||||
/* ^ This tells the algorithm to expect array */
|
||||
/* immediately following the header */
|
||||
|
||||
/* Send config string in chunks of APR_CHUNK_SIZE bytes */
|
||||
while (sent < length) {
|
||||
chars_to_send = length - sent;
|
||||
if (chars_to_send > APR_CHUNK_SIZE) {
|
||||
chars_to_send = APR_CHUNK_SIZE;
|
||||
payload->done = 0;
|
||||
} else {
|
||||
payload->done = 1;
|
||||
}
|
||||
|
||||
/* Configure per message parameter settings */
|
||||
memcpy(payload->data, data + sent, chars_to_send);
|
||||
payload->chunk_size = chars_to_send;
|
||||
|
||||
/* Send the actual message */
|
||||
pr_debug("%s: Preparing to send apr packet.\n", __func__);
|
||||
ret = afe_apr_send_pkt_crus(config, index);
|
||||
if (ret)
|
||||
pr_err("%s: crus set_param for port %d failed with code %d\n",
|
||||
__func__, port, ret);
|
||||
else
|
||||
pr_debug("%s: crus set_param sent packet with param id 0x%08x to module 0x%08x.\n",
|
||||
__func__, param, module);
|
||||
|
||||
sent += chars_to_send;
|
||||
}
|
||||
|
||||
kfree(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int crus_afe_callback(void *payload, int size)
|
||||
{
|
||||
uint32_t *payload32 = payload;
|
||||
|
||||
pr_debug("Cirrus AFE CALLBACK: size = %d\n", size);
|
||||
|
||||
switch (payload32[1]) {
|
||||
case CIRRUS_SE:
|
||||
memcpy(crus_se_get_buffer, payload32, size);
|
||||
atomic_set(&crus_se_get_param_flag, 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_se_enable(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
static struct crus_single_data_t crus_id_ctrl;
|
||||
const int crus_set = ucontrol->value.integer.value[0];
|
||||
|
||||
if (crus_set > 255) {
|
||||
pr_err("Cirrus SE Enable: Invalid entry; Enter 0 to DISABLE, 1 to ENABLE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (crus_set) {
|
||||
case 0: /* "Config SE Disable" */
|
||||
pr_debug("Cirrus SE Enable: Config DISABLE\n");
|
||||
crus_enable.value = 0;
|
||||
cirrus_se_en = 0;
|
||||
break;
|
||||
case 1: /* "Config SE Enable" */
|
||||
pr_debug("Cirrus SE Enable: Config ENABLE\n");
|
||||
crus_enable.value = 1;
|
||||
cirrus_se_en = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&crus_se_lock);
|
||||
crus_set_param(cirrus_ff_port, CIRRUS_SE,
|
||||
CIRRUS_SE_ENABLE,
|
||||
sizeof(struct crus_single_data_t),
|
||||
(void *)&crus_enable);
|
||||
|
||||
crus_id_ctrl.value = 0; /*tx=1, rx=0*/
|
||||
crus_set_param(cirrus_ff_port, CIRRUS_SE,
|
||||
CIRRUS_SE_PARAM_ID_CTRL,
|
||||
sizeof(struct crus_single_data_t),
|
||||
(void *)&crus_id_ctrl);
|
||||
mutex_unlock(&crus_se_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_se_enable_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("Starting Cirrus SE Enable Get function call\n");
|
||||
|
||||
crus_get_param(cirrus_ff_port, CIRRUS_SE, CIRRUS_SE_ENABLE,
|
||||
sizeof(struct crus_single_data_t), (void *)&crus_enable);
|
||||
|
||||
ucontrol->value.integer.value[0] = crus_enable.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_se_usecase(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct crus_rx_run_case_ctrl_t case_ctrl;
|
||||
const int crus_set = ucontrol->value.integer.value[0];
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
uint32_t max_index = e->items;
|
||||
|
||||
pr_debug("Starting Cirrus SE Config function call %d\n", crus_set);
|
||||
|
||||
if (crus_set >= max_index) {
|
||||
pr_err("Cirrus SE Config index out of bounds (%d)\n", crus_set);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cirrus_se_usecase = crus_set;
|
||||
|
||||
case_ctrl.status_l = 0;
|
||||
case_ctrl.status_r = 0;
|
||||
case_ctrl.atemp = 0;
|
||||
case_ctrl.value = cirrus_se_usecase;
|
||||
|
||||
crus_set_param(cirrus_ff_port, CIRRUS_SE,
|
||||
CRUS_PARAM_RX_SET_USECASE, sizeof(case_ctrl),
|
||||
(void *)&case_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_se_usecase_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
static struct crus_single_data_t crus_usecase;
|
||||
|
||||
pr_debug("Starting Cirrus SE Usecase Get function call\n");
|
||||
|
||||
crus_get_param(cirrus_ff_port, CIRRUS_SE, CRUS_PARAM_RX_GET_USECASE,
|
||||
sizeof(struct crus_single_data_t), (void *)&crus_usecase);
|
||||
|
||||
ucontrol->value.integer.value[0] = crus_usecase.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_load_config(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
const int crus_set = ucontrol->value.integer.value[0];
|
||||
char config[CONFIG_FILE_SIZE];
|
||||
const struct firmware *firmware;
|
||||
|
||||
pr_debug("Starting Cirrus SE Load Config function call %d\n", crus_set);
|
||||
|
||||
switch (crus_set) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: /* Load RX Config */
|
||||
cirrus_ff_load_conf_sel = crus_set;
|
||||
snprintf(config, CONFIG_FILE_SIZE, CRUS_RX_CONFIG,
|
||||
cirrus_se_usecase);
|
||||
|
||||
if (request_firmware(&firmware, config, crus_se_device) != 0) {
|
||||
pr_err("%s: Request firmware failed\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (firmware->data != NULL && firmware->size > 0) {
|
||||
pr_debug("%s: Sending RX config(%d)\n",
|
||||
__func__, cirrus_se_usecase);
|
||||
crus_send_config(firmware->data, firmware->size,
|
||||
cirrus_ff_port, CIRRUS_SE);
|
||||
} else {
|
||||
pr_err("%s: can't read firmware data\n", __func__);
|
||||
}
|
||||
|
||||
release_firmware(firmware);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cirrus_ff_load_conf_sel = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_load_config_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("Starting Cirrus SE Load Config Get function call\n");
|
||||
ucontrol->value.integer.value[0] = cirrus_ff_load_conf_sel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_delta_config(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct crus_single_data_t data;
|
||||
const int crus_set = ucontrol->value.integer.value[0];
|
||||
const struct firmware *firmware;
|
||||
|
||||
pr_debug("Starting Cirrus SE Delta Config function call %d\n",
|
||||
crus_set);
|
||||
|
||||
switch (crus_set) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: /* Load delta config over AFE */
|
||||
cirrus_ff_delta_sel = crus_set;
|
||||
|
||||
if (request_firmware(&firmware, "crus_se_delta_config.bin",
|
||||
crus_se_device) != 0) {
|
||||
pr_err("%s: Request firmware failed\n", __func__);
|
||||
cirrus_ff_delta_sel = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("%s: Sending delta config\n", __func__);
|
||||
crus_send_delta(firmware->data, firmware->size);
|
||||
release_firmware(firmware);
|
||||
break;
|
||||
case 2: /* Run delta transition */
|
||||
cirrus_ff_delta_sel = crus_set;
|
||||
data.value = 0;
|
||||
crus_set_param(cirrus_ff_port, CIRRUS_SE,
|
||||
CRUS_PARAM_RX_RUN_DELTA_CONFIG,
|
||||
sizeof(struct crus_single_data_t), &data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cirrus_ff_delta_sel = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_delta_config_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("Starting Cirrus SE Delta Config Get function call\n");
|
||||
ucontrol->value.integer.value[0] = cirrus_ff_delta_sel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_chan_swap(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct crus_dual_data_t data;
|
||||
const int crus_set = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("Starting Cirrus SE Channel Swap function call %d\n",
|
||||
crus_set);
|
||||
|
||||
switch (crus_set) {
|
||||
case 0: /* L/R */
|
||||
data.data1 = 1;
|
||||
break;
|
||||
case 1: /* R/L */
|
||||
data.data1 = 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data.data2 = cirrus_ff_chan_swap_dur;
|
||||
|
||||
crus_set_param(cirrus_ff_port, CIRRUS_SE,
|
||||
CRUS_PARAM_RX_CHANNEL_SWAP,
|
||||
sizeof(struct crus_dual_data_t), &data);
|
||||
|
||||
cirrus_ff_chan_swap_sel = crus_set;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_chan_swap_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("Starting Cirrus SE Channel Swap Get function call\n");
|
||||
ucontrol->value.integer.value[0] = cirrus_ff_chan_swap_sel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_chan_swap_dur(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int crus_set = ucontrol->value.integer.value[0];
|
||||
|
||||
pr_debug("Starting Cirrus SE Channel Swap Duration function call\n");
|
||||
|
||||
if ((crus_set < 0) || (crus_set > MAX_CHAN_SWAP_SAMPLES)) {
|
||||
pr_err("%s: Value out of range (%d)\n", __func__, crus_set);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (crus_set < MIN_CHAN_SWAP_SAMPLES) {
|
||||
pr_debug("%s: Received %d, rounding up to min value %d\n",
|
||||
__func__, crus_set, MIN_CHAN_SWAP_SAMPLES);
|
||||
crus_set = MIN_CHAN_SWAP_SAMPLES;
|
||||
}
|
||||
|
||||
cirrus_ff_chan_swap_dur = crus_set;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_crus_chan_swap_dur_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
pr_debug("Starting Cirrus SE Channel Swap Duration Get function call\n");
|
||||
ucontrol->value.integer.value[0] = cirrus_ff_chan_swap_dur;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const crus_en_text[] = {"Disable", "Enable"};
|
||||
|
||||
static const char * const crus_conf_load_text[] = {"Idle", "Load RX"};
|
||||
|
||||
static const char * const crus_delta_text[] = {"Idle", "Load", "Run"};
|
||||
|
||||
static const char * const crus_chan_swap_text[] = {"LR", "RL"};
|
||||
|
||||
static const struct soc_enum crus_en_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, crus_en_text),
|
||||
};
|
||||
|
||||
static struct soc_enum crus_se_usecase_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(MAX_TUNING_CONFIGS, crus_se_usecase_dt_text),
|
||||
};
|
||||
|
||||
static const struct soc_enum crus_conf_load_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, crus_conf_load_text),
|
||||
};
|
||||
|
||||
static const struct soc_enum crus_delta_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(3, crus_delta_text),
|
||||
};
|
||||
|
||||
static const struct soc_enum crus_chan_swap_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, crus_chan_swap_text),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new crus_mixer_controls[] = {
|
||||
SOC_ENUM_EXT("Cirrus SE", crus_en_enum[0],
|
||||
msm_crus_se_enable_get, msm_crus_se_enable),
|
||||
SOC_ENUM_EXT("Cirrus SE Audio Mode", crus_se_usecase_enum[0],
|
||||
msm_crus_se_usecase_get, msm_crus_se_usecase),
|
||||
SOC_ENUM_EXT("Cirrus SE Load Config", crus_conf_load_enum[0],
|
||||
msm_crus_load_config_get, msm_crus_load_config),
|
||||
SOC_ENUM_EXT("Cirrus SE Delta Config", crus_delta_enum[0],
|
||||
msm_crus_delta_config_get, msm_crus_delta_config),
|
||||
SOC_ENUM_EXT("Cirrus SE Channel Swap", crus_chan_swap_enum[0],
|
||||
msm_crus_chan_swap_get, msm_crus_chan_swap),
|
||||
SOC_SINGLE_EXT("Cirrus SE Channel Swap Duration", SND_SOC_NOPM, 0,
|
||||
MAX_CHAN_SWAP_SAMPLES, 0, msm_crus_chan_swap_dur_get,
|
||||
msm_crus_chan_swap_dur),
|
||||
};
|
||||
|
||||
void msm_crus_pb_add_controls(struct snd_soc_platform *platform)
|
||||
{
|
||||
crus_se_device = platform->dev;
|
||||
|
||||
if (crus_se_device == NULL)
|
||||
pr_err("%s: platform->dev is NULL!\n", __func__);
|
||||
else
|
||||
pr_debug("%s: platform->dev = %lx\n", __func__,
|
||||
(unsigned long)crus_se_device);
|
||||
|
||||
if (crus_se_usecase_dt_count == 0)
|
||||
pr_err("%s: Missing usecase config selections\n", __func__);
|
||||
|
||||
crus_se_usecase_enum[0].items = crus_se_usecase_dt_count;
|
||||
crus_se_usecase_enum[0].texts = crus_se_usecase_dt_text;
|
||||
|
||||
snd_soc_add_platform_controls(platform, crus_mixer_controls,
|
||||
ARRAY_SIZE(crus_mixer_controls));
|
||||
}
|
||||
EXPORT_SYMBOL(msm_crus_pb_add_controls);
|
||||
|
||||
static long crus_se_shared_ioctl(struct file *f, unsigned int cmd,
|
||||
void __user *arg)
|
||||
{
|
||||
int result = 0, port;
|
||||
uint32_t bufsize = 0, size;
|
||||
void *io_data = NULL;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (copy_from_user(&size, arg, sizeof(size))) {
|
||||
pr_err("%s: copy_from_user (size) failed\n", __func__);
|
||||
result = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Copy IOCTL header from usermode */
|
||||
if (copy_from_user(&crus_se_hdr, arg, size)) {
|
||||
pr_err("%s: copy_from_user (struct) failed\n", __func__);
|
||||
result = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bufsize = crus_se_hdr.data_length;
|
||||
io_data = kzalloc(bufsize, GFP_KERNEL);
|
||||
if (!io_data) {
|
||||
pr_err("%s: Memory allocation failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CRUS_SE_IOCTL_GET:
|
||||
switch (crus_se_hdr.module_id) {
|
||||
case CRUS_MODULE_ID_RX:
|
||||
port = cirrus_ff_port;
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: Unrecognized port ID (%d)\n", __func__,
|
||||
crus_se_hdr.module_id);
|
||||
port = cirrus_ff_port;
|
||||
}
|
||||
|
||||
crus_get_param(port, CIRRUS_SE, crus_se_hdr.param_id,
|
||||
bufsize, io_data);
|
||||
|
||||
result = copy_to_user(crus_se_hdr.data, io_data, bufsize);
|
||||
if (result) {
|
||||
pr_err("%s: copy_to_user failed (%d)\n", __func__,
|
||||
result);
|
||||
result = -EFAULT;
|
||||
} else {
|
||||
result = bufsize;
|
||||
}
|
||||
break;
|
||||
case CRUS_SE_IOCTL_SET:
|
||||
result = copy_from_user(io_data, (void *)crus_se_hdr.data,
|
||||
bufsize);
|
||||
if (result) {
|
||||
pr_err("%s: copy_from_user failed (%d)\n", __func__,
|
||||
result);
|
||||
result = -EFAULT;
|
||||
goto exit_io;
|
||||
}
|
||||
|
||||
switch (crus_se_hdr.module_id) {
|
||||
case CRUS_MODULE_ID_RX:
|
||||
port = cirrus_ff_port;
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: Unrecognized port ID (%d)\n", __func__,
|
||||
crus_se_hdr.module_id);
|
||||
port = cirrus_ff_port;
|
||||
}
|
||||
|
||||
crus_set_param(port, CIRRUS_SE, crus_se_hdr.param_id,
|
||||
bufsize, io_data);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Invalid IOCTL, command = %d!\n", __func__, cmd);
|
||||
result = -EINVAL;
|
||||
}
|
||||
|
||||
exit_io:
|
||||
kfree(io_data);
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static long crus_se_ioctl(struct file *f,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
return crus_se_shared_ioctl(f, cmd, (void __user *)arg);
|
||||
}
|
||||
|
||||
static long crus_se_compat_ioctl(struct file *f,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int cmd64;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
switch (cmd) {
|
||||
case CRUS_SE_IOCTL_GET32:
|
||||
cmd64 = CRUS_SE_IOCTL_GET;
|
||||
break;
|
||||
case CRUS_SE_IOCTL_SET32:
|
||||
cmd64 = CRUS_SE_IOCTL_SET;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Invalid IOCTL, command = %d!\n", __func__, cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return crus_se_shared_ioctl(f, cmd64, compat_ptr(arg));
|
||||
}
|
||||
|
||||
static int crus_se_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
atomic_inc(&crus_se_misc_usage_count);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int crus_se_release(struct inode *inode, struct file *f)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
atomic_dec(&crus_se_misc_usage_count);
|
||||
pr_debug("%s: ref count %d!\n", __func__,
|
||||
atomic_read(&crus_se_misc_usage_count));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int msm_cirrus_playback_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i, rc = 0;
|
||||
u32 ff_port_id;
|
||||
|
||||
pr_info("CRUS_SE: initializing platform device\n");
|
||||
|
||||
crus_se_usecase_dt_count = of_property_count_strings(pdev->dev.of_node,
|
||||
"usecase-names");
|
||||
if (crus_se_usecase_dt_count <= 0) {
|
||||
dev_dbg(&pdev->dev, "Usecase names not found\n");
|
||||
crus_se_usecase_dt_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((crus_se_usecase_dt_count > 0) &&
|
||||
(crus_se_usecase_dt_count <= MAX_TUNING_CONFIGS))
|
||||
of_property_read_string_array(pdev->dev.of_node,
|
||||
"usecase-names",
|
||||
crus_se_usecase_dt_text,
|
||||
crus_se_usecase_dt_count);
|
||||
else if (crus_se_usecase_dt_count > MAX_TUNING_CONFIGS) {
|
||||
dev_err(&pdev->dev, "Max of %d usecase configs allowed\n",
|
||||
MAX_TUNING_CONFIGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < crus_se_usecase_dt_count; i++)
|
||||
pr_info("CRUS_SE: usecase[%d] = %s\n", i,
|
||||
crus_se_usecase_dt_text[i]);
|
||||
|
||||
rc = of_property_read_u32_index(pdev->dev.of_node,
|
||||
"afe-port-id", 0, &ff_port_id);
|
||||
if (rc >= 0) {
|
||||
cirrus_ff_port = ff_port_id;
|
||||
pr_info("CRUS_SE: cirrus_ff_port = 0x%x", cirrus_ff_port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id msm_cirrus_playback_dt_match[] = {
|
||||
{.compatible = "cirrus,msm-cirrus-playback"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, msm_cirrus_playback_dt_match);
|
||||
|
||||
static struct platform_driver msm_cirrus_playback_driver = {
|
||||
.driver = {
|
||||
.name = "msm-cirrus-playback",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = msm_cirrus_playback_dt_match,
|
||||
},
|
||||
.probe = msm_cirrus_playback_probe,
|
||||
};
|
||||
|
||||
static const struct file_operations crus_se_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = crus_se_open,
|
||||
.release = crus_se_release,
|
||||
.unlocked_ioctl = crus_se_ioctl,
|
||||
.compat_ioctl = crus_se_compat_ioctl,
|
||||
};
|
||||
|
||||
struct miscdevice crus_se_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "msm_cirrus_playback",
|
||||
.fops = &crus_se_fops,
|
||||
};
|
||||
|
||||
static int __init crus_se_init(void)
|
||||
{
|
||||
pr_info("CRUS_SE_INIT: initializing misc device\n");
|
||||
atomic_set(&crus_se_get_param_flag, 0);
|
||||
atomic_set(&crus_se_misc_usage_count, 0);
|
||||
mutex_init(&crus_se_get_param_lock);
|
||||
mutex_init(&crus_se_lock);
|
||||
|
||||
misc_register(&crus_se_misc);
|
||||
|
||||
return platform_driver_register(&msm_cirrus_playback_driver);
|
||||
}
|
||||
module_init(crus_se_init);
|
||||
|
||||
static void __exit crus_se_exit(void)
|
||||
{
|
||||
mutex_destroy(&crus_se_get_param_lock);
|
||||
mutex_destroy(&crus_se_lock);
|
||||
|
||||
platform_driver_unregister(&msm_cirrus_playback_driver);
|
||||
}
|
||||
module_exit(crus_se_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC cirrus-playback driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in a new issue