| // 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"); |