| /* |
| * Remote control driver for the TV-card based on bt829 |
| * |
| * by Leonid Froenchenko <lfroen@galileo.co.il> |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/threads.h> |
| #include <linux/sched.h> |
| #include <linux/ioport.h> |
| #include <linux/pci.h> |
| #include <linux/delay.h> |
| |
| #include <media/lirc_dev.h> |
| |
| static int poll_main(void); |
| static int atir_init_start(void); |
| |
| static void write_index(unsigned char index, unsigned int value); |
| static unsigned int read_index(unsigned char index); |
| |
| static void do_i2c_start(void); |
| static void do_i2c_stop(void); |
| |
| static void seems_wr_byte(unsigned char al); |
| static unsigned char seems_rd_byte(void); |
| |
| static unsigned int read_index(unsigned char al); |
| static void write_index(unsigned char ah, unsigned int edx); |
| |
| static void cycle_delay(int cycle); |
| |
| static void do_set_bits(unsigned char bl); |
| static unsigned char do_get_bits(void); |
| |
| #define DATA_PCI_OFF 0x7FFC00 |
| #define WAIT_CYCLE 20 |
| |
| #define DRIVER_NAME "lirc_bt829" |
| |
| static bool debug; |
| |
| static int atir_minor; |
| static phys_addr_t pci_addr_phys; |
| static unsigned char __iomem *pci_addr_lin; |
| |
| static struct lirc_driver atir_driver; |
| |
| static struct pci_dev *do_pci_probe(void) |
| { |
| struct pci_dev *my_dev; |
| |
| my_dev = pci_get_device(PCI_VENDOR_ID_ATI, |
| PCI_DEVICE_ID_ATI_264VT, NULL); |
| if (my_dev) { |
| pr_err("Using device: %s\n", pci_name(my_dev)); |
| pci_addr_phys = 0; |
| if (my_dev->resource[0].flags & IORESOURCE_MEM) { |
| pci_addr_phys = my_dev->resource[0].start; |
| pr_info("memory at %pa\n", &pci_addr_phys); |
| } |
| if (pci_addr_phys == 0) { |
| pr_err("no memory resource ?\n"); |
| pci_dev_put(my_dev); |
| return NULL; |
| } |
| } else { |
| pr_err("pci_probe failed\n"); |
| return NULL; |
| } |
| return my_dev; |
| } |
| |
| static int atir_add_to_buf(void *data, struct lirc_buffer *buf) |
| { |
| unsigned char key; |
| int status; |
| |
| status = poll_main(); |
| key = (status >> 8) & 0xFF; |
| if (status & 0xFF) { |
| dev_dbg(atir_driver.dev, "reading key %02X\n", key); |
| lirc_buffer_write(buf, &key); |
| return 0; |
| } |
| return -ENODATA; |
| } |
| |
| static int atir_set_use_inc(void *data) |
| { |
| dev_dbg(atir_driver.dev, "driver is opened\n"); |
| return 0; |
| } |
| |
| static void atir_set_use_dec(void *data) |
| { |
| dev_dbg(atir_driver.dev, "driver is closed\n"); |
| } |
| |
| int init_module(void) |
| { |
| struct pci_dev *pdev; |
| int rc; |
| |
| pdev = do_pci_probe(); |
| if (pdev == NULL) |
| return -ENODEV; |
| |
| rc = pci_enable_device(pdev); |
| if (rc) |
| goto err_put_dev; |
| |
| if (!atir_init_start()) { |
| rc = -ENODEV; |
| goto err_disable; |
| } |
| |
| strcpy(atir_driver.name, "ATIR"); |
| atir_driver.minor = -1; |
| atir_driver.code_length = 8; |
| atir_driver.sample_rate = 10; |
| atir_driver.data = NULL; |
| atir_driver.add_to_buf = atir_add_to_buf; |
| atir_driver.set_use_inc = atir_set_use_inc; |
| atir_driver.set_use_dec = atir_set_use_dec; |
| atir_driver.dev = &pdev->dev; |
| atir_driver.owner = THIS_MODULE; |
| |
| atir_minor = lirc_register_driver(&atir_driver); |
| if (atir_minor < 0) { |
| pr_err("failed to register driver!\n"); |
| rc = atir_minor; |
| goto err_unmap; |
| } |
| dev_dbg(atir_driver.dev, "driver is registered on minor %d\n", |
| atir_minor); |
| |
| return 0; |
| |
| err_unmap: |
| iounmap(pci_addr_lin); |
| err_disable: |
| pci_disable_device(pdev); |
| err_put_dev: |
| pci_dev_put(pdev); |
| return rc; |
| } |
| |
| |
| void cleanup_module(void) |
| { |
| struct pci_dev *pdev = to_pci_dev(atir_driver.dev); |
| |
| lirc_unregister_driver(atir_minor); |
| iounmap(pci_addr_lin); |
| pci_disable_device(pdev); |
| pci_dev_put(pdev); |
| } |
| |
| |
| static int atir_init_start(void) |
| { |
| pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); |
| if (!pci_addr_lin) { |
| pr_info("pci mem must be mapped\n"); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void cycle_delay(int cycle) |
| { |
| udelay(WAIT_CYCLE*cycle); |
| } |
| |
| |
| static int poll_main(void) |
| { |
| unsigned char status_high, status_low; |
| |
| do_i2c_start(); |
| |
| seems_wr_byte(0xAA); |
| seems_wr_byte(0x01); |
| |
| do_i2c_start(); |
| |
| seems_wr_byte(0xAB); |
| |
| status_low = seems_rd_byte(); |
| status_high = seems_rd_byte(); |
| |
| do_i2c_stop(); |
| |
| return (status_high << 8) | status_low; |
| } |
| |
| static void do_i2c_start(void) |
| { |
| do_set_bits(3); |
| cycle_delay(4); |
| |
| do_set_bits(1); |
| cycle_delay(7); |
| |
| do_set_bits(0); |
| cycle_delay(2); |
| } |
| |
| static void do_i2c_stop(void) |
| { |
| unsigned char bits; |
| |
| bits = do_get_bits() & 0xFD; |
| do_set_bits(bits); |
| cycle_delay(1); |
| |
| bits |= 1; |
| do_set_bits(bits); |
| cycle_delay(2); |
| |
| bits |= 2; |
| do_set_bits(bits); |
| bits = 3; |
| do_set_bits(bits); |
| cycle_delay(2); |
| } |
| |
| static void seems_wr_byte(unsigned char value) |
| { |
| int i; |
| unsigned char reg; |
| |
| reg = do_get_bits(); |
| for (i = 0; i < 8; i++) { |
| if (value & 0x80) |
| reg |= 0x02; |
| else |
| reg &= 0xFD; |
| |
| do_set_bits(reg); |
| cycle_delay(1); |
| |
| reg |= 1; |
| do_set_bits(reg); |
| cycle_delay(1); |
| |
| reg &= 0xFE; |
| do_set_bits(reg); |
| cycle_delay(1); |
| value <<= 1; |
| } |
| cycle_delay(2); |
| |
| reg |= 2; |
| do_set_bits(reg); |
| |
| reg |= 1; |
| do_set_bits(reg); |
| |
| cycle_delay(1); |
| do_get_bits(); |
| |
| reg &= 0xFE; |
| do_set_bits(reg); |
| cycle_delay(3); |
| } |
| |
| static unsigned char seems_rd_byte(void) |
| { |
| int i; |
| int rd_byte; |
| unsigned char bits_2, bits_1; |
| |
| bits_1 = do_get_bits() | 2; |
| do_set_bits(bits_1); |
| |
| rd_byte = 0; |
| for (i = 0; i < 8; i++) { |
| bits_1 &= 0xFE; |
| do_set_bits(bits_1); |
| cycle_delay(2); |
| |
| bits_1 |= 1; |
| do_set_bits(bits_1); |
| cycle_delay(1); |
| |
| bits_2 = do_get_bits(); |
| if (bits_2 & 2) |
| rd_byte |= 1; |
| |
| rd_byte <<= 1; |
| } |
| |
| bits_1 = 0; |
| if (bits_2 == 0) |
| bits_1 |= 2; |
| |
| do_set_bits(bits_1); |
| cycle_delay(2); |
| |
| bits_1 |= 1; |
| do_set_bits(bits_1); |
| cycle_delay(3); |
| |
| bits_1 &= 0xFE; |
| do_set_bits(bits_1); |
| cycle_delay(2); |
| |
| rd_byte >>= 1; |
| rd_byte &= 0xFF; |
| return rd_byte; |
| } |
| |
| static void do_set_bits(unsigned char new_bits) |
| { |
| int reg_val; |
| |
| reg_val = read_index(0x34); |
| if (new_bits & 2) { |
| reg_val &= 0xFFFFFFDF; |
| reg_val |= 1; |
| } else { |
| reg_val &= 0xFFFFFFFE; |
| reg_val |= 0x20; |
| } |
| reg_val |= 0x10; |
| write_index(0x34, reg_val); |
| |
| reg_val = read_index(0x31); |
| if (new_bits & 1) |
| reg_val |= 0x1000000; |
| else |
| reg_val &= 0xFEFFFFFF; |
| |
| reg_val |= 0x8000000; |
| write_index(0x31, reg_val); |
| } |
| |
| static unsigned char do_get_bits(void) |
| { |
| unsigned char bits; |
| int reg_val; |
| |
| reg_val = read_index(0x34); |
| reg_val |= 0x10; |
| reg_val &= 0xFFFFFFDF; |
| write_index(0x34, reg_val); |
| |
| reg_val = read_index(0x34); |
| bits = 0; |
| if (reg_val & 8) |
| bits |= 2; |
| else |
| bits &= 0xFD; |
| |
| reg_val = read_index(0x31); |
| if (reg_val & 0x1000000) |
| bits |= 1; |
| else |
| bits &= 0xFE; |
| |
| return bits; |
| } |
| |
| static unsigned int read_index(unsigned char index) |
| { |
| unsigned char __iomem *addr; |
| /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */ |
| addr = pci_addr_lin + ((index & 0xFF) << 2); |
| return readl(addr); |
| } |
| |
| static void write_index(unsigned char index, unsigned int reg_val) |
| { |
| unsigned char __iomem *addr; |
| |
| addr = pci_addr_lin + ((index & 0xFF) << 2); |
| writel(reg_val, addr); |
| } |
| |
| MODULE_AUTHOR("Froenchenko Leonid"); |
| MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards"); |
| MODULE_LICENSE("GPL"); |
| |
| module_param(debug, bool, S_IRUGO | S_IWUSR); |
| MODULE_PARM_DESC(debug, "Debug enabled or not"); |