/* process ir data stored in driver buffer */ static void fintek_process_rx_ir_data(struct fintek_dev *fintek) { DEFINE_IR_RAW_EVENT(rawir); u8 sample; bool event = false; int i; for (i = 0; i < fintek->pkts; i++) { sample = fintek->buf[i]; switch (fintek->parser_state) { case CMD_HEADER: fintek->cmd = sample; if ((fintek->cmd == BUF_COMMAND_HEADER) || ((fintek->cmd & BUF_COMMAND_MASK) != BUF_PULSE_BIT)) { fintek->parser_state = SUBCMD; continue; } fintek->rem = (fintek->cmd & BUF_LEN_MASK); fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem); if (fintek->rem) fintek->parser_state = PARSE_IRDATA; else ir_raw_event_reset(fintek->rdev); break; case SUBCMD: fintek->rem = fintek_cmdsize(fintek->cmd, sample); fintek->parser_state = CMD_DATA; break; case CMD_DATA: fintek->rem--; break; case PARSE_IRDATA: fintek->rem--; init_ir_raw_event(&rawir); rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK) * CIR_SAMPLE_PERIOD); fit_dbg("Storing %s with duration %d", rawir.pulse ? "pulse" : "space", rawir.duration); if (ir_raw_event_store_with_filter(fintek->rdev, &rawir)) event = true; break; } if ((fintek->parser_state != CMD_HEADER) && !fintek->rem) fintek->parser_state = CMD_HEADER; } fintek->pkts = 0; if (event) { fit_dbg("Calling ir_raw_event_handle"); ir_raw_event_handle(fintek->rdev); } }
/* detect hardware features */ static int fintek_hw_detect(struct fintek_dev *fintek) { unsigned long flags; u8 chip_major, chip_minor; u8 vendor_major, vendor_minor; u8 portsel, ir_class; u16 vendor; int ret = 0; fintek_config_mode_enable(fintek); /* Check if we're using config port 0x4e or 0x2e */ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); if (portsel == 0xff) { fit_pr(KERN_INFO, "first portsel read was bunk, trying alt"); fintek_config_mode_disable(fintek); fintek->cr_ip = CR_INDEX_PORT2; fintek->cr_dp = CR_DATA_PORT2; fintek_config_mode_enable(fintek); portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); } fit_dbg("portsel reg: 0x%02x", portsel); ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS); fit_dbg("ir_class reg: 0x%02x", ir_class); switch (ir_class) { case CLASS_RX_2TX: case CLASS_RX_1TX: fintek->hw_tx_capable = true; break; case CLASS_RX_ONLY: default: fintek->hw_tx_capable = false; break; } chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); vendor = vendor_major << 8 | vendor_minor; if (vendor != VENDOR_ID_FINTEK) fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor); else fit_dbg("Read Fintek vendor ID from chip"); fintek_config_mode_disable(fintek); spin_lock_irqsave(&fintek->fintek_lock, flags); fintek->chip_major = chip_major; fintek->chip_minor = chip_minor; fintek->chip_vendor = vendor; spin_unlock_irqrestore(&fintek->fintek_lock, flags); return ret; }
static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) { struct fintek_dev *fintek = pnp_get_drvdata(pdev); unsigned long flags; fit_dbg("%s called", __func__); spin_lock_irqsave(&fintek->fintek_lock, flags); /* disable all CIR interrupts */ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); spin_unlock_irqrestore(&fintek->fintek_lock, flags); fintek_config_mode_enable(fintek); /* disable cir logical dev */ fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); /* make sure wake is enabled */ fintek_enable_wake(fintek); return 0; }
/* write val to config reg */ static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg) { fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", __func__, reg, val, fintek->cr_ip, fintek->cr_dp); outb(reg, fintek->cr_ip); outb(val, fintek->cr_dp); }
/* copy data from hardware rx register into driver buffer */ static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs) { unsigned long flags; u8 sample, status; spin_lock_irqsave(&fintek->fintek_lock, flags); /* * We must read data from CIR_RX_DATA until the hardware IR buffer * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in * the CIR_STATUS register */ do { sample = fintek_cir_reg_read(fintek, CIR_RX_DATA); fit_dbg("%s: sample: 0x%02x", __func__, sample); fintek->buf[fintek->pkts] = sample; fintek->pkts++; status = fintek_cir_reg_read(fintek, CIR_STATUS); if (!(status & CIR_STATUS_IRQ_EN)) break; } while (status & rx_irqs); fintek_process_rx_ir_data(fintek); spin_unlock_irqrestore(&fintek->fintek_lock, flags); }
/* read val from config reg */ static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg) { u8 val; outb(reg, fintek->cr_ip); val = inb(fintek->cr_dp); fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", __func__, reg, val, fintek->cr_ip, fintek->cr_dp); return val; }
static void fintek_cir_ldev_init(struct fintek_dev *fintek) { /* Select CIR logical device and enable */ fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); /* Write allocated CIR address and IRQ information to hardware */ fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI); fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO); fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL); fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)", fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len); }
static int fintek_resume(struct pnp_dev *pdev) { struct fintek_dev *fintek = pnp_get_drvdata(pdev); fit_dbg("%s called", __func__); /* open interrupt */ fintek_enable_cir_irq(fintek); /* Enable CIR logical device */ fintek_config_mode_enable(fintek); fintek_select_logical_dev(fintek, fintek->logical_dev_cir); fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); fintek_config_mode_disable(fintek); fintek_cir_regs_init(fintek); return 0; }