/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/irblaster/aml_irblaster.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include #include #include #include #include #include #include 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("20000frequency = 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; }