blob: 757fa18f8fa8d6f4a5a50dc2eee3e3e38c3844b7 [file] [log] [blame]
/*
* drivers/amlogic/irblaster/encoder.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/module.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_encoder.h>
static int irblaster_raw_gen_pulse_space(unsigned int *data,
unsigned int *max,
unsigned int pulse_width,
unsigned int space_width,
unsigned int len)
{
if (!*max)
return -ENOBUFS;
data[len - *max] = pulse_width;
if (!--*max)
return -ENOBUFS;
data[len - *max] = space_width;
--*max;
return 0;
}
/**
* irblaster_raw_gen() - Encode data to raw events with pulse-length modulation.
* @data: Pointer to data
* @timings: Pulse distance modulation timings.
* @raw: Data bits to encode.
* @Returns: buff len on success.
*
* Encodes the @n least significant bits of @data using space-distance
* modulation with the timing characteristics described by @timings, writing up
* to data using the *data pointer.
*/
int irblaster_raw_gen(unsigned int *data,
const struct irblaster_raw_timings *timings,
u32 raw)
{
int i, ret, max;
int len = timings->data_size;
unsigned int space;
max = len;
if (timings->header_pulse) {
ret = irblaster_raw_gen_pulse_space(data, &max,
timings->header_pulse,
timings->header_space,
len);
if (ret)
return ret;
}
if (timings->msb_first) {
for (i = timings->raw_nbits - 1; i >= 0; --i) {
space = timings->bit_space[(raw >> i) & 1];
ret = irblaster_raw_gen_pulse_space(data, &max,
timings->bit_pulse,
space, len);
if (ret)
return ret;
}
} else {
for (i = 0; i < timings->raw_nbits; ++i, raw >>= 1) {
space = timings->bit_space[raw & 1];
ret = irblaster_raw_gen_pulse_space(data, &max,
timings->bit_pulse,
space, len);
if (ret)
return ret;
}
}
ret = irblaster_raw_gen_pulse_space(data, &max,
timings->trailer_pulse,
timings->trailer_space,
len);
if (ret)
return ret;
return len;
}
unsigned int protocol_show_select(struct irblaster_chip *chip, char *buf)
{
struct irblaster_raw_handler *protocol;
unsigned int len = 0;
mutex_lock(&irblaster_raw_handler_lock);
list_for_each_entry(protocol, &irblaster_raw_handler_list, list) {
if (chip->protocol &&
chip->state.protocol == protocol->protocol)
len += scnprintf(buf + len, PAGE_SIZE - len, "[%s] ",
protocol->name);
else
len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
protocol->name);
}
len += scnprintf(len + buf, PAGE_SIZE - len, "\n");
mutex_unlock(&irblaster_raw_handler_lock);
return len;
}
unsigned int protocol_store_select(const char *buf)
{
struct irblaster_raw_handler *protocol;
mutex_lock(&irblaster_raw_handler_lock);
list_for_each_entry(protocol, &irblaster_raw_handler_list, list) {
if (sysfs_streq(buf, protocol->name)) {
mutex_unlock(&irblaster_raw_handler_lock);
return protocol->protocol;
}
}
mutex_unlock(&irblaster_raw_handler_lock);
return 0;
}
/**
* irblaster_raw_handler_register()
* - register a new raw_handle
* @ir_raw_handler: the raw_handle to add
* @Returns: 0 on success
*/
int irblaster_raw_handler_register(struct irblaster_raw_handler *ir_raw_handler)
{
mutex_lock(&irblaster_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &irblaster_raw_handler_list);
mutex_unlock(&irblaster_raw_handler_lock);
return 0;
}
EXPORT_SYMBOL(irblaster_raw_handler_register);
/**
* irblaster_raw_handler_unregister()
* - unregister a raw_handle
* @ir_raw_handler: the raw_handle to remove
*/
void irblaster_raw_handler_unregister(struct irblaster_raw_handler
*ir_raw_handler)
{
mutex_lock(&irblaster_raw_handler_lock);
list_del(&ir_raw_handler->list);
mutex_unlock(&irblaster_raw_handler_lock);
}
EXPORT_SYMBOL(irblaster_raw_handler_unregister);
/**
* irblaster_send_key() - send key with addr and commmand
* @chip: irblaster controller
* @addr: remote control ID
* @commmand: key
*/
int irblaster_send_key(struct irblaster_chip *chip, unsigned int addr,
unsigned int commmand)
{
int ret;
unsigned int *data;
if (!chip)
return -EINVAL;
if (chip->ops->send_key) {
ret = chip->ops->send_key(chip, addr, commmand);
if (ret) {
pr_err("%s(): irblaster_send fail\n",
__func__);
return -EINVAL;
}
} else {
data = kzalloc(sizeof(uint32_t) * MAX_PLUSE, GFP_KERNEL);
if (!data)
return -ENOMEM;
if (chip->protocol->encode) {
ret = chip->protocol->encode(chip->state.protocol,
addr, commmand, data);
if (ret <= 0) {
pr_err("%s(): irblaster encode fail\n",
__func__);
goto err;
}
} else {
pr_err("%s(): irblaster func %s not found\n",
__func__, __func__);
goto err;
}
ret = irblaster_send(chip, data,
chip->protocol->timing->data_size);
if (ret) {
pr_err("%s(): irblaster_send fail\n", __func__);
goto err;
}
kfree(data);
}
return 0;
err:
kfree(data);
return -EINVAL;
}
/**
* irblaster_set_protocol() - set irblaster protocol
* @chip: irblaster controller
* @ir_protocol: irblaster protocol
*/
int irblaster_set_protocol(struct irblaster_chip *chip,
enum irblaster_protocol ir_protocol)
{
struct irblaster_raw_handler *protocol;
if (!chip || ir_protocol < 0 || ir_protocol >= IRBLASTER_PROTOCOL_MAX)
return -EINVAL;
mutex_lock(&irblaster_raw_handler_lock);
list_for_each_entry(protocol, &irblaster_raw_handler_list, list)
if (protocol->protocol == ir_protocol) {
chip->state.protocol = ir_protocol;
chip->protocol = protocol;
mutex_unlock(&irblaster_raw_handler_lock);
return 0;
}
mutex_unlock(&irblaster_raw_handler_lock);
pr_err("%s(): irblaster protocol is not found\n", __func__);
return -EINVAL;
}
/**
* irblaster_get_protocol() - get irblaster protocol
* @chip: irblaster controller
*/
enum irblaster_protocol irblaster_get_protocol(struct irblaster_chip *chip)
{
if (!chip)
return -EINVAL;
return chip->state.protocol;
}