blob: b09506a05f18e26dc14c88027fedcbe53359e241 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2014, 2017-2018, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/miscdevice.h>
#include <sound/audio_slimslave.h>
#include <linux/slimbus/slimbus.h>
#include <linux/pm_runtime.h>
static struct slim_device *slim;
static int vote_count;
struct mutex suspend_lock;
bool suspend;
static int audio_slim_open(struct inode *inode, struct file *file)
{
pr_debug("%s:\n", __func__);
if (vote_count) {
pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
pm_runtime_mark_last_busy(slim->dev.parent);
pm_runtime_put(slim->dev.parent);
vote_count--;
}
return 0;
};
static int audio_slim_release(struct inode *inode, struct file *file)
{
pr_debug("%s:\n", __func__);
if (vote_count) {
pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
pm_runtime_mark_last_busy(slim->dev.parent);
pm_runtime_put(slim->dev.parent);
vote_count--;
} else {
pr_debug("%s: vote: vote_count=%d\n", __func__, vote_count);
pm_runtime_get_sync(slim->dev.parent);
vote_count++;
}
return 0;
};
static long audio_slim_ioctl(struct file *file, unsigned int cmd,
unsigned long u_arg)
{
switch (cmd) {
case AUDIO_SLIMSLAVE_VOTE:
mutex_lock(&suspend_lock);
if (!vote_count && !suspend) {
pr_debug("%s:AUDIO_SLIMSLAVE_VOTE\n", __func__);
pm_runtime_get_sync(slim->dev.parent);
vote_count++;
} else {
pr_err("%s:Invalid vote: vote_count=%d suspend=%d\n",
__func__, vote_count, suspend);
}
mutex_unlock(&suspend_lock);
break;
case AUDIO_SLIMSLAVE_UNVOTE:
mutex_lock(&suspend_lock);
if (vote_count && !suspend) {
pr_debug("%s:AUDIO_SLIMSLAVE_UNVOTE\n", __func__);
pm_runtime_mark_last_busy(slim->dev.parent);
pm_runtime_put(slim->dev.parent);
vote_count--;
} else {
pr_err("%s:Invalid unvote: vote_count=%d suspend=%d\n",
__func__, vote_count, suspend);
}
mutex_unlock(&suspend_lock);
break;
default:
pr_debug("%s: Invalid ioctl cmd: %d\n", __func__, cmd);
break;
}
return 0;
}
static const struct file_operations audio_slimslave_fops = {
.open = audio_slim_open,
.unlocked_ioctl = audio_slim_ioctl,
.release = audio_slim_release,
};
struct miscdevice audio_slimslave_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = AUDIO_SLIMSLAVE_IOCTL_NAME,
.fops = &audio_slimslave_fops,
};
static int audio_slimslave_probe(struct slim_device *audio_slim)
{
pr_debug("%s:\n", __func__);
mutex_init(&suspend_lock);
suspend = false;
slim = audio_slim;
misc_register(&audio_slimslave_misc);
return 0;
}
static int audio_slimslave_remove(struct slim_device *audio_slim)
{
pr_debug("%s:\n", __func__);
misc_deregister(&audio_slimslave_misc);
return 0;
}
static int audio_slimslave_resume(struct slim_device *audio_slim)
{
pr_debug("%s:\n", __func__);
mutex_lock(&suspend_lock);
suspend = false;
mutex_unlock(&suspend_lock);
return 0;
}
static int audio_slimslave_suspend(struct slim_device *audio_slim,
pm_message_t pmesg)
{
pr_debug("%s:\n", __func__);
mutex_lock(&suspend_lock);
suspend = true;
mutex_unlock(&suspend_lock);
return 0;
}
static const struct slim_device_id audio_slimslave_dt_match[] = {
{"audio-slimslave", 0},
{}
};
static struct slim_driver audio_slimslave_driver = {
.driver = {
.name = "audio-slimslave",
.owner = THIS_MODULE,
},
.probe = audio_slimslave_probe,
.remove = audio_slimslave_remove,
.id_table = audio_slimslave_dt_match,
.resume = audio_slimslave_resume,
.suspend = audio_slimslave_suspend,
};
int __init audio_slimslave_init(void)
{
return slim_driver_register(&audio_slimslave_driver);
}
void audio_slimslave_exit(void)
{
slim_driver_unregister(&audio_slimslave_driver);
}
/* Module information */
MODULE_DESCRIPTION("Audio side Slimbus slave driver");
MODULE_LICENSE("GPL v2");