372 lines
7.1 KiB
C
372 lines
7.1 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* drivers/irblaster/aml_irblaster.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include <asm/arch/io.h>
|
|
#include <asm/cpu_id.h>
|
|
#include <common.h>
|
|
#include <config.h>
|
|
#include <command.h>
|
|
#include <amlogic/aml_irblaster.h>
|
|
#include <asm/arch/secure_apb.h>
|
|
|
|
static struct aml_irblaster_drv_s irblaster_driver;
|
|
const char *protocol_name[] = {
|
|
"NEC",
|
|
};
|
|
|
|
/*NEC key value*/
|
|
#define NEC_HEADER 9000
|
|
#define NEC_IDLE 4500
|
|
const static int NEC_END[] = {
|
|
560,
|
|
560
|
|
};
|
|
const static int NEC_ONE[] = {
|
|
560,
|
|
1690
|
|
};
|
|
const static int NEC_ZERO[] = {
|
|
560,
|
|
560
|
|
};
|
|
|
|
static void get_nec_data(unsigned int value)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
unsigned int num = 0;
|
|
int index;
|
|
unsigned int *pdata = drv->windows;
|
|
pdata[num++] = NEC_HEADER;
|
|
pdata[num++] = NEC_IDLE;
|
|
/*low bit first*/
|
|
for (index = 0; index < 32; index++) {
|
|
if (value & (0x01 << index)) {
|
|
pdata[num++] = NEC_ONE[0];
|
|
pdata[num++] = NEC_ONE[1];
|
|
} else {
|
|
pdata[num++] = NEC_ZERO[0];
|
|
pdata[num++] = NEC_ZERO[1];
|
|
}
|
|
}
|
|
pdata[num++] = NEC_END[0];
|
|
pdata[num++] = NEC_END[1];
|
|
drv->windows_num = num;
|
|
drv->sendvalue = value;
|
|
}
|
|
|
|
static int send_bit(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
|
|
};
|
|
|
|
/*
|
|
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(val, 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(val, AO_IR_BLASTER_ADDR2);
|
|
return 0;
|
|
}
|
|
|
|
static void send_frame(void)
|
|
{
|
|
int i;
|
|
int exp = 0x00;
|
|
unsigned int* pdata;
|
|
unsigned int high_ct, low_ct;
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
unsigned int consumerir_cycle = 1000 / (drv->frequency / 1000);
|
|
|
|
/*reset*/
|
|
writel(readl(AO_RTI_GEN_CNTL_REG0) | (1 << 23), AO_IR_BLASTER_ADDR2);
|
|
udelay(2);
|
|
writel(readl(AO_RTI_GEN_CNTL_REG0) & ~(1 << 23), AO_IR_BLASTER_ADDR2);
|
|
|
|
/*
|
|
1. disable ir blaster
|
|
2. set the modulator_tb = 2'10; mpeg_1uS_tick 1us
|
|
*/
|
|
writel((1 << 2) | (2 << 12) | (1<<2), 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 * drv->dutycycle/100;
|
|
low_ct = consumerir_cycle - high_ct;
|
|
writel(((high_ct - 1) << 16) | ((low_ct - 1) << 0),
|
|
AO_IR_BLASTER_ADDR1);
|
|
|
|
/* Setting this bit to 1 initializes the output to be high.*/
|
|
writel(readl(AO_IR_BLASTER_ADDR0) & ~(1 << 2), AO_IR_BLASTER_ADDR0);
|
|
|
|
/*enable irblaster*/
|
|
writel(readl(AO_IR_BLASTER_ADDR0) | (1 << 0), AO_IR_BLASTER_ADDR0);
|
|
#define SEND_BIT_NUM 64
|
|
exp = drv->windows_num / SEND_BIT_NUM;
|
|
pdata = drv->windows;
|
|
|
|
while (exp) {
|
|
for (i = 0; i < SEND_BIT_NUM/2; i++) {
|
|
send_bit(*pdata, *(pdata+1), consumerir_cycle);
|
|
pdata += 2;
|
|
}
|
|
while (!(readl(AO_IR_BLASTER_ADDR0) & (1<<24))) ;
|
|
while (readl(AO_IR_BLASTER_ADDR0) & (1<<26)) ;
|
|
/*reset*/
|
|
writel(readl(AO_RTI_GEN_CNTL_REG0) | (1 << 23),
|
|
AO_RTI_GEN_CNTL_REG0);
|
|
udelay(2);
|
|
/*reset*/
|
|
writel(readl(AO_RTI_GEN_CNTL_REG0) & ~(1 << 23),
|
|
AO_RTI_GEN_CNTL_REG0);
|
|
exp--;
|
|
}
|
|
exp = (drv->windows_num % SEND_BIT_NUM) & (~(1));
|
|
for (i = 0; i < exp; ) {
|
|
send_bit(*pdata, *(pdata+1), consumerir_cycle);
|
|
pdata += 2;
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
extern void irblaster_pinmux_config(void);
|
|
static int open(void)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
|
|
drv->protocol = 0; /*default NEC*/
|
|
drv->frequency = 38000; /*freq 38k*/
|
|
|
|
/*pinmux*/
|
|
irblaster_pinmux_config();
|
|
drv->openflag = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int close(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int send(unsigned int value)
|
|
{
|
|
get_nec_data(value);
|
|
send_frame();
|
|
return 0;
|
|
}
|
|
|
|
static void print_windows(void)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
int n;
|
|
unsigned int *pdata;
|
|
|
|
pdata = drv->windows;
|
|
n = drv->windows_num;
|
|
printf ("sendvalue:%u\n", drv->sendvalue);
|
|
printf ("windows_number:%d\n", n);
|
|
printf ("windows:\n");
|
|
while (n--)
|
|
printf ("%d\n", *pdata++);
|
|
}
|
|
|
|
static int setprotocol(char *name)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
int p = 0;
|
|
int size = sizeof(protocol_name) / sizeof(protocol_name[0]);
|
|
|
|
for (p = 0; p < size; p++) {
|
|
if (!strcmp(protocol_name[p], name)) {
|
|
drv->protocol = p;
|
|
return 0;
|
|
}
|
|
}
|
|
return CMD_RET_USAGE;
|
|
}
|
|
|
|
static const char *getprocotol(void)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
int size = sizeof(protocol_name) / sizeof(protocol_name[0]);
|
|
|
|
if (drv->protocol >= size)
|
|
return NULL;
|
|
return protocol_name[drv->protocol];
|
|
}
|
|
|
|
static int setfrequency(unsigned int freq)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
|
|
if (freq < 20000 || freq > 60000) {
|
|
printf("20000<freq<60000\n");
|
|
return CMD_RET_USAGE;
|
|
}
|
|
drv->frequency = freq;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int getfrequency(void)
|
|
{
|
|
struct aml_irblaster_drv_s *drv = aml_irblaster_get_driver();
|
|
|
|
return drv->frequency;
|
|
}
|
|
|
|
unsigned char ci_nec_fe01[] = {
|
|
0x01,
|
|
0x02,
|
|
0x03,
|
|
0x04,
|
|
0x05,
|
|
0x06,
|
|
0x07,
|
|
0x08,
|
|
0x09,
|
|
0x0a,
|
|
0x1F,
|
|
0x15,
|
|
0x16,
|
|
0x0c,
|
|
0x0d,
|
|
0x0e,
|
|
0x0f,
|
|
0x11,
|
|
0x1c,
|
|
0x1b,
|
|
0x19,
|
|
0x1a,
|
|
0x1d,
|
|
0x17,
|
|
0x49,
|
|
0x43,
|
|
0x12,
|
|
0x14,
|
|
0x18,
|
|
0x59,
|
|
0x5a,
|
|
0x42,
|
|
0x44,
|
|
0x1e,
|
|
0x4b,
|
|
0x58,
|
|
0x46,
|
|
0x40,
|
|
0x38,
|
|
0x57,
|
|
0x5b,
|
|
0x54,
|
|
0x4c,
|
|
0x4e,
|
|
0x55,
|
|
0x53,
|
|
0x52,
|
|
0x39,
|
|
0x41,
|
|
0x0b,
|
|
0x00,
|
|
0x13
|
|
};
|
|
|
|
static int loop_test(unsigned int times)
|
|
{
|
|
unsigned int framecode;
|
|
int n, cnt = 0;
|
|
int size = ARRAY_SIZE(ci_nec_fe01);
|
|
|
|
printf("test loop...\n");
|
|
for ( ; times--; ) {
|
|
printf("times=%d\n", cnt++);
|
|
for (n = 0; n < size; n++) {
|
|
framecode = 0xfe01 | ((~ci_nec_fe01[n] & 0xff) << 24) |
|
|
(ci_nec_fe01[n] << 16);
|
|
printf("0x%x\n", framecode);
|
|
get_nec_data(framecode);
|
|
send_frame();
|
|
mdelay(500);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int read_reg(volatile unsigned int *addr, unsigned int length)
|
|
{
|
|
int n;
|
|
int value;
|
|
|
|
printf("read_reg\n");
|
|
for (n = 0; n < length; n++) {
|
|
value = readl(addr);
|
|
printf("0x%p=0x%x\n", addr, value);
|
|
++addr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_reg(volatile unsigned int *addr, unsigned int value)
|
|
{
|
|
writel(value, addr);
|
|
printf("0x%p=0x%x", addr, value);
|
|
return 0;
|
|
}
|
|
|
|
static struct aml_irblaster_drv_s irblaster_driver = {
|
|
.frequency = 38000,
|
|
.dutycycle = 50,
|
|
.protocol = 0,
|
|
.windows_num = 0,
|
|
.openflag = 0,
|
|
.open = open,
|
|
.close = close,
|
|
.send = send,
|
|
.setprotocol = setprotocol,
|
|
.getprocotol = getprocotol,
|
|
.setfrequency = setfrequency,
|
|
.getfrequency = getfrequency,
|
|
.test = loop_test,
|
|
.print_windows = print_windows,
|
|
.read_reg = read_reg,
|
|
.write_reg = write_reg,
|
|
};
|
|
|
|
struct aml_irblaster_drv_s *aml_irblaster_get_driver(void)
|
|
{
|
|
return &irblaster_driver;
|
|
}
|