// SPDX-License-Identifier: GPL-2.0-or-later 
 | 
/* 
 | 
 * ImgTec IR Decoder setup for NEC protocol. 
 | 
 * 
 | 
 * Copyright 2010-2014 Imagination Technologies Ltd. 
 | 
 */ 
 | 
  
 | 
#include "img-ir-hw.h" 
 | 
#include <linux/bitrev.h> 
 | 
#include <linux/log2.h> 
 | 
  
 | 
/* Convert NEC data to a scancode */ 
 | 
static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols, 
 | 
                   struct img_ir_scancode_req *request) 
 | 
{ 
 | 
    unsigned int addr, addr_inv, data, data_inv; 
 | 
    /* a repeat code has no data */ 
 | 
    if (!len) 
 | 
        return IMG_IR_REPEATCODE; 
 | 
    if (len != 32) 
 | 
        return -EINVAL; 
 | 
    /* raw encoding: ddDDaaAA */ 
 | 
    addr     = (raw >>  0) & 0xff; 
 | 
    addr_inv = (raw >>  8) & 0xff; 
 | 
    data     = (raw >> 16) & 0xff; 
 | 
    data_inv = (raw >> 24) & 0xff; 
 | 
    if ((data_inv ^ data) != 0xff) { 
 | 
        /* 32-bit NEC (used by Apple and TiVo remotes) */ 
 | 
        /* scan encoding: as transmitted, MSBit = first received bit */ 
 | 
        request->scancode = bitrev8(addr)     << 24 | 
 | 
                bitrev8(addr_inv) << 16 | 
 | 
                bitrev8(data)     <<  8 | 
 | 
                bitrev8(data_inv); 
 | 
        request->protocol = RC_PROTO_NEC32; 
 | 
    } else if ((addr_inv ^ addr) != 0xff) { 
 | 
        /* Extended NEC */ 
 | 
        /* scan encoding: AAaaDD */ 
 | 
        request->scancode = addr     << 16 | 
 | 
                addr_inv <<  8 | 
 | 
                data; 
 | 
        request->protocol = RC_PROTO_NECX; 
 | 
    } else { 
 | 
        /* Normal NEC */ 
 | 
        /* scan encoding: AADD */ 
 | 
        request->scancode = addr << 8 | 
 | 
                data; 
 | 
        request->protocol = RC_PROTO_NEC; 
 | 
    } 
 | 
    return IMG_IR_SCANCODE; 
 | 
} 
 | 
  
 | 
/* Convert NEC scancode to NEC data filter */ 
 | 
static int img_ir_nec_filter(const struct rc_scancode_filter *in, 
 | 
                 struct img_ir_filter *out, u64 protocols) 
 | 
{ 
 | 
    unsigned int addr, addr_inv, data, data_inv; 
 | 
    unsigned int addr_m, addr_inv_m, data_m, data_inv_m; 
 | 
  
 | 
    data       = in->data & 0xff; 
 | 
    data_m     = in->mask & 0xff; 
 | 
  
 | 
    protocols &= RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | RC_PROTO_BIT_NEC32; 
 | 
  
 | 
    /* 
 | 
     * If only one bit is set, we were requested to do an exact 
 | 
     * protocol. This should be the case for wakeup filters; for 
 | 
     * normal filters, guess the protocol from the scancode. 
 | 
     */ 
 | 
    if (!is_power_of_2(protocols)) { 
 | 
        if ((in->data | in->mask) & 0xff000000) 
 | 
            protocols = RC_PROTO_BIT_NEC32; 
 | 
        else if ((in->data | in->mask) & 0x00ff0000) 
 | 
            protocols = RC_PROTO_BIT_NECX; 
 | 
        else 
 | 
            protocols = RC_PROTO_BIT_NEC; 
 | 
    } 
 | 
  
 | 
    if (protocols == RC_PROTO_BIT_NEC32) { 
 | 
        /* 32-bit NEC (used by Apple and TiVo remotes) */ 
 | 
        /* scan encoding: as transmitted, MSBit = first received bit */ 
 | 
        addr       = bitrev8(in->data >> 24); 
 | 
        addr_m     = bitrev8(in->mask >> 24); 
 | 
        addr_inv   = bitrev8(in->data >> 16); 
 | 
        addr_inv_m = bitrev8(in->mask >> 16); 
 | 
        data       = bitrev8(in->data >>  8); 
 | 
        data_m     = bitrev8(in->mask >>  8); 
 | 
        data_inv   = bitrev8(in->data >>  0); 
 | 
        data_inv_m = bitrev8(in->mask >>  0); 
 | 
    } else if (protocols == RC_PROTO_BIT_NECX) { 
 | 
        /* Extended NEC */ 
 | 
        /* scan encoding AAaaDD */ 
 | 
        addr       = (in->data >> 16) & 0xff; 
 | 
        addr_m     = (in->mask >> 16) & 0xff; 
 | 
        addr_inv   = (in->data >>  8) & 0xff; 
 | 
        addr_inv_m = (in->mask >>  8) & 0xff; 
 | 
        data_inv   = data ^ 0xff; 
 | 
        data_inv_m = data_m; 
 | 
    } else { 
 | 
        /* Normal NEC */ 
 | 
        /* scan encoding: AADD */ 
 | 
        addr       = (in->data >>  8) & 0xff; 
 | 
        addr_m     = (in->mask >>  8) & 0xff; 
 | 
        addr_inv   = addr ^ 0xff; 
 | 
        addr_inv_m = addr_m; 
 | 
        data_inv   = data ^ 0xff; 
 | 
        data_inv_m = data_m; 
 | 
    } 
 | 
  
 | 
    /* raw encoding: ddDDaaAA */ 
 | 
    out->data = data_inv << 24 | 
 | 
            data     << 16 | 
 | 
            addr_inv <<  8 | 
 | 
            addr; 
 | 
    out->mask = data_inv_m << 24 | 
 | 
            data_m     << 16 | 
 | 
            addr_inv_m <<  8 | 
 | 
            addr_m; 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * NEC decoder 
 | 
 * See also http://www.sbprojects.com/knowledge/ir/nec.php 
 | 
 *        http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol 
 | 
 */ 
 | 
struct img_ir_decoder img_ir_nec = { 
 | 
    .type = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | RC_PROTO_BIT_NEC32, 
 | 
    .control = { 
 | 
        .decoden = 1, 
 | 
        .code_type = IMG_IR_CODETYPE_PULSEDIST, 
 | 
    }, 
 | 
    /* main timings */ 
 | 
    .unit = 562500, /* 562.5 us */ 
 | 
    .timings = { 
 | 
        /* leader symbol */ 
 | 
        .ldr = { 
 | 
            .pulse = { 16    /* 9ms */ }, 
 | 
            .space = { 8    /* 4.5ms */ }, 
 | 
        }, 
 | 
        /* 0 symbol */ 
 | 
        .s00 = { 
 | 
            .pulse = { 1    /* 562.5 us */ }, 
 | 
            .space = { 1    /* 562.5 us */ }, 
 | 
        }, 
 | 
        /* 1 symbol */ 
 | 
        .s01 = { 
 | 
            .pulse = { 1    /* 562.5 us */ }, 
 | 
            .space = { 3    /* 1687.5 us */ }, 
 | 
        }, 
 | 
        /* free time */ 
 | 
        .ft = { 
 | 
            .minlen = 32, 
 | 
            .maxlen = 32, 
 | 
            .ft_min = 10,    /* 5.625 ms */ 
 | 
        }, 
 | 
    }, 
 | 
    /* repeat codes */ 
 | 
    .repeat = 108,            /* 108 ms */ 
 | 
    .rtimings = { 
 | 
        /* leader symbol */ 
 | 
        .ldr = { 
 | 
            .space = { 4    /* 2.25 ms */ }, 
 | 
        }, 
 | 
        /* free time */ 
 | 
        .ft = { 
 | 
            .minlen = 0,    /* repeat code has no data */ 
 | 
            .maxlen = 0, 
 | 
        }, 
 | 
    }, 
 | 
    /* scancode logic */ 
 | 
    .scancode = img_ir_nec_scancode, 
 | 
    .filter = img_ir_nec_filter, 
 | 
}; 
 |