blob: f8d6b01ffb6e7496b07e6fbb2dac5ddb6d7e2c64 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/export.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include <linux/jiffies.h>
#include "meson_ir_main.h"
static DEFINE_MUTEX(meson_ir_raw_handler_lock);
static LIST_HEAD(meson_ir_raw_handler_list);
static LIST_HEAD(meson_ir_raw_client_list);
#define MAX_REMOTE_EVENT_SIZE 512
static int meson_ir_raw_event_thread(void *data)
{
DEFINE_REMOTE_RAW_EVENT(ev);
struct meson_ir_raw_handler *handler;
struct meson_ir_raw_handle *raw = (struct meson_ir_raw_handle *)data;
int retval;
while (!kthread_should_stop()) {
/*spin_lock_irq(&raw->lock); */
retval = kfifo_len(&raw->kfifo);
if (retval < sizeof(ev)) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
/* spin_unlock_irq(&raw->lock); */
schedule();
continue;
}
retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
/*spin_unlock_irq(&raw->lock); */
mutex_lock(&meson_ir_raw_handler_lock);
list_for_each_entry(handler, &meson_ir_raw_handler_list, list)
if (raw->dev->rc_type == handler->protocols)
handler->decode(raw->dev, ev, handler->data);
mutex_unlock(&meson_ir_raw_handler_lock);
}
return 0;
}
int meson_ir_raw_event_store(struct meson_ir_dev *dev,
struct meson_ir_raw_event *ev)
{
if (!dev->raw)
return -EINVAL;
if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
return -ENOMEM;
return 0;
}
int meson_ir_raw_event_store_edge(struct meson_ir_dev *dev,
enum raw_event_type type, u32 duration)
{
DEFINE_REMOTE_RAW_EVENT(ev);
int rc = 0;
unsigned long timeout;
if (!dev->raw)
return -EINVAL;
timeout = dev->raw->jiffies_old +
msecs_to_jiffies(dev->raw->max_frame_time);
if (time_after(jiffies, timeout) || !dev->raw->last_type)
type |= RAW_START_EVENT;
else
ev.duration = duration;
if (type & RAW_START_EVENT)
ev.reset = true;
else if (dev->raw->last_type & RAW_SPACE)
ev.pulse = false;
else if (dev->raw->last_type & RAW_PULSE)
ev.pulse = true;
else
return 0;
rc = meson_ir_raw_event_store(dev, &ev);
dev->raw->last_type = type;
dev->raw->jiffies_old = jiffies;
return rc;
}
void meson_ir_raw_event_handle(struct meson_ir_dev *dev)
{
/*unsigned long flags;*/
if (!dev || !dev->raw)
return;
/*spin_lock_irqsave(&dev->raw->lock, flags);*/
wake_up_process(dev->raw->thread);
/*spin_unlock_irqrestore(&dev->raw->lock, flags);*/
}
int meson_ir_raw_event_register(struct meson_ir_dev *dev)
{
int ret;
dev_info(dev->dev, "meson ir raw event register\n");
dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL);
if (!dev->raw)
return -ENOMEM;
dev->raw->dev = dev;
dev->raw->max_frame_time = dev->max_frame_time;
dev->raw->jiffies_old = jiffies;
ret = kfifo_alloc(&dev->raw->kfifo,
sizeof(struct meson_ir_raw_event) *
MAX_REMOTE_EVENT_SIZE, GFP_KERNEL);
if (ret < 0)
goto out;
dev->raw->thread = kthread_run(meson_ir_raw_event_thread, dev->raw,
"ir-thread");
if (IS_ERR(dev->raw->thread)) {
ret = PTR_ERR(dev->raw->thread);
goto err_alloc_thread;
}
mutex_lock(&meson_ir_raw_handler_lock);
list_add_tail(&dev->raw->list, &meson_ir_raw_client_list);
mutex_unlock(&meson_ir_raw_handler_lock);
return 0;
err_alloc_thread:
kfifo_free(&dev->raw->kfifo);
out:
kfree(dev->raw);
return ret;
}
void meson_ir_raw_event_unregister(struct meson_ir_dev *dev)
{
if (!dev || !dev->raw)
return;
kthread_stop(dev->raw->thread);
mutex_lock(&meson_ir_raw_handler_lock);
list_del(&dev->raw->list);
mutex_unlock(&meson_ir_raw_handler_lock);
}
int meson_ir_raw_handler_register(struct meson_ir_raw_handler *handler)
{
mutex_lock(&meson_ir_raw_handler_lock);
list_add_tail(&handler->list, &meson_ir_raw_handler_list);
mutex_unlock(&meson_ir_raw_handler_lock);
return 0;
}
void meson_ir_raw_handler_unregister(struct meson_ir_raw_handler *handler)
{
mutex_lock(&meson_ir_raw_handler_lock);
list_del(&handler->list);
mutex_unlock(&meson_ir_raw_handler_lock);
}