irblaster: meson: refactored irblaster driver [1/1]

PD#SWPL-1856

Problem:
refactored irblaster code

Solution:
1. Refactor the code according to the core, provider, and consumer
   frameworks.
2. Provide software encode to let irblaster work according to different
   protocols
3. Provide a unified consumer interface to allow other consumer drivers
   to use irblaster.

Verify:
test pass on g12a_u200_v1

Change-Id: Ifd841ef0ed741b7fd721defc25691744ea2103f0
Signed-off-by: Bichao Zheng <bichao.zheng@amlogic.com>
This commit is contained in:
Bichao Zheng
2019-03-01 14:33:02 +08:00
committed by Jianxin Pan
parent 754ec0f2e8
commit 849e419957
37 changed files with 2569 additions and 136 deletions

View File

@@ -14053,13 +14053,6 @@ AMLOGIC AXG ADD AO CLK
M: Yun Cai <yun.cai@amlogic.com>
F: drivers/amlogic/clk/axg/axg_ao.c
AMLOGIC Irblaster driver
M: Zan Peng <zan.peng@amlogic.com>
F: drivers/amlogic/irblaster/irblaster.c
F: drivers/amlogic/irblaster/irblaster.h
F: drivers/amlogic/irblaster/Kconfig
F: drivers/amlogic/irblaster/Makefile
AMLOGIC AXG ADD CLKMSR INTERFACE
M: wang xing <xing.wang@amlogic.com>
F: include/linux/amlogic/clk_measure.h
@@ -14415,9 +14408,9 @@ F: drivers/amlogic/cpufreq/meson-cpufreq.c
F: drivers/amlogic/clk/clk-cpu-fclk-composite.c
AMLOGIC Irblaster driver
M: yu.tu <yu.tu@amlogic.com>
F: drivers/amlogic/irblaster/meson-irblaster.c
F: drivers/amlogic/irblaster/meson-irblaster.h
M: Bichao.Zheng <bichao.zheng@amlogic.com>
F: drivers/amlogic/irblaster/*
F: include/linux/amlogic/irblaster*
AMLOGIC THERMAL DRIVER
M: Huan Biao <huan.biao@amlogic.com>

View File

@@ -421,14 +421,6 @@
pinctrl-0 = <&b_uart_pins>;
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
};
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";

View File

@@ -316,14 +316,6 @@
pinctrl-0 = <&b_uart_pins>;
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
};
/* Sound iomap */
aml_snd_iomap {
compatible = "amlogic, snd-iomap";

View File

@@ -468,6 +468,16 @@
clock-names = "clk_i2c";
clock-frequency = <100000>;
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0xc0 0xc>,
<0x40 0x4>;
#irblaster-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -743,16 +753,6 @@
};
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0xff8000c0 0x10>,
<0xff800040 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "disabled";
};
saradc:saradc {
compatible = "amlogic,meson-axg-saradc";
status = "okay";

View File

@@ -746,6 +746,17 @@
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x14c 0x10>,
<0x40 0x4>;
#irblaster-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -1323,15 +1334,6 @@
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0xff80014c 0x10>,
<0xff800040 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "okay";
};
sd_emmc_c: emmc@ffe07000 {
status = "disabled";

View File

@@ -802,6 +802,15 @@
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x14c 0x10>,
<0x40 0x4>;
#irblaster-cells = <2>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -1424,15 +1433,6 @@
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0xff80014c 0x10>,
<0xff800040 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "disabled";
};
sd_emmc_c: emmc@ffe07000 {
status = "disabled";

View File

@@ -522,6 +522,14 @@
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0xc0 0xc>,
<0x40 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
};
periphs: periphs@c8834000 {
@@ -634,6 +642,20 @@
function = "ee_cec";
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "ir_out_ao7";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "ir_out_ao9";
function = "ir_out";
};
};
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {

View File

@@ -629,6 +629,14 @@
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0xc0 0xc>,
<0x40 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
};
periphs: periphs@c8834000 {
@@ -734,6 +742,20 @@
function = "ee_cec";
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "ir_out_ao7";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "ir_out_ao9";
function = "ir_out";
};
};
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {

View File

@@ -897,11 +897,12 @@
max_frame_time = <200>;
};
meson_irblaster: irblaster@14c {
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x14c 0x10>,
<0x40 0x4>;
interrupts = <0 198 1>;
#irblaster-cells = <2>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};

View File

@@ -645,6 +645,14 @@
status = "disabled";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0xc0 0xc>,
<0x40 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
remote:rc@0580 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
@@ -1186,6 +1194,20 @@
function = "gpio_aobus";
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "remote_out_ao2";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "remote_out_ao6";
function = "ir_out";
};
};
};
&pinctrl_periphs {

View File

@@ -840,12 +840,12 @@
status = "disabled";
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0xc0 0xc>,
<0x40 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
@@ -1030,6 +1030,13 @@
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "remote_out_ao6";
function = "ir_out";
};
};
pwmleds_pins:pwmleds {
mux {

View File

@@ -362,7 +362,9 @@ CONFIG_AMLOGIC_ESM=y
CONFIG_AMLOGIC_WIFI=y
CONFIG_AMLOGIC_BT_DEVICE=y
CONFIG_AMLOGIC_PCIE=y
CONFIG_AMLOGIC_IRBLASTER=y
CONFIG_AMLOGIC_IRBLASTER_CORE=y
CONFIG_AMLOGIC_IRBLASTER_MESON=y
CONFIG_AMLOGIC_IRBLASTER_PROTOCOL=y
CONFIG_AMLOGIC_IIO=y
CONFIG_AMLOGIC_SARADC=y
CONFIG_AMLOGIC_DDR_TOOL=y

View File

@@ -421,14 +421,6 @@
pinctrl-0 = <&b_uart_pins>;
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
};
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";

View File

@@ -414,14 +414,6 @@
pinctrl-0 = <&b_uart_pins>;
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
};
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";

View File

@@ -316,14 +316,6 @@
pinctrl-0 = <&b_uart_pins>;
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
};
/* Sound iomap */
aml_snd_iomap {
compatible = "amlogic, snd-iomap";

View File

@@ -474,6 +474,16 @@
clock-names = "clk_i2c";
clock-frequency = <100000>;
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0x0 0xc0 0x0 0xc>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -749,16 +759,6 @@
};
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0xff8000c0 0x0 0x10>,
<0x0 0xff800040 0x0 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "disabled";
};
saradc:saradc {
compatible = "amlogic,meson-axg-saradc";
status = "okay";

View File

@@ -746,6 +746,17 @@
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0x14c 0x0 0x10>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -1323,15 +1334,6 @@
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0xff80014c 0x0 0x10>,
<0x0 0xff800040 0x0 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "okay";
};
sd_emmc_c: emmc@ffe07000 {
status = "disabled";

View File

@@ -802,6 +802,15 @@
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0x14c 0x0 0x10>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
};/* end of aobus */
periphs: periphs@ff634400 {
@@ -1424,15 +1433,6 @@
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
irblaster: meson-irblaster {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0xff80014c 0x0 0x10>,
<0x0 0xff800040 0x0 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
interrupts = <0 198 1>;
status = "disabled";
};
sd_emmc_c: emmc@ffe07000 {
status = "disabled";

View File

@@ -509,6 +509,14 @@
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0x0 0xc0 0x0 0xc>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
};
periphs: periphs@c8834000 {
@@ -622,6 +630,19 @@
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "ir_out_ao7";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "ir_out_ao9";
function = "ir_out";
};
};
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {

View File

@@ -629,6 +629,14 @@
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0x0 0xc0 0x0 0xc>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
};
periphs: periphs@c8834000 {
@@ -734,6 +742,20 @@
function = "ee_cec";
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "ir_out_ao7";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "ir_out_ao9";
function = "ir_out";
};
};
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {

View File

@@ -878,11 +878,12 @@
max_frame_time = <200>;
};
meson_irblaster: irblaster@14c {
irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0x14c 0x0 0x10>,
<0x0 0x40 0x0 0x4>;
interrupts = <0 198 1>;
#irblaster-cells = <2>;
interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};

View File

@@ -645,6 +645,14 @@
status = "disabled";
};
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0x0 0xc0 0x0 0xc>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
remote:rc@0580 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
@@ -1186,6 +1194,20 @@
function = "gpio_aobus";
};
};
irblaster_pins:irblaster_pin {
mux {
groups = "remote_out_ao2";
function = "ir_out";
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "remote_out_ao6";
function = "ir_out";
};
};
};
&pinctrl_periphs {

View File

@@ -840,15 +840,14 @@
status = "disabled";
};
meson-irblaster {
compatible = "amlogic, am_irblaster";
dev_name = "meson-irblaster";
status = "disable";
pinctrl-names = "default";
pinctrl-0 = <&irblaster_pins>;
irblaster: meson-irblaster@c0 {
compatible = "amlogic, aml_irblaster";
reg = <0x0 0xc0 0x0 0xc>,
<0x0 0x40 0x0 0x4>;
#irblaster-cells = <2>;
status = "disabled";
};
remote: rc@8040 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
@@ -1030,6 +1029,13 @@
};
};
irblaster_pins1:irblaster_pin1 {
mux {
groups = "remote_out_ao6";
function = "ir_out";
};
};
pwmleds_pins:pwmleds {
mux {

View File

@@ -357,7 +357,9 @@ CONFIG_AMLOGIC_ESM=y
CONFIG_AMLOGIC_WIFI=y
CONFIG_AMLOGIC_BT_DEVICE=y
CONFIG_AMLOGIC_PCIE=y
CONFIG_AMLOGIC_IRBLASTER=y
CONFIG_AMLOGIC_IRBLASTER_CORE=y
CONFIG_AMLOGIC_IRBLASTER_MESON=y
CONFIG_AMLOGIC_IRBLASTER_PROTOCOL=y
CONFIG_AMLOGIC_IIO=y
CONFIG_AMLOGIC_SARADC=y
CONFIG_AMLOGIC_DDR_TOOL=y

View File

@@ -108,7 +108,7 @@ obj-$(CONFIG_AMLOGIC_POWER) += power/
obj-$(CONFIG_AMLOGIC_PCIE) += pci/
obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster/
obj-$(CONFIG_AMLOGIC_IRBLASTER_CORE) += irblaster/
obj-$(CONFIG_AMLOGIC_IIO) += iio/

View File

@@ -1,11 +1,34 @@
#
# Input core configuration
# Amlogic IRBLASTER
#
config AMLOGIC_IRBLASTER
bool "Amlogic irblaster device surport"
menuconfig AMLOGIC_IRBLASTER_CORE
tristate "Amlogic IRBLASTER Support"
default n
help
Say Y here if you want to use the amlogic irblaster.
This is the core code of amlogic irblaster. Say Y you
can use the core api to implement the irblaster controller driver
and use the api in the core to control the irblaster.
This option alone add core code.
if AMLOGIC_IRBLASTER_CORE
config AMLOGIC_IRBLASTER_MESON
tristate "Amlogic Meson SoC irblaster driver"
default n
help
This enable irblaster support for soc mesom (irblaster communication
controller) available in Amlogic Meson SoCs.
If you want to use meson irblaster interface,
say Y here.If you are not sure, say N.
config AMLOGIC_IRBLASTER_PROTOCOL
tristate "Add Amlogic Meson irblaster encode"
default n
help
This enables encode mode support for the irblaster (communication
controller) available in Amlogic Meson SoCs.
If you want to use meson irblaster interface,
say Y here.If you are not sure, say N.
endif

View File

@@ -1,6 +1,4 @@
#
# Makefile for the remote control drivers
#
# Each configuration option enables a list of files.
obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster.o meson-irblaster.o
obj-$(CONFIG_AMLOGIC_IRBLASTER_CORE) += core.o sysfs.o
obj-$(CONFIG_AMLOGIC_IRBLASTER_MESON) += irblaster-meson.o aml-irblaster.o
obj-$(CONFIG_AMLOGIC_IRBLASTER_PROTOCOL) += encoder.o irblaster-nec-encoder.o \
irblaster-rca-encoder.o

View File

@@ -0,0 +1,430 @@
/*
* drivers/amlogic/irblaster/aml-irblaster.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/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/spinlock.h>
#include <linux/kfifo.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#include <linux/amlogic/irblaster_encoder.h>
#define AO_IR_BLASTER_ADDR0 (0x0)
#define AO_IR_BLASTER_ADDR1 (0x4)
#define AO_IR_BLASTER_ADDR2 (0x8)
#define AO_IR_BLASTER_ADDR3 (0xc)
#define DEFAULT_CARRIER_FREQ (38000)
#define DEFAULT_DUTY_CYCLE (50)
#define BLASTER_DEVICE_COUNT (32)
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#define DEFAULT_IRBLASTER_PROTOCOL IRBLASTER_PROTOCOL_NEC
#endif
#define IR_TX_EVENT_SIZE 4
#define IR_TX_BUFFER_SIZE 1024
struct tx_event {
struct list_head list;
unsigned int size;
unsigned int buffer[IR_TX_BUFFER_SIZE];
};
DECLARE_KFIFO(fifo, struct tx_event *, IR_TX_EVENT_SIZE);
struct aml_irblaster_dev {
struct irblaster_chip chip;
struct device *dev;
struct task_struct *thread;
struct completion blaster_completion;
struct mutex lock;
spinlock_t irblaster_lock; /* use to send data */
void __iomem *reg_base;
void __iomem *reset_base;
unsigned int winnum;
unsigned int winarray[MAX_PLUSE];
};
static struct aml_irblaster_dev *
to_aml_irblaster(struct irblaster_chip *chip)
{
return container_of(chip, struct aml_irblaster_dev, chip);
}
static struct tx_event *event_get(struct aml_irblaster_dev *cw)
{
struct tx_event *ev = NULL;
ev = devm_kzalloc(cw->dev,
sizeof(struct tx_event), GFP_KERNEL);
return ev;
}
static void event_put(struct aml_irblaster_dev *cw, struct tx_event *ev)
{
devm_kfree(cw->dev, ev);
}
static int send_bit(struct aml_irblaster_dev *cw, unsigned int hightime,
unsigned int lowtime, unsigned int cycle)
{
unsigned int count_delay;
uint32_t val;
int n = 0;
int tb[3] = {
1, 10, 100
};
/*
* MODULATOR_TB:
* 00: system clock clk
* 01: mpeg_xtal3_tick
* 10: mpeg_1uS_tick
* 11: mpeg_10uS_tick
* lowtime<1024,n=0,timebase=1us
* 1024<=lowtime<10240,n=1,timebase=10us
* AO_IR_BLASTER_ADDR2
* bit12: output level(or modulation enable/disable:1=enable)
* bit[11:10]: Timebase :
* 00=1us
* 01=10us
* 10=100us
* 11=Modulator clock
* bit[9:0]: Count of timebase units to delay
*/
count_delay = (((hightime + cycle/2) / cycle) - 1) & 0x3ff;
val = (0x10000 | (1 << 12)) | (3 << 10) | (count_delay << 0);
writel_relaxed(val, cw->reg_base + AO_IR_BLASTER_ADDR2);
/*
* lowtime<1024,n=0,timebase=1us
* 1024<=lowtime<10240,n=1,timebase=10us
* 10240<=lowtime,n=2,timebase=100us
*/
n = lowtime >> 10;
if (n > 0 && n < 10)
n = 1;
else if (n >= 10)
n = 2;
lowtime = (lowtime + (tb[n] >> 1))/tb[n];
count_delay = (lowtime-1) & 0x3ff;
val = (0x10000 | (0 << 12)) |
(n << 10) | (count_delay << 0);
writel_relaxed(val, cw->reg_base + AO_IR_BLASTER_ADDR2);
return 0;
}
static void send_all_frame(struct aml_irblaster_dev *cw)
{
int i, k;
int exp = 0x00;
unsigned int *pData;
unsigned int consumerir_cycle;
unsigned int high_ct, low_ct;
unsigned long cnt, flags;
consumerir_cycle = 1000 / (cw->chip.state.freq / 1000);
/*reset*/
writel_relaxed(readl_relaxed(cw->reset_base) |
(1 << 23), cw->reset_base);
udelay(2);
writel_relaxed(readl_relaxed(cw->reset_base) &
~(1 << 23), cw->reset_base);
/*
* 1.disable ir blaster
* 2.set the modulator_tb = 2'10; mpeg_1uS_tick 1us
*/
writel_relaxed((1 << 2) | (2 << 12) | (1<<2),
cw->reg_base + AO_IR_BLASTER_ADDR0);
/*
* 1. set mod_high_count = 13
* 2. set mod_low_count = 13
* 3. 60khz 8, 38k-13us, 12
*/
high_ct = consumerir_cycle * cw->chip.state.duty/100;
low_ct = consumerir_cycle - high_ct;
writel_relaxed(((high_ct - 1) << 16) | ((low_ct - 1) << 0),
cw->reg_base + AO_IR_BLASTER_ADDR1);
/* Setting this bit to 1 initializes the output to be high.*/
writel_relaxed(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
~(1 << 2), cw->reg_base + AO_IR_BLASTER_ADDR0);
/*enable irblaster*/
writel_relaxed(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) |
(1 << 0), cw->reg_base + AO_IR_BLASTER_ADDR0);
k = cw->winnum;
#define SEND_BIT_NUM 64
exp = cw->winnum / SEND_BIT_NUM;
pData = cw->winarray;
while (exp) {
spin_lock_irqsave(&cw->irblaster_lock, flags);
for (i = 0; i < SEND_BIT_NUM/2; i++) {
send_bit(cw, *pData, *(pData+1), consumerir_cycle);
pData += 2;
}
spin_unlock_irqrestore(&cw->irblaster_lock, flags);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<24)) && time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<26)) && time_is_after_eq_jiffies(cnt))
;
/*reset*/
writel_relaxed(readl_relaxed(cw->reset_base) | (1 << 23),
cw->reset_base);
udelay(2);
writel_relaxed(readl_relaxed(cw->reset_base) & ~(1 << 23),
cw->reset_base);
exp--;
}
exp = (cw->winnum % SEND_BIT_NUM) & (~(1));
spin_lock_irqsave(&cw->irblaster_lock, flags);
for (i = 0; i < exp; ) {
send_bit(cw, *pData, *(pData+1), consumerir_cycle);
pData += 2;
i += 2;
}
spin_unlock_irqrestore(&cw->irblaster_lock, flags);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<24)) && time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<26)) && time_is_after_eq_jiffies(cnt))
;
complete(&cw->blaster_completion);
}
int aml_irblaster_send(struct irblaster_chip *chip,
unsigned int *data,
unsigned int len)
{
int i, ret;
struct tx_event *ev;
struct aml_irblaster_dev *irblaster_dev = to_aml_irblaster(chip);
init_completion(&irblaster_dev->blaster_completion);
ev = event_get(irblaster_dev);
ev->size = len;
for (i = 0; i < ev->size; i++)
ev->buffer[i] = data[i];
/* to send cycle */
kfifo_put(&fifo, (const struct tx_event *)ev);
/* to wake up ir_tx_thread */
wake_up_process(irblaster_dev->thread);
/* return after processing */
ret = wait_for_completion_interruptible_timeout
(&irblaster_dev->blaster_completion,
msecs_to_jiffies(chip->sum_time / 1000));
if (!ret) {
pr_err("failed to send all data ret = %d\n", ret);
return -ETIMEDOUT;
}
return 0;
}
static int ir_tx_thread(void *data)
{
int retval, i;
unsigned long cnt;
struct aml_irblaster_dev *irblaster_dev
= (struct aml_irblaster_dev *)data;
struct tx_event *ev = NULL;
while (!kthread_should_stop()) {
retval = kfifo_len(&fifo);
if (retval <= 0) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
schedule();
continue;
}
retval = kfifo_get(&fifo, &ev);
if (retval) {
irblaster_dev->winnum = ev->size;
for (i = 0; i < irblaster_dev->winnum; i++)
irblaster_dev->winarray[i] = ev->buffer[i];
send_all_frame(irblaster_dev);
event_put(irblaster_dev, ev);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(irblaster_dev->reg_base +
AO_IR_BLASTER_ADDR0) & (1<<24)) &&
time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(irblaster_dev->reg_base +
AO_IR_BLASTER_ADDR0) & (1<<26)) &&
time_is_after_eq_jiffies(cnt))
;
} else
pr_err("kfifo_get fail\n");
}
return 0;
}
static struct irblaster_ops aml_irblaster_ops = {
.send = aml_irblaster_send,
};
static int aml_irblaster_probe(struct platform_device *pdev)
{
struct aml_irblaster_dev *irblaster_dev = NULL;
struct resource *reg_mem = NULL;
struct resource *reset_mem = NULL;
void __iomem *reg_base = NULL;
void __iomem *reset_base = NULL;
int err;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "pdev->dev.of_node == NULL!\n");
return -EINVAL;
}
irblaster_dev = devm_kzalloc(&pdev->dev,
sizeof(struct aml_irblaster_dev),
GFP_KERNEL);
if (!irblaster_dev)
return -ENOMEM;
platform_set_drvdata(pdev, irblaster_dev);
irblaster_dev->dev = &pdev->dev;
reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!IS_ERR_OR_NULL(reg_mem)) {
reg_base = devm_ioremap_resource(&pdev->dev, reg_mem);
if (IS_ERR(reg_base)) {
dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n");
return PTR_ERR(reg_base);
}
} else {
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
return PTR_ERR(reg_base);
}
reset_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!IS_ERR_OR_NULL(reset_mem)) {
reset_base = devm_ioremap_resource(&pdev->dev,
reset_mem);
if (IS_ERR(reset_base)) {
dev_err(&pdev->dev, "reg1: cannot obtain I/O memory region.\n");
return PTR_ERR(reset_base);
}
} else {
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
return PTR_ERR(reset_mem);
}
spin_lock_init(&irblaster_dev->irblaster_lock);
init_completion(&irblaster_dev->blaster_completion);
irblaster_dev->reg_base = reg_base;
irblaster_dev->reset_base = reset_base;
irblaster_dev->chip.dev = &pdev->dev;
irblaster_dev->chip.ops = &aml_irblaster_ops;
irblaster_dev->chip.of_irblaster_n_cells = 2;
irblaster_dev->chip.state.freq = DEFAULT_CARRIER_FREQ;
irblaster_dev->chip.state.duty = DEFAULT_DUTY_CYCLE;
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
irblaster_dev->chip.state.protocol = DEFAULT_IRBLASTER_PROTOCOL;
irblaster_set_protocol(&irblaster_dev->chip,
DEFAULT_IRBLASTER_PROTOCOL);
#endif
err = irblasterchip_add(&irblaster_dev->chip);
if (err < 0) {
dev_err(&pdev->dev, "failed to register irblaster chip: %d\n",
err);
return err;
}
irblaster_dev->thread = kthread_run(ir_tx_thread, irblaster_dev,
"ir-blaster-thread");
return 0;
}
static int aml_irblaster_remove(struct platform_device *pdev)
{
struct aml_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev);
irblasterchip_remove(&irblaster_dev->chip);
return 0;
}
static const struct of_device_id irblaster_dt_match[] = {
{
.compatible = "amlogic, aml_irblaster",
},
{},
};
static struct platform_driver aml_irblaster_driver = {
.probe = aml_irblaster_probe,
.remove = aml_irblaster_remove,
.suspend = NULL,
.resume = NULL,
.driver = {
.name = "aml-irblaster",
.owner = THIS_MODULE,
.of_match_table = irblaster_dt_match,
},
};
static int __init aml_irblaster_init(void)
{
return platform_driver_register(&aml_irblaster_driver);
}
static void __exit aml_irblaster_exit(void)
{
platform_driver_unregister(&aml_irblaster_driver);
}
fs_initcall_sync(aml_irblaster_init);
module_exit(aml_irblaster_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic ir blaster driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,366 @@
/*
* drivers/amlogic/irblaster/core.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/device.h>
#include <linux/atomic.h>
#include <linux/amlogic/irblaster.h>
void irblaster_chip_data_clear(struct irblaster_chip *chip)
{
chip->buffer = NULL;
chip->buffer_len = 0;
chip->sum_time = 0;
}
/**
* irblaster_send() - send raw level data
* @chip: irblaster controller
* @data: raw level data (us)
* @len: raw len
*/
int irblaster_send(struct irblaster_chip *chip, unsigned int *data,
unsigned int len)
{
unsigned int sum_time = 0;
int err, i;
if (!chip || (len % 2 == 1) || len == 0 || len > MAX_PLUSE) {
pr_err("%s(): parameter error\n", __func__);
return -EINVAL;
}
for (i = 0; i < len; i++)
sum_time += data[i];
chip->buffer = data;
chip->buffer_len = len;
chip->sum_time = sum_time;
if (chip->ops->send) {
err = chip->ops->send(chip, data, len);
if (err)
return err;
} else {
pr_err("%s(): irblaster func %s not found\n",
__func__, __func__);
return -EINVAL;
}
irblaster_chip_data_clear(chip);
return 0;
}
/**
* irblaster_set_freq() - set irblaster freq
* @chip: irblaster controller
* @freq: irblaster freq (HZ)
*/
int irblaster_set_freq(struct irblaster_chip *chip, unsigned int freq)
{
int ret;
if (!chip || freq <= 0)
return -EINVAL;
if (chip->ops->set_freq) {
ret = chip->ops->set_freq(chip, freq);
if (ret)
return -EINVAL;
}
chip->state.freq = freq;
return 0;
}
/**
* irblaster_get_freq() - get irblaster freq
* @chip: irblaster controller
*/
unsigned int irblaster_get_freq(struct irblaster_chip *chip)
{
unsigned int freq;
if (!chip)
return -EINVAL;
if (chip->ops->get_freq) {
freq = chip->ops->get_freq(chip);
if (freq == 0)
return -EINVAL;
} else {
freq = chip->state.freq;
}
return freq;
}
/**
* irblaster_set_duty() - set irblaster duty
* @chip: irblaster controller
* @duty: irblaster duty
*/
int irblaster_set_duty(struct irblaster_chip *chip, unsigned int duty)
{
int ret;
if (!chip || duty <= 0 || duty > 100)
return -EINVAL;
if (chip->ops->set_duty) {
ret = chip->ops->set_duty(chip, duty);
if (ret)
return -EINVAL;
}
chip->state.duty = duty;
return 0;
}
/**
* irblaster_set_duty() - set irblaster duty
* @chip: irblaster controller
* @duty: irblaster duty
*/
unsigned int irblaster_get_duty(struct irblaster_chip *chip)
{
unsigned int duty;
if (chip->ops->get_duty) {
duty = chip->ops->get_duty(chip);
if (duty == 0)
return -EINVAL;
} else {
duty = chip->state.duty;
}
return duty;
}
/**
* irblasterchip_remove() - remove a irblaster Controller
* @chip: the irblaster chip to remove
* @Returns: 0 on success or a negative error code on failure.
*/
int irblasterchip_remove(struct irblaster_chip *chip)
{
mutex_lock(&irblaster_lock);
list_del_init(&chip->list);
if (chip->dev)
of_node_put(chip->dev->of_node);
irblasterchip_sysfs_unexport(chip);
mutex_unlock(&irblaster_lock);
return 0;
}
EXPORT_SYMBOL_GPL(irblasterchip_remove);
static bool irblaster_ops_check(struct irblaster_ops *ops)
{
/* These one interfaces are the most basic of the irblaster */
if (ops->send)
return true;
return false;
}
/**
* irblasterchip_add() - register a new irblaster Controller
* @chip: the irblaster chip to add
* @Returns: 0 on success or a negative error code on failure.
*/
int irblasterchip_add(struct irblaster_chip *chip)
{
if (!chip || !chip->dev || !chip->ops)
return -EINVAL;
if (!irblaster_ops_check(chip->ops))
return -EINVAL;
mutex_lock(&irblaster_lock);
atomic_set(&chip->request, IRBLASTER_EXPORTED);
INIT_LIST_HEAD(&chip->list);
list_add(&chip->list, &irblaster_chips);
irblasterchip_sysfs_export(chip);
mutex_unlock(&irblaster_lock);
return 0;
}
EXPORT_SYMBOL_GPL(irblasterchip_add);
/**
* irblaster_put() - release a irblaster controller
* @chip: irblaster controller
*/
void irblaster_put(struct irblaster_chip *chip)
{
if (!chip)
return;
mutex_lock(&irblaster_lock);
atomic_set(&chip->request, IRBLASTER_EXPORTED);
irblaster_chip_data_clear(chip);
irblasterchip_sysfs_export(chip);
mutex_unlock(&irblaster_lock);
}
EXPORT_SYMBOL_GPL(irblaster_put);
static struct irblaster_chip *of_node_to_irblasterchip(struct device_node *np)
{
struct irblaster_chip *chip;
mutex_lock(&irblaster_lock);
list_for_each_entry(chip, &irblaster_chips, list)
if (chip->dev && chip->dev->of_node == np) {
mutex_unlock(&irblaster_lock);
if (atomic_read(&chip->request) == IRBLASTER_REQUESTED)
return ERR_PTR(-EPROBE_DEFER);
return chip;
}
mutex_unlock(&irblaster_lock);
return ERR_PTR(-EPROBE_DEFER);
}
static int irblaster_set_default_state(struct irblaster_chip *pc,
const struct of_phandle_args *args)
{
int ret;
if (pc->of_irblaster_n_cells < 2 ||
args->args[0] <= 0 || args->args[1] > 100)
return -EINVAL;
pc->state.freq = args->args[0];
pc->state.duty = args->args[1];
ret = irblaster_set_freq(pc, pc->state.freq);
if (ret)
return -EINVAL;
ret = irblaster_set_duty(pc, pc->state.duty);
if (ret)
return -EINVAL;
return 0;
}
/**
* of_irblaster_get() - request a irblaster via the irblaster framework
* @np: device node to get the irblaster from
* @con_id: consumer name
*
* Returns the irblaster controller parsed from the phandle and index
* specified in the "irblaster-config" property of a device tree node
* or a negative error-code on failure. Values parsed from the device
* tree are stored in the returned irblaster device object.
*
* Returns: A pointer to the requested irblaster controller or an ERR_PTR()
* -encoded error code on failure.
*/
struct irblaster_chip *of_irblaster_get(struct device_node *np,
const char *con_id)
{
struct of_phandle_args args;
struct irblaster_chip *pc;
int err, index = 0;
err = of_parse_phandle_with_args(np, "irblaster-config",
"#irblaster-cells", index,
&args);
if (err) {
pr_err("%s(): can't parse \"irblaster-config\" property\n",
__func__);
return ERR_PTR(err);
}
pc = of_node_to_irblasterchip(args.np);
if (IS_ERR(pc)) {
pr_err("%s(): irblaster chip not found\n", __func__);
pc = ERR_PTR(-EINVAL);
goto put;
}
if (args.args_count != pc->of_irblaster_n_cells) {
pr_err("%s: wrong #irblaster-cells for %s\n", np->full_name,
args.np->full_name);
pc = ERR_PTR(-EINVAL);
goto put;
}
err = irblaster_set_default_state(pc, &args);
if (err < 0) {
pr_err("%s(): irblaster get state fail\n", __func__);
pc = ERR_PTR(-EINVAL);
goto put;
}
atomic_set(&pc->request, IRBLASTER_REQUESTED);
irblasterchip_sysfs_unexport(pc);
put:
of_node_put(args.np);
return pc;
}
EXPORT_SYMBOL_GPL(of_irblaster_get);
static void devm_irblaster_release(struct device *dev, void *res)
{
irblaster_put(*(struct irblaster_chip **)res);
}
/**
* devm_of_irblaster_get() - resource managed of_irblaster_get()
* @dev: device for irblaster consumer
* @np: device node to get the irblaster from
* @con_id: consumer name
*
* This function performs like of_irblaster_get() but the acquired irblaster
* device will automatically be released on driver detach.
*
* Returns: A pointer to the requested irblaster device or an ERR_PTR()-encoded
* error code on failure.
*/
struct irblaster_chip *devm_of_irblaster_get(struct device *dev,
struct device_node *np,
const char *con_id)
{
struct irblaster_chip **dr, *chip;
dr = devres_alloc(devm_irblaster_release, sizeof(*dr), GFP_KERNEL);
if (!dr)
return ERR_PTR(-ENOMEM);
chip = of_irblaster_get(np, con_id);
if (!IS_ERR(chip)) {
*dr = chip;
devres_add(dev, dr);
} else {
devres_free(dr);
}
return chip;
}
EXPORT_SYMBOL_GPL(devm_of_irblaster_get);

View File

@@ -0,0 +1,267 @@
/*
* 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;
}

View File

@@ -0,0 +1,415 @@
/*
* drivers/amlogic/irblaster/irblaster-meson.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/of_device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/of_address.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#include <linux/amlogic/irblaster_encoder.h>
/* Amlogic AO_IR_BLASTER_ADDR0 bits */
#define BLASTER_BUSY BIT(26)
#define BLASTER_FIFO_FULL BIT(25)
#define BLASTER_FIFO_EMPTY BIT(24)
#define BLASTER_FIFO_LEVEL (0xff << 16)
#define BLASTER_MODULATOR_TB_SYSTEM_CLOCK (0x0 << 12)
#define BLASTER_MODULATOR_TB_XTAL3_TICK (0x1 << 12)
#define BLASTER_MODULATOR_TB_1US_TICK (0x2 << 12)
#define BLASTER_MODULATOR_TB_10US_TICK (0x3 << 12)
#define BLASTER_SLOW_CLOCK_DIV (0xff << 4)
#define BLASTER_SLOW_CLOCK_MODE BIT(3)
#define BLASTER_INIT_HIGH BIT(2)
#define BLASTER_INIT_LOW BIT(1)
#define BLASTER_ENABLE BIT(0)
/* Amlogic AO_IR_BLASTER_ADDR1 bits */
#define BLASTER_MODULATION_LOW_COUNT(c) ((c) << 16)
#define BLASTER_MODULATION_HIGH_COUNT(c) ((c) << 0)
/* Amlogic AO_IR_BLASTER_ADDR2 bits */
#define BLASTER_WRITE_FIFO BIT(16)
#define BLASTER_MODULATION_ENABLE BIT(12)
#define BLASTER_TIMEBASE_1US (0x0 << 10)
#define BLASTER_TIMEBASE_10US (0x1 << 10)
#define BLASTER_TIMEBASE_100US (0x2 << 10)
#define BLASTER_TIMEBASE_MODULATION_CLOCK (0x3 << 10)
/* Amlogic AO_IR_BLASTER_ADDR3 bits */
#define BLASTER_FIFO_THD_PENDING BIT(16)
#define BLASTER_FIFO_IRQ_ENABLE BIT(8)
#define BLASTER_FIFO_IRQ_THRESHOLD(c) (((c) & 0xff) << 0)
#define DEFAULT_CARRIER_FREQ (38000)
#define DEFAULT_DUTY_CYCLE (50)
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#define DEFAULT_IRBLASTER_PROTOCOL IRBLASTER_PROTOCOL_NEC
#endif
#define LIMIT_DUTY (25)
#define MAX_DUTY (75)
#define LIMIT_FREQ (25000)
#define MAX_FREQ (60000)
#define COUNT_DELAY_MASK (0X3ff)
#define TIMEBASE_SHIFT (10)
#define BLASTER_KFIFO_SIZE (4)
#define AO_IR_BLASTER_ADDR0 (0x0)
#define AO_IR_BLASTER_ADDR1 (0x4)
#define AO_IR_BLASTER_ADDR2 (0x8)
#define AO_IR_BLASTER_ADDR3 (0xc)
#define AO_RTI_GEN_CTNL_REG0 (0x0)
#define CONSUMERIR_TRANSMIT 0x5500
#define GET_CARRIER 0x5501
#define SET_CARRIER 0x5502
#define SET_DUTYCYCLE 0x5503
struct meson_irblaster_dev {
struct device *dev;
struct work_struct blaster_work;
struct irblaster_chip chip;
struct completion blaster_completion;
unsigned int count;
unsigned int irq;
unsigned int buffer_size;
unsigned int *buffer;
spinlock_t irblaster_lock; /* use to send data */
void __iomem *reg_base;
void __iomem *reset_base;
};
static void meson_irblaster_tasklet(unsigned long data);
DECLARE_TASKLET_DISABLED(irblaster_tasklet, meson_irblaster_tasklet, 0);
static struct meson_irblaster_dev *
to_meson_irblaster(struct irblaster_chip *chip)
{
return container_of(chip, struct meson_irblaster_dev, chip);
}
static void blaster_initialize(struct meson_irblaster_dev *dev)
{
unsigned int carrier_cycle = 1000 / (dev->chip.state.freq / 1000);
unsigned int high_ct, low_ct;
/*
*1. disable ir blaster
*2. set the modulator_tb = 2'10; mpeg_1uS_tick 1us
*3. set initializes the output to be high
*/
writel_relaxed((~BLASTER_ENABLE) & (BLASTER_MODULATOR_TB_1US_TICK |
BLASTER_INIT_HIGH), dev->reg_base + AO_IR_BLASTER_ADDR0);
/*
*1. set mod_high_count = 13
*2. set mod_low_count = 13
*3. 60khz-8us, 38k-13us
*/
high_ct = carrier_cycle * dev->chip.state.duty / 100;
low_ct = carrier_cycle - high_ct;
writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
dev->reg_base + AO_IR_BLASTER_ADDR1);
/*mask initialize output to be high*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) &
~BLASTER_INIT_HIGH,
dev->reg_base + AO_IR_BLASTER_ADDR0);
/*
*1. set fifo irq enable
*2. set fifo irq threshold
*/
writel_relaxed(BLASTER_FIFO_IRQ_ENABLE |
BLASTER_FIFO_IRQ_THRESHOLD(8),
dev->reg_base + AO_IR_BLASTER_ADDR3);
/*enable irblaster*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) |
BLASTER_ENABLE,
dev->reg_base + AO_IR_BLASTER_ADDR0);
}
static int write_to_fifo(struct meson_irblaster_dev *dev,
unsigned int hightime,
unsigned int lowtime)
{
unsigned int count_delay;
unsigned int cycle = 1000 / (dev->chip.state.freq / 1000);
u32 val;
int n = 0;
int tb[3] = {
1, 10, 100
};
/*
* hightime: modulator signal.
* MODULATOR_TB:
* 00: system clock
* 01: mpeg_xtal3_tick
* 10: mpeg_1uS_tick
* 11: mpeg_10uS_tick
*
* AO_IR_BLASTER_ADDR2
* bit12: output level(or modulation enable/disable:1=enable)
* bit[11:10]: Timebase :
* 00=1us
* 01=10us
* 10=100us
* 11=Modulator clock
* bit[9:0]: Count of timebase units to delay
*/
count_delay = (((hightime + cycle / 2) / cycle) - 1) & COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0));
writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);
/*
* lowtime<1024,n=0,timebase=1us
* 1024<=lowtime<10240,n=1,timebase=10us
* 10240<=lowtime,n=2,timebase=100us
*/
n = lowtime >> 10;
if (n > 0 && n < 10)
n = 1;
else if (n >= 10)
n = 2;
lowtime = (lowtime + (tb[n] >> 1)) / tb[n];
count_delay = (lowtime - 1) & COUNT_DELAY_MASK;
val = (BLASTER_WRITE_FIFO & (~BLASTER_MODULATION_ENABLE)) |
(n << TIMEBASE_SHIFT) | (count_delay << 0);
writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);
return 0;
}
static void send_all_data(struct meson_irblaster_dev *dev)
{
int i;
unsigned int *pdata = NULL;
unsigned long flags;
pdata = &dev->buffer[dev->count];
spin_lock_irqsave(&dev->irblaster_lock, flags);
for (i = 0; (i < 120) && (dev->count < dev->buffer_size);) {
write_to_fifo(dev, *pdata, *(pdata + 1));
pdata += 2;
dev->count += 2;
i += 2;
}
spin_unlock_irqrestore(&dev->irblaster_lock, flags);
}
int meson_irblaster_send(struct irblaster_chip *chip,
unsigned int *data,
unsigned int len)
{
int ret, i, sum_time = 0;
unsigned int high_ct, low_ct;
unsigned int cycle;
struct meson_irblaster_dev *irblaster_dev = to_meson_irblaster(chip);
init_completion(&irblaster_dev->blaster_completion);
irblaster_dev->buffer = data;
irblaster_dev->buffer_size = len;
irblaster_dev->count = 0;
for (i = 0; i < irblaster_dev->buffer_size; i++)
sum_time = sum_time + data[i];
/*
* 1. set mod_high_count = 13
* 2. set mod_low_count = 13
* 3. 60khz-8us, 38k-13us
*/
cycle = 1000 / (irblaster_dev->chip.state.freq / 1000);
high_ct = cycle * irblaster_dev->chip.state.duty / 100;
low_ct = cycle - high_ct;
writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
irblaster_dev->reg_base + AO_IR_BLASTER_ADDR1);
send_all_data(irblaster_dev);
ret = wait_for_completion_interruptible_timeout
(&irblaster_dev->blaster_completion,
msecs_to_jiffies(sum_time / 1000));
if (!ret) {
pr_err("failed to send all data ret = %d\n", ret);
return -ETIMEDOUT;
}
return 0;
}
static struct irblaster_ops meson_irblaster_ops = {
.send = meson_irblaster_send,
};
static void meson_irblaster_tasklet(unsigned long data)
{
struct meson_irblaster_dev *dev = (struct meson_irblaster_dev *)data;
if (dev->count < dev->buffer_size)
send_all_data(dev);
}
static irqreturn_t meson_blaster_interrupt(int irq, void *dev_id)
{
struct meson_irblaster_dev *dev = dev_id;
/*clear pending bit*/
writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR3) &
(~BLASTER_FIFO_THD_PENDING),
dev->reg_base + AO_IR_BLASTER_ADDR3);
if (dev->count >= dev->buffer_size) {
complete(&dev->blaster_completion);
return IRQ_HANDLED;
}
tasklet_schedule(&irblaster_tasklet);
return IRQ_HANDLED;
}
static int meson_irblaster_probe(struct platform_device *pdev)
{
struct meson_irblaster_dev *irblaster_dev = NULL;
struct resource *reg_mem = NULL;
void __iomem *reg_base = NULL;
int err, ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "pdev->dev.of_node == NULL!\n");
return -EINVAL;
}
irblaster_dev = devm_kzalloc(&pdev->dev,
sizeof(struct meson_irblaster_dev),
GFP_KERNEL);
if (!irblaster_dev)
return -ENOMEM;
spin_lock_init(&irblaster_dev->irblaster_lock);
platform_set_drvdata(pdev, irblaster_dev);
irblaster_dev->dev = &pdev->dev;
reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!IS_ERR_OR_NULL(reg_mem)) {
reg_base = devm_ioremap_resource(&pdev->dev, reg_mem);
if (IS_ERR(reg_base)) {
dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n");
return PTR_ERR(reg_base);
}
} else {
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
return PTR_ERR(reg_base);
}
irblaster_dev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irblaster_dev->irq) {
dev_err(&pdev->dev, "irq: Failed to request irq number.\n");
return -ENODEV;
}
init_completion(&irblaster_dev->blaster_completion);
ret = devm_request_irq(&pdev->dev, irblaster_dev->irq,
meson_blaster_interrupt,
IRQF_TRIGGER_RISING,
dev_name(&pdev->dev),
irblaster_dev);
if (ret) {
pr_err("Failed to request irq.\n");
return ret;
}
irblaster_dev->reg_base = reg_base;
irblaster_dev->chip.dev = &pdev->dev;
irblaster_dev->chip.ops = &meson_irblaster_ops;
irblaster_dev->chip.of_irblaster_n_cells = 2;
irblaster_dev->chip.state.freq = DEFAULT_CARRIER_FREQ;
irblaster_dev->chip.state.duty = DEFAULT_DUTY_CYCLE;
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
irblaster_dev->chip.state.protocol = DEFAULT_IRBLASTER_PROTOCOL;
irblaster_set_protocol(&irblaster_dev->chip,
DEFAULT_IRBLASTER_PROTOCOL);
#endif
err = irblasterchip_add(&irblaster_dev->chip);
if (err < 0) {
dev_err(&pdev->dev, "failed to register irblaster chip: %d\n",
err);
return err;
}
irblaster_tasklet.data = (unsigned long)irblaster_dev;
tasklet_enable(&irblaster_tasklet);
/*initial blaster*/
blaster_initialize(irblaster_dev);
return 0;
}
static int meson_irblaster_remove(struct platform_device *pdev)
{
struct meson_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev);
tasklet_disable(&irblaster_tasklet);
tasklet_kill(&irblaster_tasklet);
irblasterchip_remove(&irblaster_dev->chip);
return 0;
}
static const struct of_device_id irblaster_dt_match[] = {
{
.compatible = "amlogic, meson_irblaster",
},
{},
};
MODULE_DEVICE_TABLE(of, irblaster_dt_match);
static struct platform_driver meson_irblaster_driver = {
.probe = meson_irblaster_probe,
.remove = meson_irblaster_remove,
.driver = {
.name = "meson_irblaster",
.owner = THIS_MODULE,
.of_match_table = irblaster_dt_match,
},
};
static int __init meson_irblaster_init(void)
{
return platform_driver_register(&meson_irblaster_driver);
}
static void __exit meson_irblaster_exit(void)
{
platform_driver_unregister(&meson_irblaster_driver);
}
fs_initcall_sync(meson_irblaster_init);
module_exit(meson_irblaster_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic ir blaster driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,180 @@
/*
* drivers/amlogic/irblaster/irblaster-nec-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/amlogic/irblaster_encoder.h>
#define NEC_NBITS 32
#define NEC_UNIT 562 /* ns */
#define NEC_HEADER_PULSE 9000
#define NEC_HEADER_SPACE 4500
#define NEC_BIT_PULSE 560
#define NEC_BIT_0_SPACE 560
#define NEC_BIT_1_SPACE 1690
#define NEC_TRAILER_PULSE 560
#define NEC_TRAILER_SPACE 5600 /* even longer in reality */
static struct irblaster_raw_timings irblaster_nec_timings = {
.header_pulse = NEC_HEADER_PULSE,
.header_space = NEC_HEADER_SPACE,
.bit_pulse = NEC_BIT_PULSE,
.bit_space[0] = NEC_BIT_0_SPACE,
.bit_space[1] = NEC_BIT_1_SPACE,
.trailer_pulse = NEC_TRAILER_PULSE,
.trailer_space = NEC_TRAILER_SPACE,
.msb_first = 0,
.raw_nbits = NEC_NBITS,
.data_size = (NEC_NBITS + 2) * 2,
};
static u32 irblaster_nec32_scancode_to_raw(enum irblaster_protocol protocol,
unsigned int addrs,
unsigned int commmand)
{
unsigned int addr = 0, addr_inv, data, data_inv;
data = commmand & 0xff;
addr_inv = addr & 0xff;
addr = addrs & 0xff;
data_inv = data & 0xff;
return data_inv << 24 |
data << 16 |
addr_inv << 8 |
addr;
}
static u32 irblaster_necx_scancode_to_raw(enum irblaster_protocol protocol,
unsigned int addrs,
unsigned int commmand)
{
unsigned int addr, addr_inv, data, data_inv;
data = commmand & 0xff;
addr = addrs & 0xff;
addr_inv = addr & 0xff;
data_inv = data ^ 0xff;
return data_inv << 24 |
data << 16 |
addr_inv << 8 |
addr;
}
static u32 irblaster_nec_scancode_to_raw(enum irblaster_protocol protocol,
unsigned int addrs,
unsigned int commmand)
{
unsigned int addr, addr_inv, data, data_inv;
data = commmand & 0xff;
addr = addrs & 0xff;
addr_inv = addr ^ 0xff;
data_inv = data ^ 0xff;
return data_inv << 24 |
data << 16 |
addr_inv << 8 |
addr;
}
int irblaster_nec_encode(enum irblaster_protocol protocol,
unsigned int addr,
unsigned int commmand,
unsigned int *data)
{
u32 raw, ret;
if (protocol >= IRBLASTER_PROTOCOL_MAX)
return -ENODEV;
/* Convert a NEC scancode to raw NEC data */
switch (protocol) {
case IRBLASTER_PROTOCOL_NEC:
raw = irblaster_nec_scancode_to_raw(protocol, addr, commmand);
break;
case IRBLASTER_PROTOCOL_NECX:
raw = irblaster_necx_scancode_to_raw(protocol, addr, commmand);
break;
case IRBLASTER_PROTOCOL_NEC32:
raw = irblaster_nec32_scancode_to_raw(protocol, addr, commmand);
break;
default:
raw = irblaster_nec_scancode_to_raw(protocol, addr, commmand);
break;
}
/* Modulate the raw data using a pulse distance modulation */
ret = irblaster_raw_gen(data, &irblaster_nec_timings, raw);
if (ret < 0)
return ret;
return ret;
}
static struct irblaster_raw_handler irblaster_nec_handler[] = {
{
.name = "NEC",
.protocol = IRBLASTER_PROTOCOL_NEC,
.encode = irblaster_nec_encode,
.freq = 38000,
.duty = 50,
.timing = &irblaster_nec_timings,
},
{
.name = "NECX",
.protocol = IRBLASTER_PROTOCOL_NECX,
.encode = irblaster_nec_encode,
.freq = 38000,
.duty = 50,
.timing = &irblaster_nec_timings,
},
{
.name = "NEC32",
.protocol = IRBLASTER_PROTOCOL_NEC32,
.encode = irblaster_nec_encode,
.freq = 38000,
.duty = 50,
.timing = &irblaster_nec_timings,
}
};
static int __init irblaster_nec_decode_init(void)
{
int i;
for (i = 0; i < sizeof(irblaster_nec_handler) /
sizeof(struct irblaster_raw_handler); i++)
irblaster_raw_handler_register(irblaster_nec_handler + i);
return 0;
}
static void __exit irblaster_nec_decode_exit(void)
{
int i;
for (i = 0; i < sizeof(irblaster_nec_handler) /
sizeof(struct irblaster_raw_handler); i++)
irblaster_raw_handler_unregister(irblaster_nec_handler + i);
}
fs_initcall(irblaster_nec_decode_init);
module_exit(irblaster_nec_decode_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("NEC IR protocol decoder");

View File

@@ -0,0 +1,99 @@
/*
* drivers/amlogic/irblaster/irblaster-rca-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/amlogic/irblaster_encoder.h>
#define RCA_NBITS 24
#define RCA_UNIT 500 /* ns */
#define RCA_HEADER_PULSE 4000
#define RCA_HEADER_SPACE 4000
#define RCA_BIT_PULSE 500
#define RCA_BIT_0_SPACE 1000
#define RCA_BIT_1_SPACE 2000
#define RCA_TRAILER_PULSE 500
#define RCA_TRAILER_SPACE 5600 /* even longer in reality */
static struct irblaster_raw_timings irblaster_rca_timings = {
.header_pulse = RCA_HEADER_PULSE,
.header_space = RCA_HEADER_SPACE,
.bit_pulse = RCA_BIT_PULSE,
.bit_space[0] = RCA_BIT_0_SPACE,
.bit_space[1] = RCA_BIT_1_SPACE,
.trailer_pulse = RCA_TRAILER_PULSE,
.trailer_space = RCA_TRAILER_SPACE,
.msb_first = 1,
.raw_nbits = RCA_NBITS,
.data_size = (RCA_NBITS + 2) * 2,
};
static u32 irblaster_rca_scancode_to_raw(enum irblaster_protocol protocol,
unsigned int addrs,
unsigned int commmand)
{
unsigned int addr, addr_inv, data, data_inv;
data = commmand & 0xff;
addr = addrs & 0x0f;
addr_inv = addr ^ 0x0f;
data_inv = data ^ 0xff;
return addr << 20 |
data << 12 |
addr_inv << 8 |
data_inv;
}
int irblaster_rca_encode(enum irblaster_protocol protocol, unsigned int addr,
unsigned int commmand, unsigned int *data)
{
u32 raw, ret;
/* Convert a RCA scancode to raw rca data */
raw = irblaster_rca_scancode_to_raw(protocol, addr, commmand);
/* Modulate the raw data using a pulse distance modulation */
ret = irblaster_raw_gen(data, &irblaster_rca_timings, raw);
if (ret < 0)
return ret;
return ret;
}
static struct irblaster_raw_handler irblaster_rca_handler = {
.name = "RCA",
.protocol = IRBLASTER_PROTOCOL_RCA,
.encode = irblaster_rca_encode,
.freq = 38000,
.duty = 50,
.timing = &irblaster_rca_timings,
};
static int __init irblaster_rca_decode_init(void)
{
return irblaster_raw_handler_register(&irblaster_rca_handler);
}
static void __exit irblaster_rca_decode_exit(void)
{
irblaster_raw_handler_unregister(&irblaster_rca_handler);
}
fs_initcall(irblaster_rca_decode_init);
module_exit(irblaster_rca_decode_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("RCA IR protocol decoder");

View File

@@ -0,0 +1,275 @@
/*
* drivers/amlogic/irblaster/sysfs.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/device.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#include <linux/amlogic/irblaster_encoder.h>
#endif
static ssize_t send_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int i = 0, j = 0, m = 0;
int val, ret;
char tone[PS_SIZE];
unsigned int *buffer;
buffer = kzalloc(sizeof(uint32_t) * MAX_PLUSE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
mutex_lock(&chip->sys_lock);
while (buf[i] != '\0') {
if (buf[i] == 's') {
tone[j] = '\0';
ret = kstrtoint(tone, 10, &val);
if (ret) {
pr_err("Invalid tone\n");
mutex_unlock(&chip->sys_lock);
return ret;
}
buffer[m] = val * 10;
j = 0;
i++;
m++;
if (m >= MAX_PLUSE)
break;
continue;
}
tone[j] = buf[i];
i++;
j++;
if (j >= PS_SIZE) {
pr_err("send timing value is out of range\n");
mutex_unlock(&chip->sys_lock);
kfree(buffer);
return -ENOMEM;
}
}
ret = irblaster_send(chip, buffer, m);
if (ret)
pr_err("send raw data fail\n");
irblaster_chip_data_clear(chip);
mutex_unlock(&chip->sys_lock);
kfree(buffer);
return ret ? : count;
}
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
static ssize_t send_key_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
unsigned int addr, command;
int ret;
ret = sscanf(buf, "%x %x", &addr, &command);
if (ret != 2) {
pr_err("Can't parse addr and command,usage:[addr command]\n");
return -EINVAL;
}
mutex_lock(&chip->sys_lock);
ret = irblaster_send_key(chip, addr, command);
if (ret)
pr_err("send key fail\n");
mutex_unlock(&chip->sys_lock);
return ret ? : count;
}
#endif
static ssize_t carrier_freq_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->state.freq);
}
static ssize_t carrier_freq_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int ret = 0, val;
ret = kstrtoint(buf, 10, &val);
if (ret) {
pr_err("Invalid input for carrier_freq\n");
return ret;
}
mutex_lock(&chip->sys_lock);
ret = irblaster_set_freq(chip, val);
if (ret)
pr_err("set freq fail\n");
mutex_unlock(&chip->sys_lock);
return ret ? : count;
}
static ssize_t duty_cycle_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->state.duty);
}
static ssize_t duty_cycle_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int ret = 0, val;
ret = kstrtoint(buf, 10, &val);
if (ret) {
pr_err("Invalid input for duty_cycle\n");
return ret;
}
mutex_lock(&chip->sys_lock);
ret = irblaster_set_duty(chip, val);
if (ret)
pr_err("set duty fail\n");
mutex_unlock(&chip->sys_lock);
return ret ? : count;
}
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
static ssize_t protocol_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
unsigned int len;
len = protocol_show_select(chip, buf);
return len;
}
static ssize_t protocol_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
unsigned int ret, protocol;
protocol = protocol_store_select(buf);
if (protocol >= IRBLASTER_PROTOCOL_MAX)
pr_err("protocol is not found\n");
ret = irblaster_set_protocol(chip, protocol);
if (ret)
pr_err("set protocol fail\n");
return ret ? : count;
}
#endif
//static DEVICE_ATTR(debug, 0644, show_debug, store_debug);
static DEVICE_ATTR_WO(send);
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
static DEVICE_ATTR_WO(send_key);
#endif
static DEVICE_ATTR_RW(carrier_freq);
static DEVICE_ATTR_RW(duty_cycle);
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
static DEVICE_ATTR_RW(protocol);
#endif
static struct attribute *irblaster_chip_attrs[] = {
// &dev_attr_debug.attr,
&dev_attr_send.attr,
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
&dev_attr_send_key.attr,
#endif
&dev_attr_carrier_freq.attr,
&dev_attr_duty_cycle.attr,
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
&dev_attr_protocol.attr,
#endif
NULL,
};
ATTRIBUTE_GROUPS(irblaster_chip);
static struct class irblaster_class = {
.name = "irblaster",
.owner = THIS_MODULE,
.dev_groups = irblaster_chip_groups,
};
static int irblasterchip_sysfs_match(struct device *parent, const void *data)
{
return dev_get_drvdata(parent) == data;
}
void irblasterchip_sysfs_export(struct irblaster_chip *chip)
{
struct device *parent;
/*
* If device_create() fails the irblaster_chip is still usable by
* the kernel its just not exported.
*/
parent = device_create(&irblaster_class, chip->dev, MKDEV(0, 0), chip,
"irblaster%d", chip->base);
if (IS_ERR(parent)) {
dev_warn(chip->dev,
"device_create failed for irblaster_chip sysfs export\n");
}
mutex_init(&chip->sys_lock);
}
void irblasterchip_sysfs_unexport(struct irblaster_chip *chip)
{
struct device *parent;
parent = class_find_device(&irblaster_class, NULL, chip,
irblasterchip_sysfs_match);
if (parent) {
/* for class_find_device() */
put_device(parent);
device_unregister(parent);
}
}
static int __init irblaster_sysfs_init(void)
{
return class_register(&irblaster_class);
}
subsys_initcall(irblaster_sysfs_init);

View File

@@ -0,0 +1,121 @@
/*
* include/linux/amlogic/irblaster.h
*
* 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.
*
*/
#ifndef __LINUX_IRBLASTER_H
#define __LINUX_IRBLASTER_H
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#include <linux/amlogic/irblaster_encoder.h>
#endif
#define MAX_PLUSE 256
#define PS_SIZE 10
struct irblaster_chip;
static DEFINE_MUTEX(irblaster_lock);
static LIST_HEAD(irblaster_chips);
/**
* enum irblaster_idle - Whether the controller is occupied
* @IRBLASTER_EXPORTED: Controlled by the sysfs sys/class/irblaster
* @IRBLASTER_REQUESTED: Controlled by consumer driver
*/
enum irblaster_idle {
IRBLASTER_EXPORTED,
IRBLASTER_REQUESTED
};
/**
* struct irblaster_ops - irblaster controller operations
* @send: send raw level data
* @send_key: send key according to the protocol
* @set_freq: set irblaster freq
* @get_freq: get irblaster freq
* @set_duty: set irblaster duty
* @get_duty: get irblaster duty
*/
struct irblaster_ops {
int (*send)(struct irblaster_chip *chip,
unsigned int *data, unsigned int len);
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
int (*send_key)(struct irblaster_chip *chip,
unsigned int addr, int commmand);
/* int (*set_protocol)(struct irblaster_chip *chip, */
/* enum irblaster_protocol protocol);*/
#endif
int (*set_freq)(struct irblaster_chip *chip, unsigned int freq);
unsigned int (*get_freq)(struct irblaster_chip *chip);
int (*set_duty)(struct irblaster_chip *chip, unsigned int duty);
unsigned int (*get_duty)(struct irblaster_chip *chip);
};
struct irblaster_state {
unsigned int freq;
unsigned int duty;
int enabled;
int idle;
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
enum irblaster_protocol protocol;
#endif
};
/**
* struct irblaster_chip - abstract a irblaster controller
* @dev: device providing the irblaster
* @list: list node for internal use
* @ops: callbacks for this irblaster controller
* @base: number of first irblaster controlled by this chip
* @of_irblaster_n_cells: number of cells expected in the device tree
* irblaster specifier
* @state: irblaster controller status
* @buffer: data
* @buffer_len: data len
* @sum_time: total time
* @request: whether the controller is occupied
*/
struct irblaster_chip {
struct device *dev;
struct list_head list;
struct irblaster_ops *ops;
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
struct irblaster_raw_handler *protocol;
#endif
struct irblaster_state state;
struct mutex sys_lock; /* use to sysfs */
unsigned int base;
unsigned int of_irblaster_n_cells;
/* unsigned int buffer[MAX_PLUSE]; */
unsigned int *buffer;
unsigned int buffer_len;
unsigned int sum_time;
atomic_t request;
};
/* irblaster sysfs APIs */
void irblasterchip_sysfs_export(struct irblaster_chip *chip);
void irblasterchip_sysfs_unexport(struct irblaster_chip *chip);
/* irblaster provider APIs */
int irblasterchip_add(struct irblaster_chip *chip);
int irblasterchip_remove(struct irblaster_chip *chip);
void irblaster_chip_data_clear(struct irblaster_chip *chip);
#endif /* __LINUX_IRBLASTER_H */

View File

@@ -0,0 +1,55 @@
/*
* include/linux/amlogic/irblaster_consumer.h
*
* 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.
*
*/
#ifndef __LINUX_IRBLASTER_CONSUMER_H
#define __LINUX_IRBLASTER_CONSUMER_H
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#include <linux/amlogic/irblaster_encoder.h>
#endif
struct irblaster_chip;
/* irblaster user APIs */
int irblaster_send(struct irblaster_chip *chip,
unsigned int *data,
unsigned int len);
int irblaster_set_freq(struct irblaster_chip *chip,
unsigned int freq);
unsigned int irblaster_get_freq(struct irblaster_chip *chip);
int irblaster_set_duty(struct irblaster_chip *chip, unsigned int duty);
unsigned int irblaster_get_duty(struct irblaster_chip *chip);
struct irblaster_chip *of_irblaster_get(struct device_node *np,
const char *con_id);
struct irblaster_chip *devm_of_irblaster_get(struct device *dev,
struct device_node *np,
const char *con_id);
void irblaster_put(struct irblaster_chip *chip);
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
int irblaster_send_key(struct irblaster_chip *chip,
unsigned int addr,
unsigned int commmand);
int irblaster_set_protocol(struct irblaster_chip *chip,
enum irblaster_protocol ir_protocol);
enum irblaster_protocol irblaster_get_protocol(struct irblaster_chip *chip);
#endif
#endif /* __LINUX_IRBLASTER_CONSUMER_H */

View File

@@ -0,0 +1,97 @@
/*
* include/linux/amlogic/irblaster_encoder.h
*
* 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.
*
*/
#ifndef __LINUX_ENCODER_H
#define __LINUX_ENCODER_H
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
struct irblaster_chip;
int irblaster_send(struct irblaster_chip *chip, unsigned int *data,
unsigned int len);
static DEFINE_MUTEX(irblaster_raw_handler_lock);
static LIST_HEAD(irblaster_raw_handler_list);
/**
* enum irblaster_protocol - protocol of irblaster
* add a new protocol here
* @IRBLASTER_PROTOCOL_MAX: Maximum number of support protocol
* please add a new protocol before this
*/
enum irblaster_protocol {
IRBLASTER_PROTOCOL_NEC,
IRBLASTER_PROTOCOL_NECX,
IRBLASTER_PROTOCOL_NEC32,
IRBLASTER_PROTOCOL_RCA,
IRBLASTER_PROTOCOL_MAX
};
#define IRBLASTER_PROTOCOL_BIT_NEC BIT_ULL(IRBLASTER_PROTOCOL_NEC)
#define IRBLASTER_PROTOCOL_BIT_NECX BIT_ULL(IRBLASTER_PROTOCOL_NECX)
#define IRBLASTER_PROTOCOL_BIT_NEC32 BIT_ULL(IRBLASTER_PROTOCOL_NEC32)
#define IRBLASTER_PROTOCOL_BIT_RCA BIT_ULL(IRBLASTER_PROTOCOL_RCA)
/**
* struct irblaster_raw_timings - pulse-length modulation timings
* @header_pulse: duration of header pulse in ns (0 for none)
* @bit_space: duration of bit space in ns
* @bit_pulse: duration of bit pulse (for logic 0 and 1) in ns
* @trailer_space: duration of trailer space in ns
* @msb_first: 1 if most significant bit is sent first
* @raw_nbits: raw bit len
* @data_size: the total length of the array
*/
struct irblaster_raw_timings {
unsigned int header_pulse;
unsigned int header_space;
unsigned int bit_pulse;
unsigned int bit_space[2];
unsigned int trailer_pulse;
unsigned int trailer_space;
unsigned int msb_first:1;
unsigned int raw_nbits;
unsigned int data_size;
};
struct irblaster_raw_handler {
struct list_head list;
char *name;
int protocol; /* which are handled by this handler */
int (*encode)(enum irblaster_protocol protocol, unsigned int addr,
unsigned int commmand, unsigned int *data);
struct irblaster_raw_timings *timing;
struct mutex encode_lock; /* use to function encode */
u32 freq;
u32 duty;
};
/* irblaster encode APIs */
int irblaster_raw_handler_register(struct irblaster_raw_handler
*ir_raw_handler);
void irblaster_raw_handler_unregister(struct irblaster_raw_handler
*ir_raw_handler);
int irblaster_raw_gen(unsigned int *data,
const struct irblaster_raw_timings *timings,
u32 raw);
unsigned int protocol_store_select(const char *buf);
unsigned int protocol_show_select(struct irblaster_chip *chip, char *buf);
#endif /* __LINUX_ENCODER_H */