223 lines
5.9 KiB
C
223 lines
5.9 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
//
|
||
|
// adsp_service.c -- Mediatek ADSP service for ioctrl handling
|
||
|
//
|
||
|
// Copyright (c) 2018 MediaTek Inc.
|
||
|
// Author: HsinYi Chang <hsin-yi.chang@mediatek.com>
|
||
|
|
||
|
|
||
|
#include <linux/module.h> /* needed by all modules */
|
||
|
#include <linux/init.h> /* needed by module macros */
|
||
|
#include <linux/fs.h> /* needed by file_operations* */
|
||
|
#include <linux/miscdevice.h> /* needed by miscdevice* */
|
||
|
#include <linux/sysfs.h>
|
||
|
#include <linux/device.h> /* needed by device_* */
|
||
|
#include <linux/vmalloc.h> /* needed by kmalloc */
|
||
|
#include <linux/uaccess.h> /* needed by copy_to_user */
|
||
|
#include <linux/slab.h> /* needed by kmalloc */
|
||
|
#include <linux/poll.h> /* needed by poll */
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/suspend.h>
|
||
|
#include <linux/timer.h>
|
||
|
#include <linux/notifier.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_address.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/of_fdt.h>
|
||
|
#include <linux/ioport.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <adsp_ipi.h>
|
||
|
#include <adsp_helper.h>
|
||
|
#include <adsp_dvfs.h>
|
||
|
|
||
|
/*==============================================================================
|
||
|
* ioctl
|
||
|
*==============================================================================
|
||
|
*/
|
||
|
#define AUDIO_DSP_DEVICE_PATH "/dev/adsp"
|
||
|
#define AUDIO_DSP_IOC_MAGIC 'a'
|
||
|
|
||
|
#define AUDIO_DSP_IOCTL_ADSP_REG_FEATURE \
|
||
|
_IOW(AUDIO_DSP_IOC_MAGIC, 0, unsigned int)
|
||
|
#define AUDIO_DSP_IOCTL_ADSP_QUERY_STATUS \
|
||
|
_IOR(AUDIO_DSP_IOC_MAGIC, 1, unsigned int)
|
||
|
#define AUDIO_DSP_IOCTL_ADSP_RESET_CBK \
|
||
|
_IOR(AUDIO_DSP_IOC_MAGIC, 2, unsigned int)
|
||
|
|
||
|
/*
|
||
|
* =============================================================================
|
||
|
* struct
|
||
|
* =============================================================================
|
||
|
*/
|
||
|
|
||
|
struct audio_dsp_reg_feature_t {
|
||
|
uint16_t reg_flag;
|
||
|
uint16_t feature_id;
|
||
|
};
|
||
|
|
||
|
struct audio_dsp_query_status_t {
|
||
|
int16_t ready_flag;
|
||
|
uint16_t core_id;
|
||
|
};
|
||
|
/*
|
||
|
* =============================================================================
|
||
|
* static var
|
||
|
* =============================================================================
|
||
|
*/
|
||
|
static long status_update_flag;
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(status_wq);
|
||
|
static unsigned long last_dsp_status;
|
||
|
static int16_t last_ready_flag;
|
||
|
static int adsp_hal_feature_table[ADSP_NUM_FEATURE_ID];
|
||
|
|
||
|
|
||
|
/*==============================================================================
|
||
|
* functions - implementation
|
||
|
*==============================================================================
|
||
|
*/
|
||
|
void adsp_read_status_release(const unsigned long dsp_event)
|
||
|
{
|
||
|
last_dsp_status = dsp_event;
|
||
|
if (status_update_flag == 0) {
|
||
|
status_update_flag = 1;
|
||
|
pr_info("wake up event: %lu\n", dsp_event);
|
||
|
wake_up_interruptible(&status_wq);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int adsp_read_status_blocked(void)
|
||
|
{
|
||
|
int status = 0;
|
||
|
int retval = 0;
|
||
|
|
||
|
retval = wait_event_interruptible(status_wq,
|
||
|
(status_update_flag > 0));
|
||
|
if (retval == -ERESTARTSYS) {
|
||
|
pr_info("query adsp status -ERESTARTSYS");
|
||
|
status = -EINTR;
|
||
|
} else if (retval == 0) {
|
||
|
status = last_dsp_status;
|
||
|
status_update_flag = 0;
|
||
|
pr_info("query adsp status wakeup %d\n", status);
|
||
|
} else
|
||
|
status = -1;
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_COMPAT
|
||
|
long adsp_driver_compat_ioctl(
|
||
|
struct file *file, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
if (!file->f_op || !file->f_op->unlocked_ioctl) {
|
||
|
pr_notice("op null\n");
|
||
|
return -ENOTTY;
|
||
|
}
|
||
|
return file->f_op->unlocked_ioctl(file, cmd, arg);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void reset_hal_feature_table(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < ADSP_NUM_FEATURE_ID; i++) {
|
||
|
while (adsp_hal_feature_table[i] > 0) {
|
||
|
adsp_deregister_feature(i);
|
||
|
adsp_hal_feature_table[i]--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
long adsp_driver_ioctl(
|
||
|
struct file *file, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
|
||
|
struct audio_dsp_reg_feature_t feat_reg;
|
||
|
struct audio_dsp_query_status_t adsp_status;
|
||
|
|
||
|
int retval = 0;
|
||
|
unsigned int magic[2];
|
||
|
|
||
|
switch (cmd) {
|
||
|
case AUDIO_DSP_IOCTL_ADSP_REG_FEATURE: {
|
||
|
if (((void __user *)arg) == NULL) {
|
||
|
retval = -1;
|
||
|
break;
|
||
|
}
|
||
|
retval = copy_from_user(
|
||
|
&feat_reg,
|
||
|
(void __user *)arg,
|
||
|
sizeof(struct audio_dsp_reg_feature_t));
|
||
|
if (retval != 0) {
|
||
|
pr_notice("%s(), feature reg copy_from_user retval %d\n",
|
||
|
__func__, retval);
|
||
|
break;
|
||
|
}
|
||
|
if (feat_reg.feature_id >= ADSP_NUM_FEATURE_ID) {
|
||
|
retval = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
if (feat_reg.reg_flag)
|
||
|
retval = adsp_register_feature(feat_reg.feature_id);
|
||
|
else
|
||
|
retval = adsp_deregister_feature(feat_reg.feature_id);
|
||
|
|
||
|
if (retval == 0) {
|
||
|
if (feat_reg.reg_flag)
|
||
|
adsp_hal_feature_table[feat_reg.feature_id]++;
|
||
|
else
|
||
|
adsp_hal_feature_table[feat_reg.feature_id]--;
|
||
|
|
||
|
if (adsp_hal_feature_table[feat_reg.feature_id] < 0)
|
||
|
adsp_hal_feature_table[feat_reg.feature_id] = 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case AUDIO_DSP_IOCTL_ADSP_QUERY_STATUS: {
|
||
|
if (((void __user *)arg) == NULL) {
|
||
|
retval = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
retval = copy_from_user(
|
||
|
&adsp_status,
|
||
|
(void __user *)arg,
|
||
|
sizeof(struct audio_dsp_query_status_t));
|
||
|
if (retval)
|
||
|
pr_debug("%s copy_from_user fail line %d\n",
|
||
|
__func__, __LINE__);
|
||
|
|
||
|
adsp_status.ready_flag = is_adsp_ready(adsp_status.core_id);
|
||
|
|
||
|
retval = copy_to_user((void __user *)arg, &adsp_status,
|
||
|
sizeof(struct audio_dsp_reg_feature_t));
|
||
|
if (retval)
|
||
|
pr_debug("%s copy_to_user fail line %d\n",
|
||
|
__func__, __LINE__);
|
||
|
if (adsp_status.ready_flag != last_ready_flag)
|
||
|
pr_debug("%s(), AUDIO_DSP_IOCTL_ADSP_QUERY_STATUS(%d)\n",
|
||
|
__func__, adsp_status.ready_flag);
|
||
|
last_ready_flag = adsp_status.ready_flag;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case AUDIO_DSP_IOCTL_ADSP_RESET_CBK: {
|
||
|
if (copy_from_user(&magic, (void *)arg, sizeof(magic))) {
|
||
|
retval = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
if (magic[0] + magic[1] == 0xFFFFFFFF) {
|
||
|
retval = adsp_read_status_blocked();
|
||
|
pr_debug("%s(), AUDIO_DSP_IOCTL_ADSP_RESET_CBK(%d)\n",
|
||
|
__func__, retval);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|