--- arch/arm/Kconfig | 2 arch/arm/mach-omap2/board-omap3beagle.c | 52 arch/arm/mach-omap2/mux.c | 25 arch/arm/plat-omap/include/mach/mux.h | 7 drivers/Kconfig | 3 drivers/Makefile | 1 drivers/bmi/Kconfig | 17 drivers/bmi/Makefile | 8 drivers/bmi/core/Makefile | 7 drivers/bmi/core/core.c | 319 + drivers/bmi/core/device.c | 35 drivers/bmi/core/driver.c | 27 drivers/bmi/core/eeprom.c | 32 drivers/bmi/core/slot.c | 469 ++ drivers/bmi/pims/Kconfig | 104 drivers/bmi/pims/Makefile | 17 drivers/bmi/pims/camera/Kconfig | 23 drivers/bmi/pims/camera/Makefile | 12 drivers/bmi/pims/camera/bmi_ov2640.c | 929 +++++ drivers/bmi/pims/camera/bmi_vs6624.c | 915 +++++ drivers/bmi/pims/camera/bug_camera.c | 611 +++ drivers/bmi/pims/camera/bug_camera.h | 72 drivers/bmi/pims/camera/ov2640.c | 301 + drivers/bmi/pims/camera/ov2640.h | 14 drivers/bmi/pims/camera/vs6624_access.c | 597 +++ drivers/bmi/pims/camera/vs6624_access.h | 17 drivers/bmi/pims/camera/vs6624_patch.c | 373 ++ drivers/bmi/pims/camera/vs6624_regs.h | 467 ++ drivers/bmi/pims/factory_test/Makefile | 6 drivers/bmi/pims/factory_test/factory_test.c | 952 +++++ drivers/bmi/pims/gps/Makefile | 6 drivers/bmi/pims/gps/bmi_gps.c | 468 ++ drivers/bmi/pims/gsm/Makefile | 6 drivers/bmi/pims/gsm/bmi_gsm.c | 301 + drivers/bmi/pims/lcd/Makefile | 9 drivers/bmi/pims/lcd/acc.c | 114 drivers/bmi/pims/lcd/acc.h | 35 drivers/bmi/pims/lcd/bmi_lcd.c | 1790 ++++++++++ drivers/bmi/pims/lcd/bmi_lcd_inf.c | 1775 ++++++++++ drivers/bmi/pims/lcd/bmi_lcd_mi.c | 1855 +++++++++++ drivers/bmi/pims/lcd/bmi_s320x240.c | 632 +++ drivers/bmi/pims/lcd/lcd_ctl.c | 421 ++ drivers/bmi/pims/lcd/lcd_ctl.h | 87 drivers/bmi/pims/mdacc/Kconfig | 6 drivers/bmi/pims/mdacc/Makefile | 9 drivers/bmi/pims/mdacc/acc.c | 381 ++ drivers/bmi/pims/mdacc/acc.h | 54 drivers/bmi/pims/mdacc/avr.c | 511 +++ drivers/bmi/pims/mdacc/avr.h | 54 drivers/bmi/pims/mdacc/cque.c | 150 drivers/bmi/pims/mdacc/cque.h | 42 drivers/bmi/pims/mdacc/ctl.c | 176 + drivers/bmi/pims/mdacc/ctl.h | 43 drivers/bmi/pims/mdacc/md.c | 333 ++ drivers/bmi/pims/mdacc/md.h | 60 drivers/bmi/pims/mdacc/mdacc.c | 333 ++ drivers/bmi/pims/mdacc/mdacc.h | 43 drivers/bmi/pims/mdacc/mon.c | 474 ++ drivers/bmi/pims/mdacc/mon.h | 61 drivers/bmi/pims/projector/Makefile | 7 drivers/bmi/pims/projector/bmi_projector.c | 674 ++++ drivers/bmi/pims/projector/ch7024.c | 476 ++ drivers/bmi/pims/projector/ch7024.h | 166 + drivers/bmi/pims/sensor/Makefile | 6 drivers/bmi/pims/sensor/bmi_sensor.c | 4321 ++++++++++++++++++++++++++ drivers/bmi/pims/sound/Makefile | 6 drivers/bmi/pims/sound/bmi_audio.c | 4434 +++++++++++++++++++++++++++ drivers/bmi/pims/vonhippel/Makefile | 6 drivers/bmi/pims/vonhippel/bmi_vh.c | 942 +++++ drivers/bmi/pims/zb/Makefile | 5 drivers/bmi/pims/zb/bmi_zaccel.c | 684 ++++ drivers/bmi/pims/zb/bmi_zaccel.h | 288 + drivers/bmi/pims/zb/bmi_zigbee.c | 1296 +++++++ drivers/bmi/pims/zb/bmi_zigbee.h | 194 + drivers/bmi/pims/zb/bmi_znetdev.c | 977 +++++ drivers/bmi/pims/zb/bmi_zprotocol.c | 619 +++ drivers/bmi/slots/Kconfig | 21 drivers/bmi/slots/Makefile | 6 drivers/bmi/slots/slots_beagle.c | 267 + drivers/bmi/slots/slots_bug.c | 231 + include/linux/bmi-ids.h | 30 include/linux/bmi.h | 142 include/linux/bmi/at24c02.h | 26 include/linux/bmi/bmi-bus.h | 21 include/linux/bmi/bmi-control.h | 303 + include/linux/bmi/bmi-eeprom-data.h | 83 include/linux/bmi/bmi-eeprom-driver.h | 113 include/linux/bmi/bmi-eeprom.h | 75 include/linux/bmi/bmi-slot.h | 29 include/linux/bmi/bmi_audio.h | 449 ++ include/linux/bmi/bmi_camera.h | 36 include/linux/bmi/bmi_gps.h | 30 include/linux/bmi/bmi_gsm.h | 33 include/linux/bmi/bmi_ioctl.h | 27 include/linux/bmi/bmi_lcd.h | 71 include/linux/bmi/bmi_mdacc.h | 518 +++ include/linux/bmi/bmi_projector.h | 33 include/linux/bmi/bmi_sensor.h | 673 ++++ include/linux/bmi/bmi_vh.h | 135 include/linux/bmi/bmi_zb.h | 83 include/linux/mod_devicetable.h | 13 scripts/mod/file2alias.c | 20 102 files changed, 35212 insertions(+) --- git.orig/arch/arm/Kconfig +++ git/arch/arm/Kconfig @@ -1342,10 +1342,12 @@ source "drivers/regulator/Kconfig" source "drivers/uio/Kconfig" source "drivers/staging/Kconfig" +source "drivers/bmi/Kconfig" + if ARCH_OMAP source "drivers/cbus/Kconfig" endif endmenu --- git.orig/arch/arm/mach-omap2/board-omap3beagle.c +++ git/arch/arm/mach-omap2/board-omap3beagle.c @@ -23,10 +23,12 @@ #include #include #include #include + +#include #include #include #include #include @@ -404,10 +406,16 @@ static struct gpio_led gpio_leds[] = { { .name = "beagleboard::usr0", .default_trigger = "heartbeat", .gpio = 150, }, + /*{ + .name = "beagleboard::exp21", + .default_trigger = "heartbeat", + .gpio = 130, + }, + */ { .name = "beagleboard::usr1", .default_trigger = "mmc0", .gpio = 149, }, @@ -537,20 +545,54 @@ static void __init beagle_display_init(v } gpio_direction_output(beagle_display_data_dvi.panel_reset_gpio, 0); } + +static struct resource bmi_slot1_resources[] = { + [0] = { + .start = 161, + .flags = IORESOURCE_IRQ, + }, + [1] = { + .start = 134, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bmi_slot_devices[] = { + { + .name = "omap_bmi_slot", + .id = 0, + .num_resources = ARRAY_SIZE(bmi_slot1_resources), + .resource = bmi_slot1_resources, + }, +}; + + +static void omap_init_bmi_slots(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmi_slot_devices); i++) { + if (platform_device_register(&bmi_slot_devices[i]) < 0) + dev_err(&bmi_slot_devices[i].dev, + "Unable to register BMI slot\n"); + } +} + static struct omap_board_config_kernel omap3_beagle_config[] __initdata = { { OMAP_TAG_UART, &omap3_beagle_uart_config }, }; static struct platform_device *omap3_beagle_devices[] __initdata = { &beagle_dss_device, &leds_gpio, &keys_gpio, }; + static void __init omap3beagle_flash_init(void) { u8 cs = 0; u8 nandcs = GPMC_CS_NUM + 1; @@ -598,14 +640,24 @@ static void __init omap3_beagle_init(voi omap_cfg_reg(J25_34XX_GPIO170); omap3beagle_enc28j60_init(); + omap_cfg_reg(AG4_3530_GPIO134); + omap_cfg_reg(K26_34XX_GPIO161); + omap_cfg_reg(Y21_3530_GPIO156_OUT); + omap_cfg_reg(AF14_34XX_I2C3_SCL); + omap_cfg_reg(AG14_34XX_I2C3_SDA); + omap_cfg_reg(U21_3530_GPIO159_OUT); + gpio_direction_output(156, false); + gpio_direction_output(159, false); + // BMI Presence and Status usb_musb_init(); usb_ehci_init(); omap3beagle_flash_init(); beagle_display_init(); + omap_init_bmi_slots(); } static void __init omap3_beagle_map_io(void) { omap2_set_globals_343x(); --- git.orig/arch/arm/mach-omap2/mux.c +++ git/arch/arm/mach-omap2/mux.c @@ -480,14 +480,39 @@ MUX_CFG_34XX("AE6_34XX_GPIO141", 0x16e, OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) MUX_CFG_34XX("AF5_34XX_GPIO142", 0x170, OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) MUX_CFG_34XX("AE5_34XX_GPIO143", 0x172, OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("K26_34XX_GPIO161", 0x196, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) MUX_CFG_34XX("H19_34XX_GPIO164_OUT", 0x19c, OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_OUTPUT) MUX_CFG_34XX("J25_34XX_GPIO170", 0x1c6, OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) + +/*BeagleBoard/BUG-Hybrid specific GPIO stuff*/ + +MUX_CFG_34XX("AE2_3530_GPIO130", 0x158, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_OUTPUT) +/* +MUX_CFG_34XX("AG5_3530_GPIO131", 0x15A, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("AH5_3530_GPIO132", 0x15C, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("AH4_3530_GPIO133", 0x15E, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +*/ +MUX_CFG_34XX("AG4_3530_GPIO134", 0x160, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("AF4_3530_GPIO135", 0x162, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("Y21_3530_GPIO156_OUT", 0x18C, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_OUTPUT) +MUX_CFG_34XX("AA21_3530_GPIO157", 0x18E, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT) +MUX_CFG_34XX("U21_3530_GPIO159_OUT", 0x192, + OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_OUTPUT) }; #define OMAP34XX_PINS_SZ ARRAY_SIZE(omap34xx_pins) #else --- git.orig/arch/arm/plat-omap/include/mach/mux.h +++ git/arch/arm/plat-omap/include/mach/mux.h @@ -799,12 +799,19 @@ enum omap34xx_index { AE4_34XX_GPIO136_OUT, AF6_34XX_GPIO140_UP, AE6_34XX_GPIO141, AF5_34XX_GPIO142, AE5_34XX_GPIO143, + K26_34XX_GPIO161, H19_34XX_GPIO164_OUT, J25_34XX_GPIO170, + AE2_3530_GPIO130, + AG4_3530_GPIO134, + AF4_3530_GPIO135, + Y21_3530_GPIO156_OUT, + AA21_3530_GPIO157, + U21_3530_GPIO159_OUT }; struct omap_mux_cfg { struct pin_config *pins; unsigned long size; --- git.orig/drivers/Kconfig +++ git/drivers/Kconfig @@ -4,10 +4,12 @@ menu "Device Drivers" source "drivers/base/Kconfig" source "drivers/connector/Kconfig" +source "drivers/bmi/Kconfig" + source "drivers/mtd/Kconfig" source "drivers/of/Kconfig" source "drivers/parport/Kconfig" @@ -107,6 +109,7 @@ source "drivers/uio/Kconfig" source "drivers/xen/Kconfig" source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" + endmenu --- git.orig/drivers/Makefile +++ git/drivers/Makefile @@ -91,10 +91,11 @@ obj-y += lguest/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += idle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ +obj-$(CONFIG_BMI) += bmi/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ --- /dev/null +++ git/drivers/bmi/Kconfig @@ -0,0 +1,17 @@ +# +# BMI Infrastructure +# + +menuconfig BMI + tristate "BMI" + depends on I2C + default n + ---help--- + BMI bus infrastructure + +if BMI + +source drivers/bmi/slots/Kconfig +source drivers/bmi/pims/Kconfig + +endif # BMI --- /dev/null +++ git/drivers/bmi/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the bmi bus drivers. +# + +obj-$(CONFIG_BMI) += core/ +obj-$(CONFIG_BMI) += slots/ +obj-$(CONFIG_BMI) += pims/ + --- /dev/null +++ git/drivers/bmi/core/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for BMI subsystem core +# + +#bmicore-objs := core.o slot.o + +obj-$(CONFIG_BMI) += core.o driver.o slot.o eeprom.o --- /dev/null +++ git/drivers/bmi/core/core.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include +#include + + +static DEFINE_MUTEX(core_lock); + +static struct class *bmi_class; + + +struct class* bmi_get_class (void) +{ + return bmi_class; +}; +EXPORT_SYMBOL(bmi_get_class); + + +/** + * bmi_device_get - increments the reference count of the bmi device structure + * @dev: the device being referenced + * + * Each live reference to a device should be refcounted. + * + * Drivers for BMI devices should normally record such references in + * their probe() methods, when they bind to a device, and release + * them by calling bmi_dev_put(), in their disconnect() methods. + * + * A pointer to the device with the incremented reference counter is returned. + */ +struct bmi_device *bmi_dev_get(struct bmi_device *dev) +{ + if (dev) + get_device(&dev->dev); + return dev; +} + + +/** + * bmi_device_put - release a use of the bmi device structure + * @dev: device that's been disconnected + * + * Must be called when a user of a device is finished with it. When the last + * user of the device calls this function, the memory of the device is freed. + */ +void bmi_dev_put(struct bmi_device *dev) +{ + if (dev) + put_device(&dev->dev); +} + + +/** + * bmi_match_one_id - Tell if a BMI device structure has a matching + * BMI device id structure + * @id: single BMI device id structure to match + * @bdev: the BMI device structure to match against + * + * Returns the matching bmi_device_id structure or %NULL if there is no match. + */ + +static const struct bmi_device_id *bmi_match_one_id(const struct bmi_device_id *id, + const struct bmi_device *bdev) +{ + if ((id->vendor == bdev->vendor) && + (id->product == bdev->product) && + ((id->revision == bdev->revision) || (id->revision == BMI_ANY))) + return id; + return NULL; +} + + +/** + * bmi_match_id - See if a BMI device matches a given bmi_device_id table + * @ids: array of BMI device id structures to search in + * @bdev: the BMI device structure to match against. + * + * Used by a driver to check whether a BMI device present in the + * system is in its list of supported devices. Returns the matching + * bmi_device_id structure or %NULL if there is no match. + * + */ + + +const struct bmi_device_id *bmi_match_id(const struct bmi_device_id *ids, + struct bmi_device *bdev) +{ + if (ids) { + while (ids->vendor) { + if (bmi_match_one_id(ids, bdev)) + return ids; + ids++; + } + } + return NULL; +} + +/** + * bmi_device_match - Tell if a BMI device structure has a matching BMI device id structure + * @dev: the BMI device structure to match against + * @drv: the device driver to search for matching PCI device id structures + * + * Used by a driver to check whether a BMI device present in the + * system is in its list of supported devices. Returns the matching + * bmi_device_id structure or %NULL if there is no match. + */ + + +static int bmi_device_match(struct device *dev, struct device_driver *driver) +{ + struct bmi_device *bmi_dev = to_bmi_device(dev); + struct bmi_driver *bmi_drv = to_bmi_driver(driver); + const struct bmi_device_id *found_id; + + found_id = bmi_match_id(bmi_drv->id_table, bmi_dev); + + if (found_id) + return 1; + + printk(KERN_INFO "BMI: No matching Driver..."); + return 0; +} + +/* + * Uevent Generation for hotplug + */ + +static int bmi_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct bmi_device *bdev = to_bmi_device(dev); + + if (!dev) + return -ENODEV; + + if (add_uevent_var(env, "BMIBUS_SLOT=%01X", bdev->slot->slotnum)) { + return -ENOMEM; + } + if (add_uevent_var(env, "BMIBUS_VENDOR=%04X", bdev->vendor)) { + return -ENOMEM; + } + if (add_uevent_var(env, "BMIBUS_PRODUCT=%04X", bdev->product)) { + return -ENOMEM; + } + if (add_uevent_var(env, "BMIBUS_REV=%04X", bdev->revision)) { + return -ENOMEM; + } + if (add_uevent_var(env, "MODALIAS=bmi:v%04Xp%04Xr%04X", + bdev->vendor, bdev->product, + bdev->revision)) { + return -ENOMEM; + } + return 0; +} + + +struct bmi_device *bmi_alloc_dev(struct bmi_slot *slot) +{ + struct bmi_device *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "BMI: Couldn't Allocate bmi_device structure...\n"); + return NULL; + } + + device_initialize(&dev->dev); + dev->dev.bus = &bmi_bus_type; + dev_set_name(&dev->dev, "bmi-dev-%d",slot->slotnum); + dev->dev.parent = &slot->slotdev; + dev->slot = slot; + + return dev; +} + + + +/** + * __bmi_probe() + * @drv: driver to call to check if it wants the BMI device + * @bmi_dev: BMI device being probed + * + * returns 0 on success, else error. + * side-effect: bmi_dev->driver is set to drv when drv claims bmi_dev. + */ +static int +__bmi_probe(struct bmi_driver *driver, struct bmi_device *bmi_dev) +{ + int error = 0; + + if (!bmi_dev->driver && driver->probe) { + + error = driver->probe(bmi_dev); + if (error >= 0) { + // bmi_device -> bmi_driver (bmi-bus level ) + bmi_dev->driver = driver; + error = 0; + } + } + return error; +} + +static int bmi_device_probe (struct device *dev) +{ + int error = 0; + struct bmi_driver *drv; + struct bmi_device *bmi_dev; + + //By this time, we have already been match()ed against a driver. + + // device -> device_driver. (driver-core level) + + drv = to_bmi_driver(dev->driver); + bmi_dev = to_bmi_device(dev); + + + bmi_dev_get(bmi_dev); + + error = __bmi_probe(drv, bmi_dev); + if (error) + bmi_dev_put(bmi_dev); + else + kobject_uevent(&dev->kobj, KOBJ_ADD); + + return error; +} + + + +static int bmi_device_remove (struct device *dev) +{ + struct bmi_device * bmi_dev; + struct bmi_driver * driver; + + bmi_dev = to_bmi_device(dev); + driver = bmi_dev->driver; + + if (driver) { + if (driver->remove) + driver->remove(bmi_dev); + bmi_dev->driver = NULL; + } + + kobject_uevent(&dev->kobj, KOBJ_REMOVE); + bmi_dev_put(bmi_dev); + return 0; +} + +static void bmi_device_shutdown(struct device * dev) +{ + return; +} + +static int bmi_device_suspend (struct device * dev, pm_message_t state) +{ + return -1; +} + +static int bmi_device_suspend_late (struct device * dev, pm_message_t state) +{ + return -1; +} + +static int bmi_device_resume_early (struct device * dev) +{ + return -1; +} + +static int bmi_device_resume (struct device * dev) +{ + return -1; +} + + + +struct bus_type bmi_bus_type = { + .name = "bmi", + .match = bmi_device_match, + .uevent = bmi_device_uevent, + .probe = bmi_device_probe, + .remove = bmi_device_remove, + .shutdown = bmi_device_shutdown, + .suspend = bmi_device_suspend, + .suspend_late = bmi_device_suspend_late, + .resume_early = bmi_device_resume_early, + .resume = bmi_device_resume, +}; + +static int __init bmi_init(void) +{ + int ret = 0; + + ret = bus_register(&bmi_bus_type); + if (ret) { + printk(KERN_ERR "BMI: (bmi_init) - Bus registration failed...\n"); + return ret; + } + + // ret = class_register(&bmi_class); + bmi_class = class_create(THIS_MODULE, "bmi"); + if (ret) { + printk(KERN_ERR "BMI: (bmi_init) - Failed to register BMI Class...\n"); + bmi_class = NULL; + bus_unregister(&bmi_bus_type); + } + return ret; +} + +static void __exit bmi_cleanup(void) +{ + bmi_class = NULL; + bus_unregister(&bmi_bus_type); +} + +//subsys_initcall(bmi_init); +module_init(bmi_init); +module_exit(bmi_cleanup); --- /dev/null +++ git/drivers/bmi/core/device.c @@ -0,0 +1,35 @@ + + +// bmi_device accessors +static inline int bmi_device_get_status_irq (struct bmi_device *bdev) +{ + return (bdev->slot->status_irq); +} + +static inline int bmi_device_get_present_irq (struct bmi_device *bdev) +{ + return (bdev->slot->present_irq); +} + +static inline struct i2c_adapter* bmi_device_get_i2c_adapter (struct bmi_device *bdev) +{ + return (&bdev->slot->adap); +} + +static inline int bmi_device_get_slot (struct bmi_device *bdev) +{ + return (bdev->slot->slotnum); +} + +int bmi_device_present (struct bmi_device *bdev); +struct bmi_device *bmi_device_get(struct bmi_device *dev); +void bmi_device_put(struct bmi_device *dev); + +int bmi_device_read_inventory_eeprom ( struct bmi_device *bdev ); +int bmi_device_init ( struct bmi_device *bdev, struct bmi_info *info, struct bus_type *bmi_bus_type); +void bmi_device_cleanup( struct bmi_device* bdev); +int bmi_device_i2c_setup( struct bmi_device *bdev); +void bmi_device_i2c_cleanup( struct bmi_device* bdev); +int bmi_device_spi_setup( struct bmi_device *bdev, u32 speed, u8 mode, u8 bits_per_word); +void bmi_device_spi_cleanup( struct bmi_device* bdev); +struct spi_device *bmi_device_get_spi( struct bmi_device *bdev); --- /dev/null +++ git/drivers/bmi/core/driver.c @@ -0,0 +1,27 @@ +#include + +int __bmi_register_driver(struct bmi_driver *drv, struct module *owner) +{ + int error; + + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &bmi_bus_type; + drv->driver.owner = owner; + + /* register with core */ + error = driver_register(&drv->driver); + + return error; +} + + +void +bmi_unregister_driver(struct bmi_driver *drv) +{ + driver_unregister(&drv->driver); +} + +EXPORT_SYMBOL(__bmi_register_driver); +EXPORT_SYMBOL(bmi_unregister_driver); + --- /dev/null +++ git/drivers/bmi/core/eeprom.c @@ -0,0 +1,32 @@ +#include +#include + + +static inline __u8 bmi_eeprom_checksum (struct bmi_eeprom_data *raw) +{ + int i; + __u8 sum = 0; + __u8 *buf = (__u8*)raw; + + for (i = 0; i < (sizeof (struct bmi_eeprom_data) - 1); i++) { + sum ^= *buf++; + } + return sum; +} + + +int bmi_eeprom_checksum_validate (struct bmi_eeprom_data *raw) +{ + int ret = 0; + u8 calcsum; + + calcsum = bmi_eeprom_checksum (raw); + + if (calcsum != raw->checksum) { + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + + --- /dev/null +++ git/drivers/bmi/core/slot.c @@ -0,0 +1,469 @@ +/* Matt Isaacs - Kick ass platform independant BMI implementation */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(slot_lock); +static DEFINE_IDR(bmi_slot_idr); + + +//#include "slot.h" + +/* +struct slot_driver { + const char *description; + + irq_return_t (*irq) (struct bmi_slot); + int (*start) (struct bmi_slot); + int (*stop) (struct bmi_slot); +} +*/ + +static struct task_struct *kslotd_task; + +static DEFINE_SPINLOCK(slot_event_lock); +static LIST_HEAD(slot_event_list); +static DECLARE_WAIT_QUEUE_HEAD(kslotd_wait); + +static struct i2c_board_info at24c02_info = { + I2C_BOARD_INFO("at24c02", 0XA0 >> 1), +}; + +static void bmi_slot_work_handler(struct work_struct * work); + +struct bmi_slot* bmi_get_slot(int slotnum) +{ + struct bmi_slot *slot; + + mutex_lock(&slot_lock); + slot = (struct bmi_slot*)idr_find(&bmi_slot_idr, slotnum); + if (slot && !try_module_get(slot->owner)) + slot = NULL; + + mutex_unlock(&slot_lock); + + return slot; +} + +void bmi_slot_power_on (int num) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return; + } + + if (slot->actions->power_on) + slot->actions->power_on(slot); + else + printk(KERN_INFO "BMI: Slot %d power is always on...\n", num); + return; +} + +void bmi_slot_power_off (int num) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return; + } + + if (slot->actions->power_off) + slot->actions->power_off(slot); + else + printk(KERN_INFO "BMI: Slot %d power is always on...\n", num); + return; +} + +void bmi_slot_gpio_configure (int num, int gpio) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return; + } + + if (slot->actions->gpio_config) + slot->actions->gpio_config(slot, gpio); + else + printk(KERN_INFO "BMI: Slot GPIO not configurable...\n"); + return; + +} +EXPORT_SYMBOL(bmi_slot_gpio_configure); + +int bmi_slot_gpio_get(int num) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return -ENODEV; + } + + if (slot->actions->gpio_get) + return slot->actions->gpio_get(slot); + + printk(KERN_INFO "BMI: Slot GPIO not writable...\n"); + return -EIO; +} +EXPORT_SYMBOL(bmi_slot_gpio_get); + +void bmi_slot_gpio_set(int num, int data) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return; + } + + if (slot->actions->gpio_set) + slot->actions->gpio_set(slot, data); + else + printk(KERN_INFO "BMI: Slot GPIO not writable...\n"); + return; +} +EXPORT_SYMBOL(bmi_slot_gpio_set); + +void bmi_slot_gpio_write_bit(int num, int gpio, int data) +{ + return; +} + +int bmi_slot_gpio_read_bit (int num, int gpio) +{ + int gpdat; + int bit; + + gpdat = bmi_slot_gpio_get(num); + bit = (gpdat & (1 << gpio)) ? 1 : 0; + return bit; +} + + +// NOTE: When a plug-in module is removed, the gpios should be returned to inputs. +// All requested slot resourece should be released. +// The slot should be powered down. + +void bmi_slot_gpio_configure_all_as_inputs (int slot) +{ + return; +} + + +void bmi_slot_uart_enable (int num) +{ + struct bmi_slot *slot = bmi_get_slot(num); + + if (!slot) { + printk(KERN_ERR "BMI: Slot %d doesn't exist...\n", num); + return; + } + + if (slot->actions->uart_enable) + return slot->actions->uart_enable(slot); + + printk(KERN_INFO "BMI: UART always enabled...\n"); + return; +} +EXPORT_SYMBOL(bmi_slot_uart_enable); + +void bmi_slot_uart_disable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_uart_disable); + +void bmi_slot_spi_enable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_spi_enable); + +void bmi_slot_spi_disable (int num) +{ + return; +} +EXPORT_SYMBOL(bmi_slot_spi_disable); + +void bmi_slot_audio_enable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_audio_enable); + +void bmi_slot_audio_disable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_audio_disable); + +void bmi_slot_battery_enable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_battery_enable); + +void bmi_slot_battery_disable (int num) +{ + + return; +} +EXPORT_SYMBOL(bmi_slot_battery_disable); + +int bmi_slot_module_present (int num) +{ + struct bmi_slot *slot = bmi_get_slot(num); + // slot->actions->gpio_set + if (slot->actions->present != NULL) + return slot->actions->present(slot); + else + printk(KERN_INFO "BMI: Slot Driver incomplete. No presence detection...\n"); + return 0; +} + +int bmi_slot_read_eeprom(struct bmi_slot *slot, u8* data) +{ + unsigned char i = 0; + int ret; + + if (slot->eeprom == NULL) { + printk(KERN_INFO "Can't get eeprom client...\n"); + ret = -EIO; + } + else { + ret = i2c_master_send(slot->eeprom, &i, 1); + if (ret == 1) + ret = i2c_master_recv(slot->eeprom, data, sizeof(struct bmi_eeprom_data)); + } + return ret; +} + +int bmi_slot_status_irq_state (int slot) +{ + int state = 0; + return state; +} + + +#define DEBOUNCE_DELAY msecs_to_jiffies(1000) + +static irqreturn_t bmi_slot_irq_handler(int irq, void *dev_id) +{ + struct bmi_slot *slot = dev_id; + + disable_irq_nosync(irq); + printk(KERN_INFO " BMI: IRQ Triggered on slot: %d\n", slot->slotnum); + schedule_delayed_work(&slot->work, DEBOUNCE_DELAY); + return IRQ_HANDLED; +} + +static void bmi_slot_work_handler(struct work_struct * work) +{ + struct bmi_slot *slot; + struct bmi_device *bdev; + int ret; + struct bmi_eeprom_data data; + unsigned char* cdat; + + slot = work_to_slot(work); + + mutex_lock(&slot->pres_mutex); + if (bmi_slot_module_present(slot->slotnum)) { + if (!slot->present) { + slot->present = 1; + slot->eeprom = i2c_new_device(slot->adap, &at24c02_info); + + ret = bmi_slot_read_eeprom(slot, (u8*)&data); + + if (ret < 0) + { + printk(KERN_INFO "BMI: EEPROM Trouble on Slot %d...\n",slot->slotnum); + + goto irqenbl; + } + //Testing stuff here...get rid of this... + else + printk(KERN_INFO "BMI: EEPROM Found...\n"); + cdat = (char*)&data; + /*for (i = 0; i < 20; i++) + printk(KERN_INFO "0x%x\n", cdat[i]);*/ + printk(KERN_INFO "SLOTS: Vendor: 0x%x\n",(data.vendor_msb<<8) | (data.vendor_lsb)); + printk(KERN_INFO "SLOTS: Product 0x%x\n",(data.product_msb<<8) | (data.product_lsb)); + printk(KERN_INFO "SLOTS: Revision 0x%x\n",(data.revision_msb<<8) | (data.revision_lsb)); + + //Do new device allocation and hand it over to BMI... + bdev = bmi_alloc_dev(slot); + bdev->vendor = (data.vendor_msb<<8) | (data.vendor_lsb); + bdev->product = (data.product_msb<<8) | (data.product_lsb); + bdev->revision = (data.revision_msb<<8) | (data.revision_lsb); + + //Report module plugin so that udev can load appropriate drivers + //kobject_uevent (&bdev->dev.kobj, KOBJ_ADD); + ret = device_add(&bdev->dev); + if (ret) { + printk(KERN_ERR "SLOTS: Failed to add device...%d\n",ret); + goto irqenbl; //TODO: Memory allocated for by bmi_alloc_dev + } + slot->bdev = bdev; + /* + ret = device_attach(&bdev->dev); + if (ret != 1) { + printk(KERN_ERR "SLOTS: Failed to attach device...%d\n",ret); + goto irqenbl; //TODO: Memory allocated for by bmi_alloc_dev must be freed + } + */ + } + else + //spurious insertion event.. + printk(KERN_INFO "SLOTS: Spurious insertion on Slot %d...\n",slot->slotnum); + } + else { + if (slot->present) { + slot->present = 0; + printk(KERN_INFO "BMI: Module removed from slot %d...\n", slot->slotnum); + if (slot->bdev == NULL) { + printk(KERN_ERR "SLOTS: BMI Device NULL...\n"); + goto del_eeprom; + } + //Call BMI device removal stuff here... + device_del(&slot->bdev->dev); + goto del_eeprom; + } + } + irqenbl: + mutex_unlock(&slot->pres_mutex); + enable_irq(slot->present_irq); + return; + del_eeprom: + i2c_unregister_device(slot->eeprom); + slot->bdev = NULL; + slot->eeprom = NULL; + goto irqenbl; + +} + +static int bmi_register_slot(struct bmi_slot *slot) +{ + int res = 0; + struct class *class; + + // mutex_init(&slot->state_lock); + if (unlikely(WARN_ON(!bmi_bus_type.p))) + return -EAGAIN; + if (slot->actions == NULL) { + printk(KERN_INFO "SLOTS: No Slot actions defined...\n"); + goto unlist; + } + mutex_init(&slot->pres_mutex); + mutex_lock(&slot_lock); + + if (slot->slotdev.parent == NULL) { + slot->slotdev.parent = &platform_bus; + //debug message here + } + + dev_set_name(&slot->slotdev, "bmi-%d", slot->slotnum); + + class = bmi_get_class(); + if (class == NULL) { + printk(KERN_ERR "BMI Class doesn't exist...\n"); + goto unlist; + } + res = device_register(&slot->slotdev); + if (res) { + printk(KERN_ERR "SLOT: Couldn't register slot... %d\n",res); + goto unlist; + //quit + } + + //Request IRQ + INIT_DELAYED_WORK(&slot->work, bmi_slot_work_handler); + + printk(KERN_ERR "SLOT: Requesting IRQ...\n"); + res = request_irq(slot->present_irq, bmi_slot_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING , slot->name, (void*)slot); + + if (res) { + printk(KERN_ERR "SLOT: IRQ Request failed...\n"); + goto unlist; + } + + unlock: + mutex_unlock(&slot_lock); + return res; + unlist: + idr_remove(&bmi_slot_idr, slot->slotnum); + goto unlock; + +} + + +int bmi_add_slot(struct bmi_slot *slot) +{ + int slotnum = 0; + int res = 0; + + retry: + if (idr_pre_get(&bmi_slot_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + mutex_lock(&slot_lock); + + res = idr_get_new_above(&bmi_slot_idr, slot, 0, &slotnum); + mutex_unlock(&slot_lock); + if (res < 0) { + if (res == -EAGAIN) + goto retry; + return res; + } + slot->slotnum = slotnum; + return bmi_register_slot(slot); +} +EXPORT_SYMBOL(bmi_add_slot); + + +int bmi_del_slot(struct bmi_slot *slot) +{ + int res = 0; + + mutex_lock(&slot_lock); + if (idr_find(&bmi_slot_idr, slot->slotnum)) { + printk(KERN_ERR "BMI: Attempting to delete unregistered slot...\n"); + res = -EINVAL; + goto unlock; + } + + disable_irq_nosync(slot->present_irq); + free_irq(slot->present_irq, slot); + device_unregister(&slot->slotdev); + idr_remove(&bmi_slot_idr, slot->slotnum); + memset(&slot->slotdev, 0, sizeof(slot->slotdev)); + + unlock: + mutex_unlock(&slot_lock); + return res; +} +EXPORT_SYMBOL(bmi_del_slot); + --- /dev/null +++ git/drivers/bmi/pims/Kconfig @@ -0,0 +1,104 @@ +# +# BMI PIMS +# + +config BMI_PIMS + tristate "BMI_PIMS" + default n + ---help--- + BMI plug-in module support + + This driver must be built as a module. + +menu "BMI PIMS" + +config BUG_FACTORY_TEST + tristate "BUG_FACTORY_TEST" + depends on BMI_PIMS + default n + ---help--- + BMI FACTORY Test plug-in module + + This driver can also be built as a module. + +config BMI_GPS + tristate "BMI_GPS" + depends on BMI_PIMS + default n + ---help--- + BMI GPS plug-in module + + This driver can also be built as a module. + +source "drivers/bmi/pims/mdacc/Kconfig" + +config VIDEO_BMI_LCD + tristate "BMI Bus LCD Module support" + depends on FB_MXC && MACH_BUG + default n + ---help--- + This is the BMI bus driver for the LCD Plug-In Module. + +config VIDEO_BMI_LCD_S320X240 + tristate "BMI support for Sharp 320x240 module" + depends on FB_MXC && MACH_BUG && VIDEO_BMI_LCD + default n + ---help--- + This is the BMI LCD driver for the Sharp 320x240 LCD Plug-In Module. + +config VIDEO_BMI_PROJECTOR + tristate "BMI Bus PROJECTOR Module support" + depends on FB_MXC_PROJECTOR && MACH_BUG + default n + ---help--- + This is the BMI bus driver for the PROJECTOR Plug-In Module. + +config BMI_AUDIO + tristate "BMI Bus Audio Module support" + depends on BMI_PIMS + default n + ---help--- + This is the BMI bus driver for the Audio Plug-In Module. + + Select M to make a kernel-loadable module. + +#source "drivers/bmi/pims/camera/Kconfig" + +config BMI_VH + tristate "BMI von Hippel Module support" + depends on BMI_PIMS + default n + ---help--- + BMI von Hippel plug-in module + + This driver can also be built as a module. + +config BMI_SENSOR + tristate "BMI Sensor Module support" + depends on BMI_PIMS + default n + ---help--- + BMI Sensor plug-in module + + This driver can also be built as a module. + +config BMI_ZB + tristate "BMI ZigBee Module support" + depends on BMI_PIMS + default n + ---help--- + BMI ZigBee plug-in module + + This driver can also be built as a module. + +config BMI_GSM + tristate "BMI GSM/UMTS Module support" + depends on BMI_PIMS + default n + ---help--- + BMI von Hippel plug-in module + + This driver can also be built as a module. + +endmenu + --- /dev/null +++ git/drivers/bmi/pims/Makefile @@ -0,0 +1,17 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BUG_FACTORY_TEST) += factory_test/ +obj-$(CONFIG_BMI_GPS) += gps/ +obj-$(CONFIG_BMI_MDACC) += mdacc/ +obj-$(CONFIG_VIDEO_BMI_LCD) += lcd/ +obj-$(CONFIG_BMI_CAMERA) += camera/ +obj-$(CONFIG_BMI_AUDIO) += sound/ +obj-$(CONFIG_BMI_VH) += vonhippel/ +obj-$(CONFIG_BMI_GSM) += gsm/ +obj-$(CONFIG_BMI_SENSOR) += sensor/ +obj-$(CONFIG_VIDEO_BMI_PROJECTOR) += projector/ +obj-$(CONFIG_BMI_ZB) += zb/ + + --- /dev/null +++ git/drivers/bmi/pims/camera/Kconfig @@ -0,0 +1,23 @@ +config BMI_CAMERA + tristate "BMI Camera" + depends on MACH_BUG + default n + ---help--- + This is the BMI Camera driver. + +choice + prompt "Select Camera" + depends on (BMI_CAMERA) + +config BMI_CAMERA_VS6624 + tristate "ST VS6624 camera support" + ---help--- + If you plan to use the ST VS6624 Camera with your BUG system, say Y here. + +config BMI_CAMERA_OV2640 + tristate "Omnivision OV2640 camera support" + ---help--- + If you plan to use the Omnivision OV2640 Camera with your BUG system, say Y here. +endchoice + + --- /dev/null +++ git/drivers/bmi/pims/camera/Makefile @@ -0,0 +1,12 @@ +#ifeq ($(CONFIG_MACH_BUG),y) +# obj-$(CONFIG_BMI_CAMERA) += bug_v4l2_capture.o +#endif + +obj-$(CONFIG_BMI_CAMERA) += bug_camera.o + +bmi_camera_vs6624-objs := bmi_vs6624.o vs6624_access.o +obj-$(CONFIG_BMI_CAMERA_VS6624) += bmi_camera_vs6624.o + +bmi_camera_ov2640-objs := bmi_ov2640.o ov2640.o +obj-$(CONFIG_BMI_CAMERA_OV2640) += bmi_camera_ov2640.o + --- /dev/null +++ git/drivers/bmi/pims/camera/bmi_ov2640.c @@ -0,0 +1,929 @@ +#include +#include +#include +#include +#include +#include +#include "bug_camera.h" +#include "ov2640.h" + +#define BMI_OV2640_VERSION "1.0" + +// BMI device ID table +static struct bmi_device_id bmi_ov2640_tbl[] = +{ + { .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_CAMERA_OV2640, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_ov2640_tbl); + +int bmi_ov2640_probe(struct bmi_device *bdev); +void bmi_ov2640_remove(struct bmi_device *bdev); +int bmi_ov2640_suspend(struct bmi_device *bdev); +int bmi_ov2640_resume(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_ov2640_driver = +{ + .name = "bmi_ov2640", + .id_table = bmi_ov2640_tbl, + .probe = bmi_ov2640_probe, + .remove = bmi_ov2640_remove, + }; + + +struct bmi_ov2640 { + struct bmi_device *bdev; + struct bmi_cam bcam; + unsigned int shutter; // shutter button save state + unsigned int zoomin; // zoomin button save state + unsigned int zoomout; // zoom out button save state + unsigned int flash; // state of camera FLASH + int irq; + struct input_dev *idev; + struct work_struct work; + +}; + +#define work_to_bmi_ov2640(w) container_of(w, struct bmi_ov2640, work) + +/* IOX Bits Definitions + + IOX Bit 7 CAM_RST* Output: 0 = Reset, 1 = Normal Operation + IOX Bit 6 GRNLED Output: 0 = Green LED on, 1 = Green LED off + IOX Bit 5 SER_SYNC Output: 0 = Normal Operation, 1 = send SYNC + IOX Bit 4 FLASH_TORCH* Output: 0 = low beam, 1 = high beam + IOX Bit 3 STROBE_R I/O, not used. + IOX Bit 2 ZOOMB Input: Zoom OUT Button: 0 = depressed, 1 = released + IOX Bit 1 ZOOMA Input: Zoom IN Button 0 = depressed, 1 = released + IOX Bit 0 GPIO0_SHUTTER* Input: Shutter Button 0 = depressed, 1 = released + + GPIO Bits Definitions + + GPIO Bit 3 REDLED Output: 0 = Red LED on, 1 = Red LED off + GPIO Bit 2 FLASHON Output: 0 = Flash LED off, 1 = Flash LED on + GPIO Bit 1 SER_RST* Output: 0 = Serializer Reset, 1 = Normal Operation + GPIO Bit 0 GPIO0_SHUTTER* Input: Shutter Button: 0 = depressed, 1 = released + + +*/ + + + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + + +// read byte from I2C IO expander + +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer failed\n"); + ret = -1; + } + return ret; +} + + +// write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +/* + * Input interrupt handler and support routines + */ + + +// work handler +void bmi_ov2640_buttons_work(struct work_struct * work) +{ + struct i2c_adapter *adap; + struct bmi_ov2640 *pim = work_to_bmi_ov2640(work); + + unsigned char iox_data; + unsigned int test_value; + int sync_flag = 0; + int err; + + + // avoid i2c i/o if camera hardware not present. + + if (bmi_device_present (pim->bdev) == 0) { + goto exit; + } + + adap = bmi_device_get_i2c_adapter (pim->bdev); + + + // read IOX data input + err = ReadByte_IOX (adap, IOX_INPUT_REG, &iox_data); + if (err) { + goto exit; + } + + // zoom in button + test_value = !((iox_data & 0x2) >> 1); + if (test_value != pim->zoomin) { + pim->zoomin = test_value; + input_report_key (pim->idev, BN_ZOOMIN, test_value); + sync_flag = 1; + } + + + // zoom out button + test_value = !((iox_data & 0x4) >> 2); + if (test_value != pim->zoomout) { + pim->zoomout = test_value; + input_report_key (pim->idev, BN_ZOOMOUT, test_value); + sync_flag = 1; + } + + if ( sync_flag ) { + input_sync (pim->idev); + } +exit: + enable_irq (pim->irq); + return; + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + struct bmi_ov2640 *pim = dummy; + unsigned int test_value; + + int slot; + + disable_irq_nosync(irq); + + slot = bmi_device_get_slot(pim->bdev); + + + // shutter button on GPIO + + test_value = !(bmi_read_gpio_data_reg (slot) & 0x1); + + if (!test_value == pim->shutter) { + pim->shutter = test_value; + input_report_key (pim->idev, BN_SHUTTER, test_value); + input_sync (pim->idev); + } + + // other buttons on I2C IOX + schedule_work (&pim->work); + return IRQ_HANDLED; +} + +/* + * control functions + */ + + +// configure IOX IO and states +void configure_IOX(struct bmi_ov2640 *cam) +{ + struct i2c_adapter *adap; + + adap = bmi_device_get_i2c_adapter (cam->bdev); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, 0xC0); // CAMRST* = 1, GRNLED = Off + WriteByte_IOX (adap, IOX_CONTROL, 0x0F); // IOX[7:4]=OUT, IOX[3:0]=IN + return; + +} + +// configure GPIO IO and states +void configure_GPIO(struct bmi_ov2640 *cam) +{ + // set states before turning on outputs + + int slot; + + + slot = bmi_device_get_slot (cam->bdev); + + bmi_set_module_gpio_data (slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data (slot, 2, 0); // Flash LED=OFF + bmi_set_module_gpio_data (slot, 1, 0); // SER_RST=0 + + // configure direction + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); // SHUTTER + + // This is needed. + bmi_set_module_gpio_data (slot, 2, 0); // Flash LED off. + return; + +} + +// deconfigure IOX and GPIO +void deconfigure_module(struct bmi_ov2640 *cam) +{ + int slot; + struct i2c_adapter *adap; + + slot = bmi_device_get_slot (cam->bdev); + adap = bmi_device_get_i2c_adapter (cam->bdev); + + + if ( bmi_device_present (cam->bdev) ) { + WriteByte_IOX (adap, IOX_CONTROL, 0xFF); + } + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); +} + + +// configure serializer on plug-in module +void configure_serializer(struct bmi_ov2640 *cam) +{ + int slot = bmi_device_get_slot (cam->bdev); + bmi_set_module_gpio_data (slot, 1, 1); // SER_RST=1 +} + +void deconfigure_serializer(struct bmi_ov2640 *cam) +{ + int slot = bmi_device_get_slot (cam->bdev); + bmi_set_module_gpio_data (slot, 1, 0); // SER_RST=0 +} + +void enable_camera(struct bmi_ov2640 *cam) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + + adap = bmi_device_get_i2c_adapter (cam->bdev); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + + iox_data |= (0x80); //Set CAM_RST* to 1. + + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + return; +} + +// disable camera on plug-in module +void disable_camera(struct bmi_ov2640 *cam) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + + if (cam == NULL) + { + printk(KERN_INFO "bmi_camera_ov2640: disable_camera: NULL Pointer on cam\n"); + return; + } + if (cam->bdev == NULL) + { + printk(KERN_INFO "bmi_camera_ov2640: disable_camera: NULL Pointer on cam->bdev\n"); + return; + } + adap = bmi_device_get_i2c_adapter (cam->bdev); + + if ( bmi_device_present(cam->bdev) ) { + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + + iox_data &= ~(0x80); //Set CAM_RST* to 0; + + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + } + return; +} + +// generate sync +void generate_camera_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + udelay(20); // 60 MHz * 1024 = ~17 us sync time + return; +} + +void set_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + return; +} + +void clear_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + return; +} + + +// check serializer lock +int check_camera_lock(void) +{ + return bmi_sensor_lock_status(); +} + +void bmi_ov2640_set_color(struct bmi_cam *cam, int bright, int saturation, int red, int green, int blue) +{ + + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + adap = &bmi_ov2640->bdev->adap; + +// ov2640_set_color (adap, bright, saturation, red, green, blue); + printk (KERN_ERR "bmi_ov2640_set_color() - NOT IMPLEMENTED.\n"); + return; + +} + +void bmi_ov2640_get_color(struct bmi_cam *cam, int *bright, int *saturation, int *red, int *green, int *blue) +{ + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + adap = &bmi_ov2640->bdev->adap; + +// ov2640_get_color (adap, bright, saturation, red, green, blue); + printk (KERN_ERR "bmi_ov2640_get_color() - NOT IMPLEMENTED.\n"); + return; +} + + + + +void bmi_ov2640_set_ae_mode (struct bmi_cam *cam, int ae_mode) +{ + printk (KERN_ERR "bmi_ov2640_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +void bmi_ov2640_get_ae_mode (struct bmi_cam *cam, int *ae_mode) +{ + printk (KERN_ERR "bmi_ov2640_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +sensor_interface * bmi_ov2640_config (struct bmi_cam *cam, int *frame_rate, int high_quality) +{ + + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + int i; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + adap = &bmi_ov2640->bdev->adap; + + //Bring up the serial link + + bmi_sensor_active(1); + configure_serializer (bmi_ov2640); + + adap = &bmi_ov2640->bdev->adap; + set_sync (adap); + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + break; + } + else { + printk(KERN_ERR "bmi_ov2640_config() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync(adap); + + + if(!check_camera_lock()) { + printk(KERN_ERR "bmi_ov2640_config(): camera serializer NOT LOCKED\n"); + } + + return &cam->interface; + +} + + +sensor_interface * bmi_ov2640_reset (struct bmi_cam *cam) +{ + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + + + adap = bmi_device_get_i2c_adapter (bmi_ov2640->bdev); + + //disable the serial link + + deconfigure_serializer (bmi_ov2640); + bmi_sensor_inactive(); + + return &cam->interface; +} + +int bmi_ov2640_activate (struct bmi_cam *cam, struct input_dev *idev) +{ + int rc = 0; + int i; + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + + + //bmi_ov2640 struct fields + bmi_ov2640->idev = idev; + + bmi_ov2640->shutter = 0; + bmi_ov2640->zoomin = 0; + bmi_ov2640->zoomout = 0; + bmi_ov2640->flash = 0; + + + // install button irq_handler + if (request_irq(bmi_ov2640->irq, &module_irq_handler, 0, "bmi_cam_button", bmi_ov2640)) { + printk (KERN_ERR + "bmi_ov2640_activate() - request_irq (irq = %d) failed.\n", + bmi_ov2640->irq); + + rc = -EBUSY; + goto exit; + } + + //Activate serial link + bmi_sensor_active(1); // rising edge clock + configure_serializer (bmi_ov2640); + + adap = &bmi_ov2640->bdev->adap; + set_sync (adap); + + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + break; + } + else { + printk(KERN_ERR "bmi_ov2640_activate() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + +exit: + return rc; +} + +int bmi_ov2640_deactivate (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + + //De-activate serial link + deconfigure_serializer (bmi_ov2640); + bmi_sensor_inactive(); + + //uninstall button irq_handler + free_irq(bmi_ov2640->irq, bmi_ov2640); + return 0; +} + + +void bmi_ov2640_link_enable (struct bmi_cam *cam) +{ + int i; + struct i2c_adapter *adap; + struct bmi_ov2640 *bmi_ov2640; + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + + //Activate serial link + bmi_sensor_active(1); // rising edge clock + configure_serializer (bmi_ov2640); + + adap = bmi_device_get_i2c_adapter (bmi_ov2640->bdev); + + set_sync (adap); + + + //REWORK: Speed this up. (shorten delay) + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + break; + } + else { + printk(KERN_ERR "bmi_ov2640_activate() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + return; +} + + +void bmi_ov2640_link_disable (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + + + //De-activate serial link + deconfigure_serializer (bmi_ov2640); + bmi_sensor_inactive(); + + return; +} + + +int bmi_ov2640_flash_led_off (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + int slot; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 2, 0); // Flash LED off. + return 0; +} + +int bmi_ov2640_flash_led_on (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + int slot; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 2, 1); // Flash LED on. + return 0; +} + +int bmi_ov2640_flash_high_beam (struct bmi_cam *cam) +{ + unsigned char data; + struct bmi_ov2640 *bmi_ov2640; + struct i2c_adapter *adap; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + adap = bmi_device_get_i2c_adapter (bmi_ov2640->bdev); + + ReadByte_IOX (adap, IOX_INPUT_REG, &data); + data |= 0x10; //High Beam + WriteByte_IOX (adap, IOX_OUTPUT_REG, data); + + return 0; +} + +int bmi_ov2640_flash_low_beam (struct bmi_cam *cam) +{ + unsigned char data; + struct bmi_ov2640 *bmi_ov2640; + struct i2c_adapter *adap; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + adap = bmi_device_get_i2c_adapter (bmi_ov2640->bdev); + + ReadByte_IOX (adap, IOX_INPUT_REG, &data); + data &= ~(0x10); // Low Beam + WriteByte_IOX (adap, IOX_OUTPUT_REG, data); + + return 0; +} + +int bmi_ov2640_red_led_off (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + int slot; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 3, 1); // Red LED=OFF + return 0; +} + + +int bmi_ov2640_red_led_on (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + int slot; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 3, 0); // Red LED=ON + return 0; +} + + +int bmi_ov2640_green_led_off (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + struct i2c_adapter *adap; + unsigned char iox_data; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + adap = bmi_device_get_i2c_adapter (bdev); + + if ( bmi_device_present(bdev) ) { + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + + iox_data |= (0x40); //Set GRNLED to 1.(Off) + + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + } + return 0; +} + +int bmi_ov2640_green_led_on (struct bmi_cam *cam) +{ + struct bmi_ov2640 *bmi_ov2640; + struct bmi_device *bdev; + struct i2c_adapter *adap; + unsigned char iox_data; + + bmi_ov2640 = container_of(cam, struct bmi_ov2640, bcam); + bdev = bmi_ov2640->bdev; + + adap = bmi_device_get_i2c_adapter (bdev); + + if ( bmi_device_present(bdev) ) { + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + + iox_data &= ~(0x40); //Set GRNLED to 0.(On) + + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data & 0xF0); + } + return 0; +} + + +int bmi_ov2640_probe(struct bmi_device *bdev) +{ + + int slot = bmi_device_get_slot(bdev); + + // allocate a driver-specific structure + + struct bmi_ov2640 *bmi_ov2640 = kzalloc(sizeof(struct bmi_ov2640), GFP_KERNEL); + if (!bmi_ov2640) { + return -1; + } + + // attach bmi_device structure (so we can find it later). + + bmi_device_set_drvdata(bdev, bmi_ov2640); + printk(KERN_INFO "bmi_ov2640: probe: bmi_ov2640 pointer 0x%p \n",bdev->dev.driver_data); + + // initialize bmi_ov2640 struct + bmi_ov2640->bdev = bdev; + + // sensor interface struct fields + + bmi_ov2640->bcam.interface.clk_mode = 0; // gated + bmi_ov2640->bcam.interface.ext_vsync = 1; // external vsync + bmi_ov2640->bcam.interface.Vsync_pol = 0; // non-inverted + bmi_ov2640->bcam.interface.Hsync_pol = 0; // non-inverted + bmi_ov2640->bcam.interface.pixclk_pol = 0; // non-inverted + bmi_ov2640->bcam.interface.data_pol = 0; // non-inverted + bmi_ov2640->bcam.interface.data_width = 1; // 8-bits + bmi_ov2640->bcam.interface.width = 1600-1; // 1280 - SXGA 1600 - UXGA + bmi_ov2640->bcam.interface.height = 1200-1; // 1024 - SXGA 1200 - UXGA + + bmi_ov2640->bcam.interface.pixel_fmt = IPU_PIX_FMT_UYVY; // YUV422 + bmi_ov2640->bcam.interface.mclk = 12000000; // frequency/src + + //bmi_camera_sensor struct fields + + bmi_ov2640->bcam.sensor.set_color = bmi_ov2640_set_color; + bmi_ov2640->bcam.sensor.get_color = bmi_ov2640_get_color; + bmi_ov2640->bcam.sensor.set_ae_mode = bmi_ov2640_set_ae_mode; + bmi_ov2640->bcam.sensor.get_ae_mode = bmi_ov2640_get_ae_mode; + bmi_ov2640->bcam.sensor.config = bmi_ov2640_config; + bmi_ov2640->bcam.sensor.reset = bmi_ov2640_reset; + + //bmi_camera_link struct fields + + bmi_ov2640->bcam.link.enable = bmi_ov2640_link_enable; + bmi_ov2640->bcam.link.disable = bmi_ov2640_link_disable; + + //bmi_camera_cntl struct fields + + bmi_ov2640->bcam.cntl.flash_led_off = bmi_ov2640_flash_led_off; + bmi_ov2640->bcam.cntl.flash_led_on = bmi_ov2640_flash_led_on; + bmi_ov2640->bcam.cntl.flash_high_beam = bmi_ov2640_flash_high_beam; + bmi_ov2640->bcam.cntl.flash_low_beam = bmi_ov2640_flash_low_beam; + bmi_ov2640->bcam.cntl.red_led_off = bmi_ov2640_red_led_off; + bmi_ov2640->bcam.cntl.red_led_on = bmi_ov2640_red_led_on; + bmi_ov2640->bcam.cntl.green_led_off = bmi_ov2640_green_led_off; + bmi_ov2640->bcam.cntl.green_led_on = bmi_ov2640_green_led_on; + + + //bmi_cam struct fields + + bmi_ov2640->bcam.activate = bmi_ov2640_activate ; + bmi_ov2640->bcam.deactivate = bmi_ov2640_deactivate; + + //bmi_ov2640 struct fields + bmi_ov2640->shutter = 0; + bmi_ov2640->zoomin = 0; + bmi_ov2640->zoomout = 0; + bmi_ov2640->flash = 0; + + bmi_ov2640->irq = bmi_device_get_status_irq (bdev); + + //initialize struct work_struct + INIT_WORK (&bmi_ov2640->work, bmi_ov2640_buttons_work); + + //Power stablization delay + mdelay(500); + + //Do one-time hw initialization (e.g. patch) + + // configure IOX + configure_IOX (bmi_ov2640); + + // configure GPIO + configure_GPIO (bmi_ov2640); + + // chip enable camera + enable_camera (bmi_ov2640); + + ov2640_patch (&bmi_ov2640->bdev->adap); + + //register with bug_camera + + //REWORK: check return code + register_bug_camera (&bmi_ov2640->bcam, slot); + + return 0; +} + +void bmi_ov2640_remove(struct bmi_device *bdev) +{ + + //get our pointer + struct bmi_ov2640 *bmi_ov2640 = (struct bmi_ov2640*)(bmi_device_get_drvdata (bdev)); + int slot = bmi_device_get_slot (bdev); + + if (bdev == NULL) + { + printk(KERN_INFO "bmi_ov2640: bmi-ov2640_remove: NULL Pointer on bdev\n"); + return; + } + if (bmi_ov2640 == NULL) + { + printk(KERN_INFO "bmi_ov2640: bmi_ov2640_remove: NULL Pointer on bmi_ov2640\n"); + printk(KERN_INFO "bmi_ov2640: bmi_ov2640_remove: bdev->epid.vendor: 0x%x\n",bdev->epid.vendor); + printk(KERN_INFO "bmi_ov2640: bmi_ov2640_remove: bdev->epid.product: 0x%x\n",bdev->epid.product); + printk(KERN_INFO "bmi_ov2640: bmi_ov2640_remove: bdev->epid.revision: 0x%x\n",bdev->epid.revision); + printk(KERN_INFO "bmi_ov3640: bmi_ov2640_remove: bmi_ov2640 pointer 0x%p \n",bdev->dev.driver_data); + return; + } + //unregister with bug_camera + unregister_bug_camera ( &bmi_ov2640->bcam, slot); + + disable_camera (bmi_ov2640); + deconfigure_module (bmi_ov2640); + + + flush_scheduled_work(); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + + //free driver-specific structure + kfree (bmi_ov2640); + return; +} + + +static __init int bmi_ov2640_init(void) +{ + printk("BMI OV2640 Camera Sensor Driver v%s \n", BMI_OV2640_VERSION); + +// Register with BMI bus. + return bmi_register_driver (&bmi_ov2640_driver); + +} + +static void __exit bmi_ov2640_cleanup(void) +{ + +// UnRegister with BMI bus. + bmi_unregister_driver (&bmi_ov2640_driver); + return; +} + + +module_init(bmi_ov2640_init); +module_exit(bmi_ov2640_cleanup); + +MODULE_AUTHOR("EnCADIS Design, Inc."); +MODULE_DESCRIPTION("OV2640 Camera Driver"); +MODULE_LICENSE("GPL"); + --- /dev/null +++ git/drivers/bmi/pims/camera/bmi_vs6624.c @@ -0,0 +1,915 @@ +#include +#include +#include +#include +#include +#include +#include "bug_camera.h" +#include "vs6624_access.h" + + +// BMI device ID table +static struct bmi_device_id bmi_vs6624_tbl[] = +{ + { .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_CAMERA_VS6624, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_vs6624_tbl); + +int bmi_vs6624_probe(struct bmi_device *bdev); +void bmi_vs6624_remove(struct bmi_device *bdev); +int bmi_vs6624_suspend(struct bmi_device *bdev); +int bmi_vs6624_resume(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_vs6624_driver = +{ + .name = "bmi_vs6624", + .id_table = bmi_vs6624_tbl, + .probe = bmi_vs6624_probe, + .remove = bmi_vs6624_remove, + }; + + +struct bmi_vs6624 { + struct bmi_device *bdev; + struct bmi_cam bcam; + unsigned int shutter; // shutter button save state + unsigned int zoomin; // zoomin button save state + unsigned int zoomout; // zoom out button save state + unsigned int flash; // state of camera FLASH + int irq; + struct input_dev *idev; + struct work_struct work; + +}; + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + + +// read byte from I2C IO expander + +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + printk (KERN_ERR "ReadByte_IOX() - data = %02X\n", *data); + + ret = 0; + } + else { + //Rework: add conditional debug messages here + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer failed\n"); + ret = -1; + } + return ret; +} + + +// write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + printk (KERN_ERR "WriteByte_IOX() - data = %02X\n", data); + ret = 0; + } + else { + //Rework: add conditional debug messages here + + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +/* + * Input interrupt handler and support routines + */ + + +// work handler +void bmi_vs6624_buttons_work(void *arg) +{ + struct i2c_adapter *adap; + struct bmi_vs6624 *pim = (struct bmi_vs6624*)arg; + + unsigned char iox_data; + unsigned int test_value; + int sync_flag = 0; + int err; + + printk (KERN_ERR "bmi_vs6624_buttons_work() - enter\n"); + + + // avoid i2c i/o if camera hardware not present. + + if (bmi_device_present (pim->bdev) == 0) { + goto exit; + } + + adap = bmi_device_get_i2c_adapter (pim->bdev); + + + // read IOX data input + err = ReadByte_IOX (adap, IOX_INPUT_REG, &iox_data); + if (err) { + goto exit; + } + + // zoom in button + test_value = !((iox_data & 0x2) >> 1); + if (test_value != pim->zoomin) { + printk (KERN_ERR "bmi_vs6624buttons_work() - report ZOOMIN\n"); + pim->zoomin = test_value; + input_report_key (pim->idev, BN_ZOOMIN, test_value); + sync_flag = 1; + } + + + // zoom out button + test_value = !((iox_data & 0x4) >> 2); + if (test_value != pim->zoomout) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - report ZOOMOUT\n"); + pim->zoomout = test_value; + input_report_key (pim->idev, BN_ZOOMOUT, test_value); + sync_flag = 1; + } +#if 0 + // flash button + test_value = (iox_data & 0x8) >> 3; + if (test_value != pim->flash) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - report FLASH\n"); + pim->flash = test_value; + input_report_key (pim->idev, BN_FLASH, test_value); + sync_flag = 1; + } +#endif + + if ( sync_flag ) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - input_sync()ing..\n"); + input_sync (pim->idev); + } +exit: + printk (KERN_ERR "bmi_vs6624_buttons_work() -exit\n"); + enable_irq (pim->irq); + return; + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + struct bmi_vs6624 *pim = dummy; + unsigned int test_value; + + int slot; + + printk (KERN_ERR "module_irq_handler() - enter\n"); + + disable_irq_nosync(irq); + + slot = bmi_device_get_slot(pim->bdev); + + + // shutter button on GPIO + + test_value = !(bmi_read_gpio_data_reg (slot) & 0x1); + + if (!test_value == pim->shutter) { + pim->shutter = test_value; + printk (KERN_ERR "module_irq_handler() - report SHUTTER\n"); + input_report_key (pim->idev, BN_SHUTTER, test_value); + input_sync (pim->idev); + } + + // other buttons on I2C IOX + schedule_work (&pim->work); + printk (KERN_ERR "module_irq_handler() - exit\n"); + return IRQ_HANDLED; +} + +/* + * control functions + */ + + +// configure IOX IO and states +void configure_IOX(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap; + + adap = bmi_device_get_i2c_adapter (cam->bdev); + + printk (KERN_ERR "configure_IOX() - enter\n"); + + + WriteByte_IOX (adap, IOX_OUTPUT_REG, 0x40); // CE=0, F_CHG=1,SYNC=0, TORCH=0 + WriteByte_IOX (adap, IOX_CONTROL, 0x0F); // IOX[7:4]=OUT, IOX[3:0]=IN +} + +// configure GPIO IO and states +void configure_GPIO(struct bmi_vs6624 *cam) +{ + // set states before turning on outputs + + int slot; + + slot = bmi_device_get_slot (cam->bdev); + + bmi_set_module_gpio_data (slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data (slot, 2, 1); // Green LED=OFF + bmi_set_module_gpio_data (slot, 1, 0); // SER_RST=0 + + // configure direction + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); // SHUTTER +} + +// deconfigure IOX and GPIO +void deconfigure_module(struct bmi_vs6624 *cam) +{ + int slot; + struct i2c_adapter *adap; + + slot = bmi_device_get_slot (cam->bdev); + adap = bmi_device_get_i2c_adapter (cam->bdev); + + + if ( bmi_device_present (cam->bdev) ) { + WriteByte_IOX (adap, IOX_CONTROL, 0xFF); + } + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); +} + + +// configure serializer on plug-in module +void configure_serializer(struct bmi_vs6624 *cam) +{ + int slot = bmi_device_get_slot (cam->bdev); + bmi_set_module_gpio_data (slot, 1, 1); // SER_RST=1 +} + +void deconfigure_serializer(struct bmi_vs6624 *cam) +{ + int slot = bmi_device_get_slot (cam->bdev); + bmi_set_module_gpio_data (slot, 1, 0); // SER_RST=0 +} + +void enable_camera(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + + printk (KERN_ERR "enable_camera() enter\n"); + + adap = bmi_device_get_i2c_adapter (cam->bdev); + + // The first i2c read seems to mess everything up. + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + printk (KERN_ERR "enable_camera() iox_data = %02X\n", iox_data); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data | 0x80); + printk (KERN_ERR "enable_camera() exit\n"); +} + +// disable camera on plug-in module +void disable_camera(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + + printk (KERN_ERR "disable_camera() enter\n"); + + adap = bmi_device_get_i2c_adapter (cam->bdev); + + if ( bmi_device_present(cam->bdev) ) { + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data & 0x70); + } + + printk (KERN_ERR "disable_camera() exit\n"); +} + +// generate sync +void generate_camera_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "generate_camera_sync() - enter\n"); + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + + printk(KERN_INFO "generate_camera_sync() - read = %02X\n", iox_data[0]); + printk(KERN_INFO "generate_camera_sync() - write = %02X\n", *iox_data | 0x20); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + + printk(KERN_INFO "generate_camera_sync() - write = %02X\n", *iox_data & 0xD0); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + udelay(20); // 60 MHz * 1024 = ~17 us sync time + + printk(KERN_INFO "generate_camera_sync() - exit\n"); +} + +void set_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "set_sync() - enter\n"); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + + printk(KERN_INFO "set_sync() - exit\n"); +} + +void clear_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "clear_sync() - enter\n"); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + + printk(KERN_INFO "clear_sync() - exit\n"); +} + + +// check serializer lock +int check_camera_lock(void) +{ + return bmi_sensor_lock_status(); +} + +void bmi_vs6624_set_color(struct bmi_cam *cam, int bright, int saturation, int red, int green, int blue) +{ + + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + adap = &bmi_vs6624->bdev->adap; + + vs6624_set_color (adap, bright, saturation, red, green, blue); + return; + +} + +void bmi_vs6624_get_color(struct bmi_cam *cam, int *bright, int *saturation, int *red, int *green, int *blue) +{ + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + adap = &bmi_vs6624->bdev->adap; + + vs6624_get_color (adap, bright, saturation, red, green, blue); + return; +} + + + + +void bmi_vs6624_set_ae_mode (struct bmi_cam *cam, int ae_mode) +{ + printk (KERN_ERR "bmi_vs6624_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +void bmi_vs6624_get_ae_mode (struct bmi_cam *cam, int *ae_mode) +{ + printk (KERN_ERR "bmi_vs6624_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +sensor_interface * bmi_vs6624_config (struct bmi_cam *cam, int *frame_rate, int high_quality) +{ + //REWORK: Add code here + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + int i; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + adap = &bmi_vs6624->bdev->adap; + + printk (KERN_ERR "bmi_vs6624_CONFIG() - enter\n"); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_config(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_config(): camera serializer NOT LOCKED\n"); + } + + //Bring up the serial link + + bmi_sensor_active(1); + configure_serializer (bmi_vs6624); + + adap = &bmi_vs6624->bdev->adap; + set_sync (adap); + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_config() - camera serializer locked, i = %d\n", i); + break; + } + else { + printk(KERN_ERR "bmi_vs6624_config() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + + + if(check_camera_lock()) { + + printk(KERN_INFO "bmi_vs6624_config(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_config(): camera serializer NOT LOCKED\n"); + } + + + printk (KERN_ERR "bmi_vs6624_CONFIG() - exit\n"); + return &cam->interface; + +} + + +sensor_interface * bmi_vs6624_reset (struct bmi_cam *cam) +{ + //REWORK: Add code here + //REWORK: What is a valid soft reset sequence ? + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + printk (KERN_ERR "bmi_vs6624_RESET() - enter\n"); + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + adap = bmi_device_get_i2c_adapter (bmi_vs6624->bdev); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_reset(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_reset(): camera serializer NOT LOCKED\n"); + } + //disable the serial link + + deconfigure_serializer (bmi_vs6624); + bmi_sensor_inactive(); + + if(check_camera_lock()) { + + printk(KERN_INFO "bmi_vs6624_reset(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_reset(): camera serializer NOT LOCKED\n"); + } + + printk (KERN_ERR "bmi_vs6624_RESET() - exit\n"); + + return &cam->interface; +} + +int bmi_vs6624_activate (struct bmi_cam *cam, struct input_dev *idev) +{ + //REWORK: Add code here + int rc = 0; + int i; + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + printk (KERN_ERR "int bmi_vs6624_activate () - enter\n"); + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + //bmi_vs6624 struct fields + bmi_vs6624->idev = idev; + + bmi_vs6624->shutter = 0; + bmi_vs6624->zoomin = 0; + bmi_vs6624->zoomout = 0; + bmi_vs6624->flash = 0; + + printk (KERN_ERR "bmi_vs6624_activate () - irq = %d\n", bmi_vs6624->irq); + + // install button irq_handler + if (request_irq(bmi_vs6624->irq, &module_irq_handler, 0, "bmi_cam_button", bmi_vs6624)) { + printk (KERN_ERR + "bmi_vs6624_activate() - request_irq (irq = %d) failed.\n", + bmi_vs6624->irq); + + rc = -EBUSY; + goto exit; + } + + //Activate serial link + bmi_sensor_active(1); // rising edge clock + configure_serializer (bmi_vs6624); + + adap = &bmi_vs6624->bdev->adap; + set_sync (adap); + + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_activate() - camera serializer locked, i = %d\n", i); + break; + } + else { + printk(KERN_ERR "bmi_vs6624_activate() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_activate(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_activate(): camera serializer NOT LOCKED\n"); + } + + +exit: + printk (KERN_ERR "int bmi_vs6624_activate () - exit\n"); + return rc; +} + +int bmi_vs6624_deactivate (struct bmi_cam *cam) +{ + //REWORK: Add code here + + struct bmi_vs6624 *bmi_vs6624; + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + //De-activate serial link + deconfigure_serializer (bmi_vs6624); + bmi_sensor_inactive(); + + + //uninstall button irq_handler + free_irq(bmi_vs6624->irq, bmi_vs6624); + + + return 0; +} + + +void bmi_vs6624_link_enable (struct bmi_cam *cam) +{ + //REWORK: Add code here + + int i; + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + //Activate serial link + bmi_sensor_active(1); // rising edge clock + configure_serializer (bmi_vs6624); + + adap = bmi_device_get_i2c_adapter (bmi_vs6624->bdev); + + set_sync (adap); + + + //REWORK: Speed this up. (shorten delay) + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_activate() - camera serializer locked, i = %d\n", i); + break; + } + else { + printk(KERN_ERR "bmi_vs6624_activate() - camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + + if(check_camera_lock()) { + printk(KERN_INFO "bmi_vs6624_activate(): camera serializer LOCKED\n"); + } + else { + printk(KERN_ERR "bmi_vs6624_activate(): camera serializer NOT LOCKED\n"); + } + + return; +} + + +void bmi_vs6624_link_disable (struct bmi_cam *cam) +{ + struct bmi_vs6624 *bmi_vs6624; + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + //De-activate serial link + deconfigure_serializer (bmi_vs6624); + bmi_sensor_inactive(); + + return; +} + + + +int bmi_vs6624_red_led_off (struct bmi_cam *cam) +{ + struct bmi_vs6624 *bmi_vs6624; + struct bmi_device *bdev; + int slot; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + bdev = bmi_vs6624->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 3, 1); // Red LED=OFF + return 0; +} + + +int bmi_vs6624_red_led_on (struct bmi_cam *cam) +{ + struct bmi_vs6624 *bmi_vs6624; + struct bmi_device *bdev; + int slot; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + bdev = bmi_vs6624->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 3, 0); // Red LED=ON + return 0; +} + + +int bmi_vs6624_green_led_off (struct bmi_cam *cam) +{ + struct bmi_vs6624 *bmi_vs6624; + struct bmi_device *bdev; + int slot; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + bdev = bmi_vs6624->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 2, 1); // Green LED=OFF + return 0; +} + + +int bmi_vs6624_green_led_on (struct bmi_cam *cam) +{ + struct bmi_vs6624 *bmi_vs6624; + struct bmi_device *bdev; + int slot; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + bdev = bmi_vs6624->bdev; + + slot = bmi_device_get_slot (bdev); + + bmi_set_module_gpio_data (slot, 2, 0); // Green LED=ON + return 0; +} + + +int bmi_vs6624_probe(struct bmi_device *bdev) +{ + + //REWORK: Add code here + + int slot = bmi_device_get_slot(bdev); + + + // allocate a driver-specific structure + + struct bmi_vs6624 *bmi_vs6624 = kzalloc(sizeof(struct bmi_vs6624), GFP_KERNEL); + if (!bmi_vs6624) { + return -1; + } + + // attach bmi_device structure (so we can find it later). + bmi_device_set_drvdata(bdev, bmi_vs6624); + + + // initialize bmi_vs6624 struct + bmi_vs6624->bdev = bdev; + + // sensor interface struct fields +# if 1 + bmi_vs6624->bcam.interface.clk_mode = 0; // gated + bmi_vs6624->bcam.interface.ext_vsync = 1; // external vsync +#else + bmi_vs6624->bcam.interface.clk_mode = 2; // progressive + bmi_vs6624->bcam.interface.ext_vsync = 0; // internal vsync +# endif + bmi_vs6624->bcam.interface.Vsync_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.Hsync_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.pixclk_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.data_pol = 0; // non-inverted MTW=1 + bmi_vs6624->bcam.interface.data_width = 1; // 8-bits + bmi_vs6624->bcam.interface.width = 1280-1; // 1280 - SXGA + bmi_vs6624->bcam.interface.height = 1024-1; // 1024 - SXGA + + bmi_vs6624->bcam.interface.pixel_fmt = IPU_PIX_FMT_UYVY; // YUV422 + bmi_vs6624->bcam.interface.mclk = 12000000; // frequency/src + + //bmi_camera_sensor struct fields + + bmi_vs6624->bcam.sensor.set_color = bmi_vs6624_set_color; + bmi_vs6624->bcam.sensor.get_color = bmi_vs6624_get_color; + bmi_vs6624->bcam.sensor.set_ae_mode = bmi_vs6624_set_ae_mode; + bmi_vs6624->bcam.sensor.get_ae_mode = bmi_vs6624_get_ae_mode; + bmi_vs6624->bcam.sensor.config = bmi_vs6624_config; + bmi_vs6624->bcam.sensor.reset = bmi_vs6624_reset; + + //bmi_camera_link struct fields + + bmi_vs6624->bcam.link.enable = bmi_vs6624_link_enable; + bmi_vs6624->bcam.link.disable = bmi_vs6624_link_disable; + + //bmi_camera_cntl struct fields + + bmi_vs6624->bcam.cntl.flash_led_off = 0; + bmi_vs6624->bcam.cntl.flash_led_off = 0; + bmi_vs6624->bcam.cntl.flash_high_beam = 0; + bmi_vs6624->bcam.cntl.flash_low_beam = 0; + bmi_vs6624->bcam.cntl.red_led_off = bmi_vs6624_red_led_off; + bmi_vs6624->bcam.cntl.red_led_on = bmi_vs6624_red_led_on; + bmi_vs6624->bcam.cntl.green_led_off = bmi_vs6624_green_led_off; + bmi_vs6624->bcam.cntl.green_led_on = bmi_vs6624_green_led_on; + + + //bmi_cam struct fields + + bmi_vs6624->bcam.activate = bmi_vs6624_activate ; + bmi_vs6624->bcam.deactivate = bmi_vs6624_deactivate; + + //bmi_vs6624 struct fields + bmi_vs6624->shutter = 0; + bmi_vs6624->zoomin = 0; + bmi_vs6624->zoomout = 0; + bmi_vs6624->flash = 0; + + bmi_vs6624->irq = bmi_device_get_status_irq (bdev); + + //initialize struct work_struct + INIT_WORK (&bmi_vs6624->work, bmi_vs6624_buttons_work, bmi_vs6624); + + //Power stablization delay + mdelay(500); + + //Do one-time hw initialization (e.g. patch) + + // configure IOX + configure_IOX (bmi_vs6624); + + // configure GPIO + configure_GPIO (bmi_vs6624); + + // chip enable camera + enable_camera (bmi_vs6624); + + vs6624_patch (&bmi_vs6624->bdev->adap); + + //register with bug_camera + + //REWORK: check return code + register_bug_camera (&bmi_vs6624->bcam, slot); + + return 0; +} + +void bmi_vs6624_remove(struct bmi_device *bdev) +{ + //REWORK: Add code here + + + //get our pointer + struct bmi_vs6624 *bmi_vs6624 = (struct bmi_vs6624*)(bmi_device_get_drvdata (bdev)); + int slot = bmi_device_get_slot (bdev); + + + //unregister with bug_camera + unregister_bug_camera ( &bmi_vs6624->bcam, slot); + + //REWORK: Avoid I2c access if camera module is not present. + + disable_camera (bmi_vs6624); + deconfigure_module (bmi_vs6624); + + + flush_scheduled_work(); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + + //free driver-specific structure + kfree (bmi_vs6624); + return; +} + + +static __init int bmi_vs6624_init(void) +{ + +// Register with BMI bus. + return bmi_register_driver (&bmi_vs6624_driver); + +} + +static void __exit bmi_vs6624_cleanup(void) +{ + +// UnRegister with BMI bus. + bmi_unregister_driver (&bmi_vs6624_driver); + return; +} + + +module_init(bmi_vs6624_init); +module_exit(bmi_vs6624_cleanup); + +MODULE_AUTHOR("EnCADIS Design, Inc."); +MODULE_DESCRIPTION("VS6624 Camera Driver"); +MODULE_LICENSE("GPL"); + --- /dev/null +++ git/drivers/bmi/pims/camera/bug_camera.c @@ -0,0 +1,611 @@ +#include +#include +#include +#include +#include +#include "bug_camera.h" + +#define BUG_CAMERA_VERSION "1.0" + + +struct cam_ctl +{ + int slot; + struct cdev cdev; + struct device *class_dev; +}; + +// private device structure +struct bug_cam +{ + unsigned int cam_cnt; //number of cameras present + unsigned int active; //active camera slot + + struct bmi_cam *bcam[4]; //slot-specific behavior + + struct input_dev *input_dev; // input device + struct cam_ctl cntl[4]; // control character device + struct device *class_dev; + int cntl_devno; // control device number + int open_flag; // force single open +}; + +static struct bug_cam bug_cam; + +static int cntl_open (struct inode *, struct file *); +static int cntl_release (struct inode *, struct file *); +static int cntl_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +// control file operations + +struct file_operations cam_ctl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + +/* + * control device operations + */ + +static int cam_ctl_major; + +int ctl_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BUG Camera Control"); + + if (retval) { + return -1; + } + cam_ctl_major = MAJOR(dev_id); + return 0; +} + +void ctl_clean (void) +{ + dev_t dev_id; + + dev_id = MKDEV(cam_ctl_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int ctl_probe (struct cam_ctl *cam_ctl, int slot) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + cdev = &cam_ctl->cdev; + cdev_init (cdev, &cam_ctl_fops); + + dev_id = MKDEV (cam_ctl_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + bmi_class = bmi_get_bmi_class (); + + cam_ctl->class_dev = device_create (bmi_class, NULL, MKDEV(cam_ctl_major, slot), cam_ctl, "bmi_cam_ctl_m%i", slot+1); + + if (IS_ERR(cam_ctl->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_cam_ctl_m%i; errno = %ld\n", + slot+1, PTR_ERR(cam_ctl->class_dev)); + cam_ctl->class_dev = NULL; + } + cam_ctl->slot = slot; + + return ret; +} + +void ctl_remove (struct cam_ctl *cam_ctl, int slot) +{ + struct class *bmi_class; + + bmi_class = bmi_get_bmi_class (); + device_destroy (bmi_class, MKDEV(cam_ctl_major, slot)); + + cam_ctl->class_dev = 0; + + cdev_del (&cam_ctl->cdev); + return; +} + +// open +static int cntl_open(struct inode *inode, struct file *filp) +{ + if (bug_cam.open_flag) { + return - EBUSY; + } + bug_cam.open_flag = 1; + filp->private_data = &bug_cam; + return 0; +} + +// release +static int cntl_release(struct inode *inode, struct file *filp) +{ + bug_cam.open_flag = 0; + return 0; +} + + + + +static struct bmi_cam* get_selected (void) +{ + struct bmi_cam *bcam; + + if (bug_cam.active == -1) { + return 0; + } + + bcam = bug_cam.bcam [bug_cam.active]; + return bcam; + +} + + +static int select_slot (int slot) +{ + struct bmi_cam *bcam; + + // validate slot number + if ((slot < 0) || slot > 3) { + + printk(KERN_ERR + "bug_camera.c: Invalid value (%d) for camera selection.\n", + slot); + + return -EINVAL; + } + + // error if this slot not registered + if ( bug_cam.bcam [slot] == NULL ) { + return -ENODEV; + } + + // abort if this slot already active + if (bug_cam.active == slot) { + return 0; + } + + // if another slot is active, deactivate it. + if (bug_cam.active != -1) { + bcam = bug_cam.bcam [bug_cam.active]; + if ((bcam) && (bcam->deactivate)) { + bcam->deactivate (bcam); + } + } + + // activate this slot + bcam = bug_cam.bcam [slot]; + if (bcam->activate) { + bcam->activate (bcam, bug_cam.input_dev); + } + bug_cam.active = slot; + return 0; +} + + +// ioctl +static int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bmi_cam *bcam; + int err; + + switch (cmd) { + + // error if no camera selected. (active) + + case BMI_CAM_FLASH_LED_ON: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.flash_led_on)) { + bcam->cntl.flash_led_on (bcam); + } + return 0; + + case BMI_CAM_FLASH_LED_OFF: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.flash_led_off)) { + bcam->cntl.flash_led_off (bcam); + } + return 0; + + + case BMI_CAM_FLASH_HIGH_BEAM: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.flash_high_beam)) { + bcam->cntl.flash_high_beam (bcam); + } + return 0; + + case BMI_CAM_FLASH_LOW_BEAM: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.flash_low_beam)) { + bcam->cntl.flash_low_beam (bcam); + } + return 0; + + case BMI_CAM_RED_LED_OFF: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.red_led_off)) { + bcam->cntl.red_led_off (bcam); + } + return 0; + + case BMI_CAM_RED_LED_ON: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.red_led_on)) { + bcam->cntl.red_led_on (bcam); + } + return 0; + + case BMI_CAM_GREEN_LED_OFF: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.green_led_off)) { + bcam->cntl.green_led_off (bcam); + } + return 0; + + case BMI_CAM_GREEN_LED_ON: + + bcam = get_selected(); + if ((bcam) && (bcam->cntl.green_led_on)) { + bcam->cntl.green_led_on (bcam); + } + return 0; + + case BMI_CAM_SELECT: + + err = select_slot (arg); + return err; + + case BMI_CAM_GET_SELECTED: + + return (int) bug_cam.active; + + default: + return -ENOTTY; + } + return 0; +} + + +void bug_camera_set_color(int bright, int saturation, int red, int green, int blue) +{ + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + + if (bug_cam.active == -1) { + return; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->sensor.set_color)) { + bcam->sensor.set_color (bcam, bright, saturation, red, green, blue); + } + return; + +} + +void bug_camera_get_color(int *bright, int *saturation, int *red, int *green, int *blue) +{ + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + + if (bug_cam.active == -1) { + return; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->sensor.get_color)) { + bcam->sensor.get_color (bcam, bright, saturation, red, green, blue); + } + return; +} + + + + +void bug_camera_set_ae_mode (int ae_mode) +{ + //delegate to active bmi_cam. + printk (KERN_ERR " bug_camera_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +void bug_camera_get_ae_mode (int *ae_mode) +{ + //delegate to active bmi_cam. + printk (KERN_ERR " bug_camera_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +sensor_interface * bug_camera_config (int *frame_rate, int high_quality) +{ + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + sensor_interface *sensor_if = 0; + + if (bug_cam.active == -1) { + return 0; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->sensor.config)) { + sensor_if = bcam->sensor.config (bcam, frame_rate, high_quality); + } + return sensor_if; +} + + +sensor_interface * bug_camera_reset (void) +{ + + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + sensor_interface *sensor_if = 0; + + if (bug_cam.active == -1) { + return 0; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->sensor.reset)) { + sensor_if = bcam->sensor.reset(bcam); + } + return sensor_if; +} + +void bug_camera_link_enable (void) +{ + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + + if (bug_cam.active == -1) { + return; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->link.enable)) { + bcam->link.enable(bcam); + } + return; +} + +void bug_camera_link_disable (void) +{ + //delegate to active bmi_cam. + + struct bmi_cam *bcam; + + if (bug_cam.active == -1) { + return; + } + + bcam = bug_cam.bcam [bug_cam.active]; + + if ((bcam) && (bcam->link.disable)) { + bcam->link.disable(bcam); + } + return; +} + + +// Link point for bug_v4l2_capture + +struct camera_sensor camera_sensor_if = { + + set_color: bug_camera_set_color, + get_color: bug_camera_get_color, + config: bug_camera_config, + reset: bug_camera_reset, +}; + + +struct camera_link camera_link_if = { + enable: bug_camera_link_enable, + disable: bug_camera_link_disable, + +}; + +int register_bug_camera( struct bmi_cam *bcam, int slot) +{ + printk (KERN_ERR "register_bug_camera() - slot = %d\n", slot); + + if (!bcam) { + return -1; + } + if ((slot < 0) || (slot > 3)) { + return -1; + } + if ( bug_cam.bcam [slot]) { + return -1; + } + else { + bug_cam.bcam [slot] = bcam; + bug_cam.cam_cnt += 1; + } + + if (ctl_probe(&bug_cam.cntl[slot], slot)) { + printk(KERN_ERR "\n"); + } + // Activate this camera if no other is active + if ( bug_cam.active == -1) { + bug_cam.active = slot; + bcam->activate( bcam, bug_cam.input_dev); + } + + return 0; + +} + +int unregister_bug_camera( struct bmi_cam *bcam, int slot) +{ + + if (!bcam) { + return -1; + } + if ((slot < 0) || (slot > 3)) { + return -1; + } + if ( bug_cam.bcam [slot] != bcam) { + return -1; + } + else { + bug_cam.bcam [slot] = 0; + bug_cam.cam_cnt -= 1; + + // Deactivate this camera if active + + if (bug_cam.active == slot) { + bcam->deactivate( bcam); + bug_cam.active = -1; + } + + } + return 0; +} + +static __init int bug_camera_init(void) +{ + int err = 0; + struct class *bmi_class; + + printk("BUG Camera Driver v%s \n", BUG_CAMERA_VERSION); + + memset (&bug_cam, 0, sizeof(struct bug_cam)); + + //No camera is active. + bug_cam.active = -1; + + //No cameras registered. + bug_cam.cam_cnt = 0; + + // Allocate and Register input device. + + // allocate input device + bug_cam.input_dev = input_allocate_device(); + if (!bug_cam.input_dev) { + printk(KERN_ERR "bug_camera_init: Can't allocate input_dev\n"); + err = -ENOMEM; + goto exit; + } + + // set up input device + bug_cam.input_dev->name = "bug_cam"; + bug_cam.input_dev->phys = "bug_cam"; + bug_cam.input_dev->id.bustype = BUS_BMI; + //bug_cam.input_dev->private = &bug_cam; + bug_cam.input_dev->evbit[0] = BIT(EV_KEY); + bug_cam.input_dev->keybit[BIT_WORD(BN_SHUTTER)] = BIT_MASK(BN_SHUTTER) | + BIT_MASK(BN_ZOOMIN) | + BIT_MASK(BN_ZOOMOUT); + // register input device + err = input_register_device (bug_cam.input_dev); + if (err) { + printk(KERN_ERR + "bug_camera_init() - input_register_device failed.\n"); + input_free_device(bug_cam.input_dev); + goto exit; + } + + //Register character device. + /* + // allocate char device number + err = alloc_chrdev_region (&bug_cam.cntl_devno, 0, 1, + "bug_camera_control"); + if (err < 0) { + printk(KERN_ERR "bug_camera_init(): alloc_chrdev_region failed.\n"); + goto err_exit; + } + + // init and add control character device + cdev_init (&bug_cam.cntl, &cntl_fops); + + err = cdev_add (&bug_cam.cntl, bug_cam.cntl_devno, 1); + if (err ) { + printk(KERN_ERR "bmi_camera_init(): cdev_add failed\n"); + goto err_exit2; + } + + // create class device + bmi_class = bmi_get_bmi_class (); + + bug_cam.class_dev = device_create (bmi_class, NULL, bug_cam.cntl_devno, &bug_cam, "bug_camera_control"); + + if (IS_ERR(bug_cam.class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bug_camera; errno = %ld\n", + PTR_ERR(bug_cam.class_dev)); + bug_cam.class_dev = NULL; + goto err_exit2; + } + */ + goto exit; + +err_exit2: + unregister_chrdev_region (bug_cam.cntl_devno, 1); +err_exit: + input_unregister_device (bug_cam.input_dev); +exit: + return err; +} + +static void __exit bug_camera_clean(void) +{ + /* struct class *bmi_class; + + bmi_class = bmi_get_bmi_class (); + device_destroy (bmi_class, bug_cam.cntl_devno);*/ + + bug_cam.class_dev = 0; + // Unregister character device. + unregister_chrdev_region (bug_cam.cntl_devno, 1); + + // Unregister input device. + input_unregister_device (bug_cam.input_dev); + +// input_free_device (bug_cam.input_dev); + return; +} + + +module_init(bug_camera_init); +module_exit(bug_camera_clean); + +// Exported symbols +EXPORT_SYMBOL(camera_sensor_if); +EXPORT_SYMBOL(camera_link_if); +EXPORT_SYMBOL(register_bug_camera); +EXPORT_SYMBOL(unregister_bug_camera); + +MODULE_AUTHOR("EnCADIS Design, Inc."); +MODULE_DESCRIPTION("Bug Camera Driver"); +MODULE_LICENSE("GPL"); + --- /dev/null +++ git/drivers/bmi/pims/camera/bug_camera.h @@ -0,0 +1,72 @@ +#ifndef BUG_CAMARA_H +#define BUG_CAMARA_H + +#include +#include + + +struct bmi_cam; +struct input_dev; + +# if 1 +struct bmi_camera_sensor { + + void (*set_color) (struct bmi_cam *bcam, int bright, int saturation, int red, int green, + int blue); + + void (*get_color) (struct bmi_cam *bcam, int *bright, int *saturation, int *red, int *green, + int *blue); + + void (*set_ae_mode) (struct bmi_cam *bcam, int ae_mode); + void (*get_ae_mode) (struct bmi_cam *bcam, int *ae_mode); + sensor_interface *(*config) (struct bmi_cam *bcam, int *frame_rate, int high_quality); + sensor_interface *(*reset) (struct bmi_cam *bcam); +}; + +struct bmi_camera_link { + + void (*enable) (struct bmi_cam *bcam); + void (*disable) (struct bmi_cam *bcam); + +}; +# endif + +struct bmi_camera_cntl { + + int (*flash_led_on) (struct bmi_cam *bcam); + int (*flash_led_off) (struct bmi_cam *bcam); + int (*flash_high_beam) (struct bmi_cam *bcam); + int (*flash_low_beam ) (struct bmi_cam *bcam); + int (*red_led_off) (struct bmi_cam *bcam); + int (*red_led_on) (struct bmi_cam *bcam); + int (*green_led_off) (struct bmi_cam *bcam); + int (*green_led_on) (struct bmi_cam *bcam); + +}; + +struct bmi_cam { + + sensor_interface interface; // pointer to this struct is returned by config() + + struct bmi_camera_sensor sensor; // v4l function pointers + + struct bmi_camera_link link; // bug link control function pointers + + struct bmi_camera_cntl cntl; // bmi camera control function pointers + + + int (*activate) (struct bmi_cam *cam, struct input_dev *idev); + + int (*deactivate) (struct bmi_cam *cam); + + +}; + + + +int register_bug_camera( struct bmi_cam *bcam, int slot); +int unregister_bug_camera( struct bmi_cam *bcam, int slot); + + +#endif + --- /dev/null +++ git/drivers/bmi/pims/camera/ov2640.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C Slave Address */ +#define OV2640_I2C_ADDRESS 0x30 // 7-bit address + + +int ov2640_ReadByte(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with 8-Bit Pointer */ + + rmsg[0].addr = OV2640_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = OV2640_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + ret = i2c_transfer(adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ov2640_ReadByte() - i2c_transfer failed.\n"); + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + +int ov2640_WriteByte(struct i2c_adapter *adap, unsigned char addr, unsigned char offset, unsigned char data) +{ + int ret; + struct i2c_msg wmsg; + int num_msgs; + + char buf[2]; + + buf[0] = (char)offset; + buf[1] = (char)data; + + + wmsg.addr = addr >> 1; + wmsg.flags = 0; /* write */ + wmsg.len = 2; + wmsg.buf = buf; + num_msgs = 1; + + ret = i2c_transfer (adap, &wmsg, num_msgs); + + if (ret == 1) { + ret = 0; + } + else { + printk (KERN_ERR "ov2640_WriteByte() - i2c_transfer failed.\n"); + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + + +void load_2640_12MHZ_Initial_UXGA_YUV (struct i2c_adapter *adap) + +{ + + // ;2640_Initial_100206.ovt + ov2640_WriteByte (adap, 0x60, 0xff, 0x01); + ov2640_WriteByte (adap, 0x60, 0x12, 0x80); + + msleep(1); // delay 1ms + + ov2640_WriteByte (adap, 0x60, 0xff, 0x00); + ov2640_WriteByte (adap, 0x60, 0x2c, 0xff); + ov2640_WriteByte (adap, 0x60, 0x2e, 0xdf); + ov2640_WriteByte (adap, 0x60, 0xff, 0x01); + ov2640_WriteByte (adap, 0x60, 0x3c, 0x32); +// ov2640_WriteByte (adap, 0x60, 0x11, 0x01); //7.5fps); + ov2640_WriteByte (adap, 0x60, 0x11, 0x00); //7.5fps for 48; + ov2640_WriteByte (adap, 0x60, 0x09, 0x02); + ov2640_WriteByte (adap, 0x60, 0x04, 0x28); + ov2640_WriteByte (adap, 0x60, 0x13, 0xe5); + ov2640_WriteByte (adap, 0x60, 0x14, 0x48); + ov2640_WriteByte (adap, 0x60, 0x2c, 0x0c); + ov2640_WriteByte (adap, 0x60, 0x33, 0x78); + ov2640_WriteByte (adap, 0x60, 0x3a, 0x33); + ov2640_WriteByte (adap, 0x60, 0x3b, 0xfb); + ov2640_WriteByte (adap, 0x60, 0x3e, 0x00); + ov2640_WriteByte (adap, 0x60, 0x43, 0x11); + ov2640_WriteByte (adap, 0x60, 0x16, 0x10); + ov2640_WriteByte (adap, 0x60, 0x39, 0x02); + ov2640_WriteByte (adap, 0x60, 0x35, 0x88); + ov2640_WriteByte (adap, 0x60, 0x22, 0x0a); + ov2640_WriteByte (adap, 0x60, 0x37, 0x40); + ov2640_WriteByte (adap, 0x60, 0x23, 0x00); + ov2640_WriteByte (adap, 0x60, 0x34, 0xa0); + ov2640_WriteByte (adap, 0x60, 0x36, 0x1a); + ov2640_WriteByte (adap, 0x60, 0x06, 0x02); + ov2640_WriteByte (adap, 0x60, 0x07, 0xc0); + ov2640_WriteByte (adap, 0x60, 0x0d, 0xb7); + ov2640_WriteByte (adap, 0x60, 0x0e, 0x01); + ov2640_WriteByte (adap, 0x60, 0x4c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x4a, 0x81); + ov2640_WriteByte (adap, 0x60, 0x21, 0x99); + ov2640_WriteByte (adap, 0x60, 0x24, 0x3a); + ov2640_WriteByte (adap, 0x60, 0x25, 0x32); + ov2640_WriteByte (adap, 0x60, 0x26, 0x82); + ov2640_WriteByte (adap, 0x60, 0x5c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x63, 0x00); + ov2640_WriteByte (adap, 0x60, 0x5d, 0x55); + ov2640_WriteByte (adap, 0x60, 0x5e, 0x7d); + ov2640_WriteByte (adap, 0x60, 0x5f, 0x7d); + ov2640_WriteByte (adap, 0x60, 0x60, 0x55); + ov2640_WriteByte (adap, 0x60, 0x61, 0x70); + ov2640_WriteByte (adap, 0x60, 0x62, 0x80); + ov2640_WriteByte (adap, 0x60, 0x7c, 0x05); + ov2640_WriteByte (adap, 0x60, 0x20, 0x80); + ov2640_WriteByte (adap, 0x60, 0x28, 0x30); + ov2640_WriteByte (adap, 0x60, 0x6c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x6d, 0x80); + ov2640_WriteByte (adap, 0x60, 0x6e, 0x00); + ov2640_WriteByte (adap, 0x60, 0x70, 0x02); + ov2640_WriteByte (adap, 0x60, 0x71, 0x94); + ov2640_WriteByte (adap, 0x60, 0x73, 0xc1); +// ov2640_WriteByte (adap, 0x60, 0x3d, 0x28); //12MHz XVCLK + ov2640_WriteByte (adap, 0x60, 0x3d, 0x30); //12MHz XVCLK -> 48 MHz); +// ov2640_WriteByte (adap, 0x60, 0x3d, 0x38); //12MHz XVCLK -> 24 MHz); //MTW + ov2640_WriteByte (adap, 0x60, 0x5a, 0x57); + ov2640_WriteByte (adap, 0x60, 0x4f, 0xbb); + ov2640_WriteByte (adap, 0x60, 0x50, 0x9c); + ov2640_WriteByte (adap, 0x60, 0x12, 0x00); + ov2640_WriteByte (adap, 0x60, 0x17, 0x11); + ov2640_WriteByte (adap, 0x60, 0x18, 0x76); //M+ + ov2640_WriteByte (adap, 0x60, 0x19, 0x01); + ov2640_WriteByte (adap, 0x60, 0x1a, 0x97); + ov2640_WriteByte (adap, 0x60, 0x32, 0x36); + + ov2640_WriteByte (adap, 0x60, 0xff, 0x00); + ov2640_WriteByte (adap, 0x60, 0xe5, 0x7f); + ov2640_WriteByte (adap, 0x60, 0xf9, 0xc0); + ov2640_WriteByte (adap, 0x60, 0x41, 0x24); + ov2640_WriteByte (adap, 0x60, 0xe0, 0x14); + ov2640_WriteByte (adap, 0x60, 0x76, 0xff); + ov2640_WriteByte (adap, 0x60, 0x33, 0xa0); + ov2640_WriteByte (adap, 0x60, 0x42, 0x20); + ov2640_WriteByte (adap, 0x60, 0x43, 0x18); + ov2640_WriteByte (adap, 0x60, 0x4c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x87, 0xd0); + ov2640_WriteByte (adap, 0x60, 0x88, 0x3f); + ov2640_WriteByte (adap, 0x60, 0xd7, 0x03); + ov2640_WriteByte (adap, 0x60, 0xd9, 0x10); + ov2640_WriteByte (adap, 0x60, 0xd3, 0x82); + ov2640_WriteByte (adap, 0x60, 0xc8, 0x08); + ov2640_WriteByte (adap, 0x60, 0xc9, 0x80); + ov2640_WriteByte (adap, 0x60, 0x7c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x02); + ov2640_WriteByte (adap, 0x60, 0x7c, 0x03); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x48); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x48); + ov2640_WriteByte (adap, 0x60, 0x7c, 0x08); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x20); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x10); + ov2640_WriteByte (adap, 0x60, 0x7d, 0x0e); + ov2640_WriteByte (adap, 0x60, 0x90, 0x00); + ov2640_WriteByte (adap, 0x60, 0x91, 0x0e); + ov2640_WriteByte (adap, 0x60, 0x91, 0x1a); + ov2640_WriteByte (adap, 0x60, 0x91, 0x31); + ov2640_WriteByte (adap, 0x60, 0x91, 0x5a); + ov2640_WriteByte (adap, 0x60, 0x91, 0x69); + ov2640_WriteByte (adap, 0x60, 0x91, 0x75); + ov2640_WriteByte (adap, 0x60, 0x91, 0x7e); + ov2640_WriteByte (adap, 0x60, 0x91, 0x88); + ov2640_WriteByte (adap, 0x60, 0x91, 0x8f); + ov2640_WriteByte (adap, 0x60, 0x91, 0x96); + ov2640_WriteByte (adap, 0x60, 0x91, 0xa3); + ov2640_WriteByte (adap, 0x60, 0x91, 0xaf); + ov2640_WriteByte (adap, 0x60, 0x91, 0xc4); + ov2640_WriteByte (adap, 0x60, 0x91, 0xd7); + ov2640_WriteByte (adap, 0x60, 0x91, 0xe8); + ov2640_WriteByte (adap, 0x60, 0x91, 0x20); + ov2640_WriteByte (adap, 0x60, 0x92, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x06); + ov2640_WriteByte (adap, 0x60, 0x93, 0xe3); + ov2640_WriteByte (adap, 0x60, 0x93, 0x05); + ov2640_WriteByte (adap, 0x60, 0x93, 0x05); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x02); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x93, 0x00); + ov2640_WriteByte (adap, 0x60, 0x96, 0x00); + ov2640_WriteByte (adap, 0x60, 0x97, 0x08); + ov2640_WriteByte (adap, 0x60, 0x97, 0x19); + ov2640_WriteByte (adap, 0x60, 0x97, 0x02); + ov2640_WriteByte (adap, 0x60, 0x97, 0x0c); + ov2640_WriteByte (adap, 0x60, 0x97, 0x24); + ov2640_WriteByte (adap, 0x60, 0x97, 0x30); + ov2640_WriteByte (adap, 0x60, 0x97, 0x28); + ov2640_WriteByte (adap, 0x60, 0x97, 0x26); + ov2640_WriteByte (adap, 0x60, 0x97, 0x02); + ov2640_WriteByte (adap, 0x60, 0x97, 0x98); + ov2640_WriteByte (adap, 0x60, 0x97, 0x80); + ov2640_WriteByte (adap, 0x60, 0x97, 0x00); + ov2640_WriteByte (adap, 0x60, 0x97, 0x00); + ov2640_WriteByte (adap, 0x60, 0xc3, 0xed); + ov2640_WriteByte (adap, 0x60, 0xa4, 0x00); + ov2640_WriteByte (adap, 0x60, 0xa8, 0x00); + ov2640_WriteByte (adap, 0x60, 0xc5, 0x11); + ov2640_WriteByte (adap, 0x60, 0xc6, 0x51); + ov2640_WriteByte (adap, 0x60, 0xbf, 0x80); + ov2640_WriteByte (adap, 0x60, 0xc7, 0x10); + ov2640_WriteByte (adap, 0x60, 0xb6, 0x66); + ov2640_WriteByte (adap, 0x60, 0xb8, 0xa5); + ov2640_WriteByte (adap, 0x60, 0xb7, 0x64); + ov2640_WriteByte (adap, 0x60, 0xb9, 0x7c); + ov2640_WriteByte (adap, 0x60, 0xb3, 0xaf); + ov2640_WriteByte (adap, 0x60, 0xb4, 0x97); + ov2640_WriteByte (adap, 0x60, 0xb5, 0xff); + ov2640_WriteByte (adap, 0x60, 0xb0, 0xc5); + ov2640_WriteByte (adap, 0x60, 0xb1, 0x94); + ov2640_WriteByte (adap, 0x60, 0xb2, 0x0f); + ov2640_WriteByte (adap, 0x60, 0xc4, 0x5c); +// + ov2640_WriteByte (adap, 0x60, 0xc0, 0xca); //M+ + ov2640_WriteByte (adap, 0x60, 0xc1, 0x96); + ov2640_WriteByte (adap, 0x60, 0x8c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x86, 0x3d); //2M + ov2640_WriteByte (adap, 0x60, 0x50, 0x00); + ov2640_WriteByte (adap, 0x60, 0x51, 0x90); + ov2640_WriteByte (adap, 0x60, 0x52, 0x2c); + ov2640_WriteByte (adap, 0x60, 0x53, 0x00); + ov2640_WriteByte (adap, 0x60, 0x54, 0x00); + ov2640_WriteByte (adap, 0x60, 0x55, 0x88); + ov2640_WriteByte (adap, 0x60, 0x57, 0x00); + ov2640_WriteByte (adap, 0x60, 0x5a, 0x90); + ov2640_WriteByte (adap, 0x60, 0x5b, 0x2c); + ov2640_WriteByte (adap, 0x60, 0x5c, 0x05); +// + ov2640_WriteByte (adap, 0x60, 0xc3, 0xed); + ov2640_WriteByte (adap, 0x60, 0x7f, 0x00); + ov2640_WriteByte (adap, 0x60, 0xda, 0x01); //UYVY + ov2640_WriteByte (adap, 0x60, 0xd3, 0x82); + ov2640_WriteByte (adap, 0x60, 0xe5, 0x1f); + ov2640_WriteByte (adap, 0x60, 0xe1, 0x67); + ov2640_WriteByte (adap, 0x60, 0xe0, 0x00); + ov2640_WriteByte (adap, 0x60, 0xdd, 0x7f); + ov2640_WriteByte (adap, 0x60, 0x05, 0x00); +// ov2640_WriteByte (adap, 0x60, 0xff, 0x01); +} + +void load_2640_UXGA(struct i2c_adapter *adap) +{ + +// ;1600x1200 + ov2640_WriteByte (adap, 0x60, 0xc0, 0xca); //M+ + ov2640_WriteByte (adap, 0x60, 0xc1, 0x96); + ov2640_WriteByte (adap, 0x60, 0x8c, 0x00); + ov2640_WriteByte (adap, 0x60, 0x86, 0x3d); //2M + ov2640_WriteByte (adap, 0x60, 0x50, 0x00); + ov2640_WriteByte (adap, 0x60, 0x51, 0x90); + ov2640_WriteByte (adap, 0x60, 0x52, 0x2c); + ov2640_WriteByte (adap, 0x60, 0x53, 0x00); + ov2640_WriteByte (adap, 0x60, 0x54, 0x00); + ov2640_WriteByte (adap, 0x60, 0x55, 0x88); + ov2640_WriteByte (adap, 0x60, 0x57, 0x00); + ov2640_WriteByte (adap, 0x60, 0x5a, 0x90); + ov2640_WriteByte (adap, 0x60, 0x5b, 0x2c); + ov2640_WriteByte (adap, 0x60, 0x5c, 0x05); +} + + + + +void ov2640_patch (struct i2c_adapter *adap) +{ + load_2640_12MHZ_Initial_UXGA_YUV (adap); + load_2640_UXGA(adap); + +} + --- /dev/null +++ git/drivers/bmi/pims/camera/ov2640.h @@ -0,0 +1,14 @@ +#ifndef VS6624_ACCESS_H +#define VS6624_ACCESS_H + +#include + +void ov2640_patch (struct i2c_adapter *adap); + +int vs6624_ReadByte(struct i2c_adapter *adap, unsigned short offset, unsigned char *data); +int vs6624_WriteByte(struct i2c_adapter *adap, unsigned short offset, unsigned char data); +int vs6624_WriteSequence(struct i2c_adapter *adap, const unsigned short array[][2], unsigned short len); +void vs6624_dump_regs(struct i2c_adapter *adap); + +#endif + --- /dev/null +++ git/drivers/bmi/pims/camera/vs6624_access.c @@ -0,0 +1,597 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vs6624_regs.h" + +# include "vs6624_patch.c" + + +int vs6624_ReadByte(struct i2c_adapter *adap, unsigned short offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + char buf[2]; + + buf[0] = (offset & 0xFF00) >> 8; + buf[1] = (offset & 0x00FF); + + /* Read Byte with 16-Bit Pointer */ + + rmsg[0].addr = VS6624_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 2; + rmsg[0].buf = buf; + + rmsg[1].addr = VS6624_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + ret = i2c_transfer(adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "vs6624_ReadByte() - i2c_transfer failed.\n"); + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + +int vs6624_WriteByte(struct i2c_adapter *adap, unsigned short offset, unsigned char data) +{ + int ret; + char buf[2]; + struct i2c_msg wmsg[2]; + int num_msgs; + + buf[0] = (offset & 0xFF00) >> 8; + buf[1] = (offset & 0x00FF); + + wmsg[0].addr = VS6624_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 2; + wmsg[0].buf = buf; + + wmsg[1].addr = VS6624_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "vs6624_WriteByte() - i2c_transfer failed.\n"); + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + +int vs6624_WriteSequence(struct i2c_adapter *adap, const unsigned short array[][2], unsigned short len) +{ + u16 x; + + for (x = 0; x < len; x++) + vs6624_WriteByte (adap, array[x][0], (unsigned char) array[x][1]); + + return 0; +} + + +static unsigned char get_reg(struct i2c_adapter *adap, unsigned short offset) +{ + unsigned char buf[1]; + + vs6624_ReadByte (adap, offset, &buf[0]); + + return buf[0]; +} + +void +vs6624_set_color(struct i2c_adapter *adap, int bright, int saturation, int red, int green, int blue) +{ + switch (saturation) { + case 150: + vs6624_WriteByte(adap, bColourSaturation0, 0xFF & 0xFF); + break; + case 75: + vs6624_WriteByte(adap, bColourSaturation0, 0xC0 & 0xFF); + break; + case 50: + vs6624_WriteByte(adap, bColourSaturation0, 0x80 & 0xFF); + break; + case 25: + vs6624_WriteByte(adap, bColourSaturation0, 0x40 & 0xFF); + break; + default: + vs6624_WriteByte(adap, bColourSaturation0, 0x78 & 0xFF); + break; + } +} + +void +vs6624_get_color(struct i2c_adapter *adap, int *bright, int *saturation, int *red, int *green, int *blue) +{ + *saturation = (int) get_reg( adap, bColourSaturation0); + switch (*saturation) { + case 0xFF: + *saturation = 150; + break; + case 0xC0: + *saturation = 75; + break; + case 0x80: + *saturation = 50; + break; + case 0x40: + *saturation = 25; + break; + default: + *saturation = 100; + break; + } +} + + + +static int vs_probe(struct i2c_adapter *adap) +{ + + unsigned char buf[2]; + + vs6624_ReadByte (adap, DeviceID_MSB, &buf[0]); + vs6624_ReadByte (adap, DeviceID_LSB, &buf[1]); + + if ((((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF)) == VS6624_ID) { + + printk(KERN_INFO "%s: Firmware Version %d.%d \n", + SENSOR_NAME, get_reg(adap, bFirmwareVsnMajor), get_reg (adap, bFirmwareVsnMinor)); + printk(KERN_INFO "%s: Patch Version %d.%d \n", + SENSOR_NAME, get_reg(adap, bPatchVsnMajor), get_reg(adap, bPatchVsnMinor)); + + return 0; + } + + printk (KERN_ERR "vs_probe: No VS6624 found.\n"); + printk (KERN_ERR "vs_probe: buf[0] = 0x%x\n", (buf[0] & 0xFF) << 8); + printk (KERN_ERR "vs_probe: buf[1] = 0x%x\n", (buf[1] & 0xFF)); + printk (KERN_ERR "vs_probe: DeviceID = %d\n", ((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF)); + + return -ENODEV; +} + +void vs6624_patch (struct i2c_adapter *adap) +{ + printk (KERN_ERR "vs6624_patch() - enter\n"); + msleep(100); + + vs6624_WriteByte (adap, PWR_MAN_SETUP_MODE_SELECT, 0x0); + msleep(10); + vs_probe (adap); + + printk (KERN_ERR "vs6624_patch() - applying patch p1\n"); + vs6624_WriteSequence (adap, patch_p1, sizeof(patch_p1) / (sizeof(u16) * 2)); + msleep(50); + + printk (KERN_ERR "vs6624_patch() - applying patch p2\n"); + vs6624_WriteSequence (adap, patch_p2, sizeof(patch_p2) / (sizeof(u16) * 2)); + + vs6624_WriteByte (adap, PWR_MAN_SETUP_MODE_SELECT, 0x2); + msleep(100); + + vs6624_WriteByte (adap, PWR_MAN_DIO_ENABLE, 0x1); + msleep(1); + + printk(KERN_INFO "MODE: %d \n", get_reg(adap, bState)); //pjg + + vs_probe (adap); + + + // Flicker correction + vs6624_WriteByte (adap, bLightingFrequencyHz, 0x64); // AC frequency == 100 + + // Pan step size + vs6624_WriteByte (adap, uwPanStepHSizeMSB0, 0x0); // H pan step == 15 + vs6624_WriteByte (adap, uwPanStepHSizeLSB0, 0xf); + vs6624_WriteByte (adap, uwPanStepVSizeMSB0, 0x0); // V pan step == 15 + vs6624_WriteByte (adap, uwPanStepVSizeLSB0, 0xf); + + vs6624_WriteByte (adap, bSyncCodeSetup, 0x21); // SYNC //pjg + vs6624_WriteByte (adap, bHSyncSetup, 0xF); // Active lines only, Automatic //pjg + vs6624_WriteByte (adap, bVSyncSetup, 0x7); // Active lines only, Automatic //pjg + + vs6624_WriteByte (adap, uwDesiredFrameRate_Num_MSB, 0x0); + vs6624_WriteByte (adap, uwDesiredFrameRate_Num_LSB, 0x0F); // frame rate numerator == 15 MTW + vs6624_WriteByte (adap, bDesiredFrameRate_Den, 0x1); // frame rate denominator == 1 + + printk(KERN_INFO "MODE: %d \n", get_reg(adap, bState)); //pjg + + vs6624_WriteByte (adap, uwExternalClockFrequencyMhzNumeratorMSB, 0x0); + vs6624_WriteByte (adap, uwExternalClockFrequencyMhzNumeratorLSB, 12); + vs6624_WriteByte (adap, bExternalClockFrequencyMhzDenominator, 0x1); + + vs6624_WriteByte (adap, bPClkSetup, 0x85); // Pix Clk Mode == free run + + printk(KERN_INFO "MODE: %d \n", get_reg(adap, bState)); //pjg + vs6624_WriteByte (adap, bUserCommand, 0x2); // RUN + + printk(KERN_INFO "MODE: %d \n", get_reg(adap, bState)); //pjg + printk (KERN_ERR "vs6624_patch() - writing setup patch\n"); + vs6624_WriteSequence (adap, patch_run_setup, sizeof(patch_run_setup) / (sizeof(u16) * 2)); + + + printk(KERN_INFO "MODE: %d \n", get_reg(adap, bState)); //pjg + + vs_probe (adap); //pjg + printk (KERN_ERR "vs6624_patch() - exit\n"); + return; +} + + + +static int ReadByteV(struct i2c_adapter *adap, unsigned short offset, char* name) +{ + unsigned char tmp[1]; + vs6624_ReadByte(adap, offset, &tmp[0]); + printk (KERN_ERR "vs6624 offset = %04X, data = %02X %s\n", offset, tmp[0], name); + return 0; + +} + + +void vs6624_dump_regs(struct i2c_adapter *adap) +{ + + printk (KERN_ERR "vs6624_dump_regs() - enter\n"); + + ReadByteV(adap, 0xc044,"PWR_MAN_DIO_ENABLE "); + ReadByteV(adap, 0x0001,"uwDeviceId "); + ReadByteV(adap, 0x0001,"DeviceID_MSB "); + ReadByteV(adap, 0x0002,"DeviceID_LSB "); + ReadByteV(adap, 0x0004,"bFirmwareVsnMajor "); + ReadByteV(adap, 0x0006,"bFirmwareVsnMinor "); + ReadByteV(adap, 0x0008,"bPatchVsnMajor "); + ReadByteV(adap, 0x000a,"bPatchVsnMinor "); + ReadByteV(adap, 0x0180,"bUserCommand "); + ReadByteV(adap, 0x0186,"bManualNextState "); + ReadByteV(adap, 0x0200,"bNextState "); + ReadByteV(adap, 0x0202,"bState "); + ReadByteV(adap, 0x0280,"fMeteringOn "); + ReadByteV(adap, 0x0282,"fExitOnStable "); + ReadByteV(adap, 0x0284,"bStreamLength "); + ReadByteV(adap, 0x0300,"fIsColdStart "); + ReadByteV(adap, 0x0302,"bNonViewLive_ActivePipeSetupBank "); + ReadByteV(adap, 0x0304,"bSnapShoot_ActivePipeSetupBank "); + ReadByteV(adap, 0x0306,"fSnapShoot_NoWaiting "); + ReadByteV(adap, 0x0308,"SensorMode "); + ReadByteV(adap, 0x0380,"bImageSize0 "); + ReadByteV(adap, 0x0383,"uwManualHSize0 "); + ReadByteV(adap, 0x0383,"uwManualHSizeMSB0 "); + ReadByteV(adap, 0x0384,"uwManualHSizeLSB0 "); + ReadByteV(adap, 0x0387,"uwManualVSize0 "); + ReadByteV(adap, 0x0387,"uwManualVSizeMSB0 "); + ReadByteV(adap, 0x0388,"uwManualVSizeLSB0 "); + ReadByteV(adap, 0x038b,"uwZoomStepHSize0 "); + ReadByteV(adap, 0x038b,"uwZoomStepHSizeMSB0 "); + ReadByteV(adap, 0x038c,"uwZoomStepHSizeLSB0 "); + ReadByteV(adap, 0x038f,"uwZoomStepVSize0 "); + ReadByteV(adap, 0x038f,"uwZoomStepVSizeMSB0 "); + ReadByteV(adap, 0x0390,"uwZoomStepVSizeLSB0 "); + ReadByteV(adap, 0x0392,"bZoomControl0 "); + ReadByteV(adap, 0x0395,"uwPanStepHSize0 "); + ReadByteV(adap, 0x0395,"uwPanStepHSizeMSB0 "); + ReadByteV(adap, 0x0396,"uwPanStepHSizeLSB0 "); + ReadByteV(adap, 0x0399,"uwPanStepVSize0 "); + ReadByteV(adap, 0x0399,"uwPanStepVSizeMSB0 "); + ReadByteV(adap, 0x039a,"uwPanStepVSizeLSB0 "); + ReadByteV(adap, 0x039c,"bPanControl0 "); + ReadByteV(adap, 0x039e,"bCropControl0 "); + ReadByteV(adap, 0x03a1,"uwManualCropHorizontalStart0 "); + ReadByteV(adap, 0x03a5,"uwManualCropHorizontalSize0 "); + ReadByteV(adap, 0x03a9,"uwManualCropVerticalStart0 "); + ReadByteV(adap, 0x03ad,"uwManualCropVerticalSize0 "); + ReadByteV(adap, 0x03a1,"bCropHStartMSB0 "); + ReadByteV(adap, 0x03a2,"bCropHStartLSB0 "); + ReadByteV(adap, 0x03a9,"bCropVStartMSB0 "); + ReadByteV(adap, 0x03aa,"bCropVStartLSB0 "); + ReadByteV(adap, 0x03a5,"bCropHSizeMSB0 "); + ReadByteV(adap, 0x03a6,"bCropHSizeLSB0 "); + ReadByteV(adap, 0x03ad,"bCropVSizeMSB0 "); + ReadByteV(adap, 0x03ae,"bCropVSizeLSB0 "); + ReadByteV(adap, 0x03b0,"bDataFormat0 "); + ReadByteV(adap, 0x03b2,"bBayerOutputAlignment0 "); + ReadByteV(adap, 0x03b4,"bContrast0 "); + ReadByteV(adap, 0x03b6,"bColourSaturation0 "); + ReadByteV(adap, 0x03b8,"bGamma0 "); + ReadByteV(adap, 0x03ba,"fHorizontalMirror0 "); + ReadByteV(adap, 0x03bc,"fVerticalFlip0 "); + ReadByteV(adap, 0x03be,"bChannelID0 "); + ReadByteV(adap, 0x0400,"bImageSize1 "); + ReadByteV(adap, 0x0403,"uwManualHSize1 "); + ReadByteV(adap, 0x0407,"uwManualVSize1 "); + ReadByteV(adap, 0x040b,"uwZoomStepHSize1 "); + ReadByteV(adap, 0x040f,"uwZoomStepVSize1 "); + ReadByteV(adap, 0x0412,"bZoomControl1 "); + ReadByteV(adap, 0x0415,"uwPanStepHSize1 "); + ReadByteV(adap, 0x0419,"uwPanStepVSize1 "); + ReadByteV(adap, 0x041c,"bPanControl1 "); + ReadByteV(adap, 0x041e,"bCropControl1 "); + ReadByteV(adap, 0x0421,"uwManualCropHorizontalStart1 "); + ReadByteV(adap, 0x0425,"uwManualCropHorizontalSize1 "); + ReadByteV(adap, 0x0429,"uwManualCropVerticalStart1 "); + ReadByteV(adap, 0x042d,"uwManualCropVerticalSize1 "); + ReadByteV(adap, 0x0421,"bCropHStartMSB1 "); + ReadByteV(adap, 0x0422,"bCropHStartLSB1 "); + ReadByteV(adap, 0x0429,"bCropVStartMSB1 "); + ReadByteV(adap, 0x042a,"bCropVStartLSB1 "); + ReadByteV(adap, 0x0425,"bCropHSizeMSB1 "); + ReadByteV(adap, 0x0426,"bCropHSizeLSB1 "); + ReadByteV(adap, 0x042d,"bCropVSizeMSB1 "); + ReadByteV(adap, 0x042e,"bCropVSizeLSB1 "); + ReadByteV(adap, 0x0430,"bDataFormat1 "); + ReadByteV(adap, 0x0432,"bBayerOutputAlignment1 "); + ReadByteV(adap, 0x0434,"bContrast1 "); + ReadByteV(adap, 0x0436,"bColourSaturation1 "); + ReadByteV(adap, 0x0438,"bGamma1 "); + ReadByteV(adap, 0x043a,"fHorizontalMirror1 "); + ReadByteV(adap, 0x043c,"fVerticalFlip1 "); + ReadByteV(adap, 0x043e,"bChannelID1 "); + ReadByteV(adap, 0x0480,"fEnable "); + ReadByteV(adap, 0x0482,"bInitialPipeSetupBank "); + ReadByteV(adap, 0x0500,"CurrentPipeSetupBank "); + ReadByteV(adap, 0x0580,"bTimeToPowerdown "); + ReadByteV(adap, 0x058a,"fVRegSleep "); + ReadByteV(adap, 0x058c,"fSmoothLineReading "); + ReadByteV(adap, 0x0605,"uwExternalClockFrequencyMhzNumerator "); + ReadByteV(adap, 0x0605,"uwExternalClockFrequencyMhzNumeratorMSB "); + ReadByteV(adap, 0x0606,"uwExternalClockFrequencyMhzNumeratorLSB "); + ReadByteV(adap, 0x0608,"bExternalClockFrequencyMhzDenominator "); + ReadByteV(adap, 0x0681,"fpExternalClockFrequencyMhz "); + ReadByteV(adap, 0x0880,"bSysClkMode "); + ReadByteV(adap, 0x0882,"bMode "); + ReadByteV(adap, 0x0c80,"bLightingFrequencyHz "); + ReadByteV(adap, 0x0c82,"fFlickerCompatibleFrameLength "); + ReadByteV(adap, 0x0d05,"fpFlickerFreePeriod_us "); + ReadByteV(adap, 0x0d08,"fAntiFlickerEnabled "); + ReadByteV(adap, 0x0d81,"uwDesiredFrameRate_Num "); + ReadByteV(adap, 0x0d81,"uwDesiredFrameRate_Num_MSB "); + ReadByteV(adap, 0x0d82,"uwDesiredFrameRate_Num_LSB "); + ReadByteV(adap, 0x0d84,"bDesiredFrameRate_Den "); + ReadByteV(adap, 0x0e01,"fpRequestedFrameRate_Hz "); + ReadByteV(adap, 0x0e01,"fpRequestedFrameRate_Hz_MSB "); + ReadByteV(adap, 0x0e02,"fpRequestedFrameRate_Hz_LSB "); + ReadByteV(adap, 0x0e05,"fpMaxFrameRate_Hz "); + ReadByteV(adap, 0x0e09,"fpMinFrameRate_Hz "); + ReadByteV(adap, 0x0e0c,"fChangePending "); + ReadByteV(adap, 0x0e0f,"uwRequiredFrameLength_lines "); + ReadByteV(adap, 0x0e12,"ClipFrameRate "); + ReadByteV(adap, 0x0e80,"fDisableFrameRateDamper "); + ReadByteV(adap, 0x0e82,"bImpliedGainThresholdLow_num "); + ReadByteV(adap, 0x0e84,"bImpliedGainThresholdLow_den "); + ReadByteV(adap, 0x0e86,"bImpliedGainThresholdHigh_num "); + ReadByteV(adap, 0x0e88,"bImpliedGainThresholdHigh_den "); + ReadByteV(adap, 0x0e8a,"bUserMinimumFrameRate_Hz "); + ReadByteV(adap, 0x0e8c,"bUserMaximumFrameRate_Hz "); + ReadByteV(adap, 0x0e8e,"bRelativeChange_num "); + ReadByteV(adap, 0x0e90,"bRelativeChange_den "); + ReadByteV(adap, 0x0e92,"fDivorceMinFrameRateFromMaxIntegration "); + ReadByteV(adap, 0x0f01,"fpImpliedGain "); + ReadByteV(adap, 0x0f05,"uwMaximumFrameLength_lines "); + ReadByteV(adap, 0x0f09,"uwMinimumFrameLength_lines "); + ReadByteV(adap, 0x0f0d,"uwFrameLengthChange_lines "); + ReadByteV(adap, 0x0f11,"fpDesiredAutomaticFrameRate_Hz "); + ReadByteV(adap, 0x0f15,"uwCurrentFrameLength_lines "); + ReadByteV(adap, 0x0f19,"uwDesiredFrameLength_lines "); + ReadByteV(adap, 0x0f1c,"fAutomaticFrameRateStable "); + ReadByteV(adap, 0x0f1e,"fAutomaticFrameRateClip "); + ReadByteV(adap, 0x0f81,"uwXOffset "); + ReadByteV(adap, 0x0f85,"uwYOffset "); + ReadByteV(adap, 0x0f89,"uwXSize "); + ReadByteV(adap, 0x0f8d,"uwYSize "); + ReadByteV(adap, 0x1180,"ExposureControls_bMode "); + ReadByteV(adap, 0x1182,"bExposureMetering "); + ReadByteV(adap, 0x1184,"bManualExposureTime_s_num "); + ReadByteV(adap, 0x1186,"bManualExposureTime_s_den "); + ReadByteV(adap, 0x1189,"fpManualDesiredExposureTime_us "); + ReadByteV(adap, 0x1190,"iExposureCompensation "); + ReadByteV(adap, 0x1195,"uwDirectModeCoarseIntegration_lines "); + ReadByteV(adap, 0x1199,"uwDirectModeFineIntegration_pixels "); + ReadByteV(adap, 0x119d,"uwDirectModeCodedAnalogGain "); + ReadByteV(adap, 0x11a1,"fpDirectModeDigitalGain "); + ReadByteV(adap, 0x11a5,"uwFlashGunModeCoarseIntegration_lines "); + ReadByteV(adap, 0x11a9,"uwFlashGunModeFineIntegration_pixels "); + ReadByteV(adap, 0x11ad,"uwFlashGunModeCodedAnalogGain "); + ReadByteV(adap, 0x11b1,"fpFlashGunModeDigitalGain "); + ReadByteV(adap, 0x11b4,"fFreezeAutoExposure "); + ReadByteV(adap, 0x11b7,"fpUserMaximumIntegrationTime_us "); + ReadByteV(adap, 0x11bb,"fpRecommendFlashGunAnalogGainThreshold "); + ReadByteV(adap, 0x11be,"fEnableHighClipForDesiredExposureTime "); + ReadByteV(adap, 0x11c0,"bAntiFlickerMode "); + ReadByteV(adap, 0x1201,"fpMaximumStep "); + ReadByteV(adap, 0x1205,"fpMinimumStep "); + ReadByteV(adap, 0x1209,"fpMinimumDesiredExposureTime_us "); + ReadByteV(adap, 0x120d,"fpStepProportion "); + ReadByteV(adap, 0x1211,"fpMaximumNegativeStepThreshold "); + ReadByteV(adap, 0x1215,"fpRelativeOnTargetStabilityThreshold "); + ReadByteV(adap, 0x1219,"fpDigitalGainFloor "); + ReadByteV(adap, 0x121d,"fpDigitalGainCeiling "); + ReadByteV(adap, 0x1221,"fpRelativeIntTimeHysThreshold "); + ReadByteV(adap, 0x1225,"fpRelativeDigitalGainHysThreshold "); + ReadByteV(adap, 0x1229,"fpRelativeCompilationProblemThreshold "); + ReadByteV(adap, 0x122d,"fpRoundUpBunchFudge "); + ReadByteV(adap, 0x1231,"fpFineClampThreshold "); + ReadByteV(adap, 0x1235,"fpMaximumManualExposureTime_s "); + ReadByteV(adap, 0x1239,"fpRelativeStabilityThresholdForAutoFocus "); + ReadByteV(adap, 0x123c,"bLeakShift "); + ReadByteV(adap, 0x1281,"fpLeakyEnergy "); + ReadByteV(adap, 0x1285,"fpRelativeStep "); + ReadByteV(adap, 0x1309,"uwCoarseIntegrationPending_lines "); + ReadByteV(adap, 0x130d,"uwFineIntegrationPending_pixels "); + ReadByteV(adap, 0x1311,"fpAnalogGainPending "); + ReadByteV(adap, 0x1315,"fpDigitalGainPending "); + ReadByteV(adap, 0x1319,"fpDesiredExposureTime_us "); + ReadByteV(adap, 0x131d,"fpCompiledExposureTime_us "); + ReadByteV(adap, 0x132b,"uwCodedAnalogGainPending "); + ReadByteV(adap, 0x1480,"bWhiteBalanceMode "); + ReadByteV(adap, 0x1482,"bManualRedGain "); + ReadByteV(adap, 0x1484,"bManualGreenGain "); + ReadByteV(adap, 0x1486,"bManualBlueGain "); + ReadByteV(adap, 0x148b,"fpFlashRedGain "); + ReadByteV(adap, 0x148f,"fpFlashGreenGain "); + ReadByteV(adap, 0x1493,"fpFlashBlueGain "); + ReadByteV(adap, 0x1500,"bStatus "); + ReadByteV(adap, 0x1505,"fpRedGain "); + ReadByteV(adap, 0x1509,"fpGreenGain "); + ReadByteV(adap, 0x150d,"fpBlueGain "); + ReadByteV(adap, 0x1581,"fpStableTotalStepThreshold "); + ReadByteV(adap, 0x1585,"fpMinimumRelativeStep "); + ReadByteV(adap, 0x1589,"fpMaximumRelativeStep "); + ReadByteV(adap, 0x1601,"fpRedA "); + ReadByteV(adap, 0x1605,"fpBlueA "); + ReadByteV(adap, 0x1609,"fpRedB "); + ReadByteV(adap, 0x160d,"fpBlueB "); + ReadByteV(adap, 0x1611,"fpMaximumDistanceAllowedFromLocus "); + ReadByteV(adap, 0x1614,"fEnableConstrainedWhiteBalance "); + ReadByteV(adap, 0x1616,"bACCSRCCtrl "); + ReadByteV(adap, 0x1681,"fpOutputRedGain "); + ReadByteV(adap, 0x1685,"fpOutputGreenGain "); + ReadByteV(adap, 0x1689,"fpOutputBlueGain "); + ReadByteV(adap, 0x168c,"fAreGainsConstrained "); + ReadByteV(adap, 0x1701,"fpGradientOfLocusAB "); + ReadByteV(adap, 0x1705,"fpDistanceOfInputPointFromLocusAB "); + ReadByteV(adap, 0x1709,"fpConstrainedRedPoint "); + ReadByteV(adap, 0x170d,"fpConstrainedBluePoint "); + ReadByteV(adap, 0x1880,"bMaxNumberOfFramesToWaitForStability "); + ReadByteV(adap, 0x1900,"fWhiteBalanceStable "); + ReadByteV(adap, 0x1902,"fExposureStable "); + ReadByteV(adap, 0x1904,"fDarkCalStable "); + ReadByteV(adap, 0x1906,"fStable "); + ReadByteV(adap, 0x1908,"fForcedStablility "); + ReadByteV(adap, 0x1985,"fpRedTilt "); + ReadByteV(adap, 0x1989,"fpGreenTilt "); + ReadByteV(adap, 0x198d,"fpBlueTilt "); + ReadByteV(adap, 0x1990,"bBlackCorrectionOffset "); + ReadByteV(adap, 0x1a01,"uwSensorAnalogGainFloor "); + ReadByteV(adap, 0x1a05,"uwSensorAnalogGainCeiling "); + ReadByteV(adap, 0x1a80,"bFlashMode "); + ReadByteV(adap, 0x1a83,"uwFlashOffLine "); + ReadByteV(adap, 0x1b00,"fFlashRecommended "); + ReadByteV(adap, 0x1b02,"fFlashGrabComplete "); + ReadByteV(adap, 0x1d01,"uwHorizontalOffset "); + ReadByteV(adap, 0x1d05,"uwVerticalOffset "); + ReadByteV(adap, 0x1d08,"iR2RCoefficient "); + ReadByteV(adap, 0x1d0a,"iR2GRCoefficient "); + ReadByteV(adap, 0x1d0c,"iR2GBCoefficient "); + ReadByteV(adap, 0x1d0e,"iR2BCoefficient "); + ReadByteV(adap, 0x1d10,"iR4RCoefficient "); + ReadByteV(adap, 0x1d12,"iR4GRCoefficient "); + ReadByteV(adap, 0x1d14,"iR4GBCoefficient "); + ReadByteV(adap, 0x1d16,"iR4BCoefficient "); + ReadByteV(adap, 0x1d80,"ScythefDisableFilter "); + ReadByteV(adap, 0x1e00,"JackfDisableFilter "); + ReadByteV(adap, 0x1e80,"bAntiAliasFilterSuppress "); + ReadByteV(adap, 0x1f00,"ColourMatrixDamperfDisable "); + ReadByteV(adap, 0x1f03,"fpLowThreshold "); + ReadByteV(adap, 0x1f07,"fpHighThreshold "); + ReadByteV(adap, 0x1f0b,"fpMinimumOutput "); + ReadByteV(adap, 0x1f81,"fpGInR "); + ReadByteV(adap, 0x1f85,"fpBInR "); + ReadByteV(adap, 0x1f89,"fpRInG "); + ReadByteV(adap, 0x1f8d,"fpBInG "); + ReadByteV(adap, 0x1f91,"fpRInB "); + ReadByteV(adap, 0x1f95,"fpGInB "); + ReadByteV(adap, 0x2000,"bUserPeakGain "); + ReadByteV(adap, 0x2002,"fDisableGainDamping "); + ReadByteV(adap, 0x2005,"fpDamperLowThreshold_Gain "); + ReadByteV(adap, 0x2009,"fpDamperHighThreshold_Gain "); + ReadByteV(adap, 0x200d,"fpMinimumDamperOutput_Gain "); + ReadByteV(adap, 0x2010,"bUserPeakLoThresh "); + ReadByteV(adap, 0x2012,"fDisableCoringDamping "); + ReadByteV(adap, 0x2014,"bUserPeakHiThresh "); + ReadByteV(adap, 0x2017,"fpDamperLowThreshold_Coring "); + ReadByteV(adap, 0x201b,"fpDamperHighThreshold_Coring "); + ReadByteV(adap, 0x201f,"fpMinimumDamperOutput_Coring "); + ReadByteV(adap, 0x2022,"bBlockControl "); + ReadByteV(adap, 0x2280,"fGammaManuCtrl0 "); + ReadByteV(adap, 0x2282,"bRPeakGamma0 "); + ReadByteV(adap, 0x2284,"bGPeakGamma0 "); + ReadByteV(adap, 0x2286,"bBPeakGamma0 "); + ReadByteV(adap, 0x2288,"bRUnPeakGamma0 "); + ReadByteV(adap, 0x228a,"bGUnPeakGamma0 "); + ReadByteV(adap, 0x228c,"bBUnPeakGamma0 "); + ReadByteV(adap, 0x2294,"bYuvSetup MTW "); + ReadByteV(adap, 0x2300,"fGammaManuCtrl1 "); + ReadByteV(adap, 0x2302,"bRPeakGamma1 "); + ReadByteV(adap, 0x2304,"bGPeakGamma1 "); + ReadByteV(adap, 0x2306,"bBPeakGamma1 "); + ReadByteV(adap, 0x2308,"bRUnPeakGamma1 "); + ReadByteV(adap, 0x230a,"bGUnPeakGamma1 "); + ReadByteV(adap, 0x230c,"bBUnPeakGamma1 "); + ReadByteV(adap, 0x2381,"uwLumaExcursion0 "); + ReadByteV(adap, 0x2385,"uwLumaMidpointTimes20 "); + ReadByteV(adap, 0x2389,"uwChromaExcursion0 "); + ReadByteV(adap, 0x238d,"uwChromaMidpointTimes20 "); + ReadByteV(adap, 0x2401,"uwLumaExcursion1 "); + ReadByteV(adap, 0x2405,"uwLumaMidpointTimes21 "); + ReadByteV(adap, 0x2409,"uwChromaExcursion1 "); + ReadByteV(adap, 0x240d,"uwChromaMidpointTimes21 "); + ReadByteV(adap, 0x2480,"FadeToBlackfDisable "); + ReadByteV(adap, 0x2483,"fpBlackValue "); + ReadByteV(adap, 0x2487,"fpDamperLowThreshold "); + ReadByteV(adap, 0x248b,"fpDamperHighThreshold "); + ReadByteV(adap, 0x248f,"fpDamperOutput "); + ReadByteV(adap, 0x2580,"bCodeCheckEn "); + ReadByteV(adap, 0x2582,"bBlankFormat "); + ReadByteV(adap, 0x2584,"bSyncCodeSetup "); + ReadByteV(adap, 0x2586,"bHSyncSetup "); + ReadByteV(adap, 0x2588,"bVSyncSetup "); + ReadByteV(adap, 0x258a,"bPClkSetup "); + ReadByteV(adap, 0x258c,"fPclkEn "); + ReadByteV(adap, 0x258e,"bOpfSpSetup "); + ReadByteV(adap, 0x2590,"bBlankData_MSB "); + ReadByteV(adap, 0x2592,"bBlankData_LSB "); + ReadByteV(adap, 0x2594,"bRgbSetup "); + ReadByteV(adap, 0x2596,"bYuvSetup "); + ReadByteV(adap, 0x2598,"bVsyncRisingCoarseH "); + ReadByteV(adap, 0x259a,"bVsyncRisingCoarseL "); + ReadByteV(adap, 0x259c,"bVsyncRisingFineH "); + ReadByteV(adap, 0x259e,"bVsyncRisingFineL "); + ReadByteV(adap, 0x25a0,"bVsyncFallingCoarseH "); + ReadByteV(adap, 0x25a2,"bVsyncFallingCoarseL "); + ReadByteV(adap, 0x25a4,"bVsyncFallingFineH "); + ReadByteV(adap, 0x25a6,"bVsyncFallingFineL "); + ReadByteV(adap, 0x25a8,"bHsyncRisingH "); + ReadByteV(adap, 0x25aa,"bHsyncRisingL "); + ReadByteV(adap, 0x25ac,"bHsyncFallingH "); + ReadByteV(adap, 0x25ae,"bHsyncFallingL "); + ReadByteV(adap, 0x25b0,"bOutputInterface "); + ReadByteV(adap, 0x25b2,"bCCPExtraData "); + ReadByteV(adap, 0x2600,"NoRAfDisable "); + ReadByteV(adap, 0x2602,"bUsage "); + ReadByteV(adap, 0x2604,"bSplit_Kn "); + ReadByteV(adap, 0x2606,"bSplit_Nl "); + ReadByteV(adap, 0x2608,"bTight_Green "); + ReadByteV(adap, 0x260a,"fDisableNoraPromoting "); + ReadByteV(adap, 0x260d,"DamperLowThreshold "); + ReadByteV(adap, 0x2611,"DamperHighThreshold "); + ReadByteV(adap, 0x2615,"MinimumDamperOutput "); + + return; +} --- /dev/null +++ git/drivers/bmi/pims/camera/vs6624_access.h @@ -0,0 +1,17 @@ +#ifndef VS6624_ACCESS_H +#define VS6624_ACCESS_H + +#include +void vs6624_get_color(struct i2c_adapter *adap, int *bright, int *saturation, int *red, int *green, int *blue); +void vs6624_set_color(struct i2c_adapter *adap, int bright, int saturation, int red, int green, int blue); + + + +void vs6624_patch (struct i2c_adapter *adap); +int vs6624_ReadByte(struct i2c_adapter *adap, unsigned short offset, unsigned char *data); +int vs6624_WriteByte(struct i2c_adapter *adap, unsigned short offset, unsigned char data); +int vs6624_WriteSequence(struct i2c_adapter *adap, const unsigned short array[][2], unsigned short len); +void vs6624_dump_regs(struct i2c_adapter *adap); + +#endif + --- /dev/null +++ git/drivers/bmi/pims/camera/vs6624_patch.c @@ -0,0 +1,373 @@ +static const unsigned short patch_p1[][2] = { + {0x8104, 3}, + {0x8105, 1}, + {0xc900, 0x03}, + {0xc904, 0x47}, + {0xc905, 0x10}, + {0xc906, 0x80}, + {0xc907, 0x3a}, + {0x903a, 0x02}, + {0x903b, 0x47}, + {0x903c, 0x15}, + {0xc908, 0x31}, + {0xc909, 0xdc}, + {0xc90a, 0x80}, + {0xc90b, 0x44}, + {0x9044, 0x02}, + {0x9045, 0x31}, + {0x9046, 0xe2}, + {0xc90c, 0x07}, + {0xc90d, 0xe0}, + {0xc90e, 0x80}, + {0xc90f, 0x47}, + {0x9047, 0x90}, + {0x9048, 0x83}, + {0x9049, 0x81}, + {0x904a, 0xe0}, + {0x904b, 0x60}, + {0x904c, 0x08}, + {0x904d, 0x90}, + {0x904e, 0xc0}, + {0x904f, 0x43}, + {0x9050, 0x74}, + {0x9051, 0x01}, + {0x9052, 0xf0}, + {0x9053, 0x80}, + {0x9054, 0x05}, + {0x9055, 0xE4}, + {0x9056, 0x90}, + {0x9057, 0xc0}, + {0x9058, 0x43}, + {0x9059, 0xf0}, + {0x905a, 0x02}, + {0x905b, 0x07}, + {0x905c, 0xec}, + {0xc910, 0x5d}, + {0xc911, 0xca}, + {0xc912, 0x80}, + {0xc913, 0x5d}, + {0x905d, 0xa3}, + {0x905e, 0x04}, + {0x905f, 0xf0}, + {0x9060, 0xa3}, + {0x9061, 0x04}, + {0x9062, 0xf0}, + {0x9063, 0x22}, + {0xc914, 0x72}, + {0xc915, 0x92}, + {0xc916, 0x80}, + {0xc917, 0x64}, + {0x9064, 0x74}, + {0x9065, 0x01}, + {0x9066, 0x02}, + {0x9067, 0x72}, + {0x9068, 0x95}, + {0xc918, 0x47}, + {0xc919, 0xf2}, + {0xc91a, 0x81}, + {0xc91b, 0x69}, + {0x9169, 0x74}, + {0x916a, 0x02}, + {0x916b, 0xf0}, + {0x916c, 0xec}, + {0x916d, 0xb4}, + {0x916e, 0x10}, + {0x916f, 0x0a}, + {0x9170, 0x90}, + {0x9171, 0x80}, + {0x9172, 0x16}, + {0x9173, 0xe0}, + {0x9174, 0x70}, + {0x9175, 0x04}, + {0x9176, 0x90}, + {0x9177, 0xd3}, + {0x9178, 0xc4}, + {0x9179, 0xf0}, + {0x917a, 0x22}, + {0xc91c, 0x0a}, + {0xc91d, 0xbe}, + {0xc91e, 0x80}, + {0xc91f, 0x73}, + {0x9073, 0xfc}, + {0x9074, 0xa3}, + {0x9075, 0xe0}, + {0x9076, 0xf5}, + {0x9077, 0x82}, + {0x9078, 0x8c}, + {0x9079, 0x83}, + {0x907a, 0xa3}, + {0x907b, 0xa3}, + {0x907c, 0xe0}, + {0x907d, 0xfc}, + {0x907e, 0xa3}, + {0x907f, 0xe0}, + {0x9080, 0xc3}, + {0x9081, 0x9f}, + {0x9082, 0xff}, + {0x9083, 0xec}, + {0x9084, 0x9e}, + {0x9085, 0xfe}, + {0x9086, 0x02}, + {0x9087, 0x0a}, + {0x9088, 0xea}, + {0xc920, 0x47}, + {0xc921, 0x38}, + {0xc922, 0x80}, + {0xc923, 0x89}, + {0x9089, 0xec}, + {0x908a, 0xd3}, + {0x908b, 0x94}, + {0x908c, 0x20}, + {0x908d, 0x40}, + {0x908e, 0x01}, + {0x908f, 0x1c}, + {0x9090, 0x90}, + {0x9091, 0xd3}, + {0x9092, 0xd4}, + {0x9093, 0xec}, + {0x9094, 0xf0}, + {0x9095, 0x02}, + {0x9096, 0x47}, + {0x9097, 0x3d}, + {0xc924, 0x45}, + {0xc925, 0xca}, + {0xc926, 0x80}, + {0xc927, 0x98}, + {0x9098, 0x12}, + {0x9099, 0x77}, + {0x909a, 0xd6}, + {0x909b, 0x02}, + {0x909c, 0x45}, + {0x909d, 0xcd}, + {0xc928, 0x20}, + {0xc929, 0xd5}, + {0xc92a, 0x80}, + {0xc92b, 0x9e}, + {0x909e, 0x90}, + {0x909f, 0x82}, + {0x90a0, 0x18}, + {0x90a1, 0xe0}, + {0x90a2, 0xb4}, + {0x90a3, 0x03}, + {0x90a4, 0x0e}, + {0x90a5, 0x90}, + {0x90a6, 0x83}, + {0x90a7, 0xbf}, + {0x90a8, 0xe0}, + {0x90a9, 0x60}, + {0x90aa, 0x08}, + {0x90ab, 0x90}, + {0x90ac, 0x81}, + {0x90ad, 0xfc}, + {0x90ae, 0xe0}, + {0x90af, 0xff}, + {0x90b0, 0xc3}, + {0x90b1, 0x13}, + {0x90b2, 0xf0}, + {0x90b3, 0x90}, + {0x90b4, 0x81}, + {0x90b5, 0xfc}, + {0x90b6, 0xe0}, + {0x90b7, 0xff}, + {0x90b8, 0x02}, + {0x90b9, 0x20}, + {0x90ba, 0xda}, + {0xc92c, 0x70}, + {0xc92d, 0xbc}, + {0xc92e, 0x80}, + {0xc92f, 0xbb}, + {0x90bb, 0x90}, + {0x90bc, 0x82}, + {0x90bd, 0x18}, + {0x90be, 0xe0}, + {0x90bf, 0xb4}, + {0x90c0, 0x03}, + {0x90c1, 0x06}, + {0x90c2, 0x90}, + {0x90c3, 0xc1}, + {0x90c4, 0x06}, + {0x90c5, 0x74}, + {0x90c6, 0x05}, + {0x90c7, 0xf0}, + {0x90c8, 0x90}, + {0x90c9, 0xd3}, + {0x90ca, 0xa0}, + {0x90cb, 0x02}, + {0x90cc, 0x70}, + {0x90cd, 0xbf}, + {0xc930, 0x72}, + {0xc931, 0x21}, + {0xc932, 0x81}, + {0xc933, 0x3b}, + {0x913b, 0x7d}, + {0x913c, 0x02}, + {0x913d, 0x7f}, + {0x913e, 0x7b}, + {0x913f, 0x02}, + {0x9140, 0x72}, + {0x9141, 0x25}, + {0xc934, 0x28}, + {0xc935, 0xae}, + {0xc936, 0x80}, + {0xc937, 0xd2}, + {0x90d2, 0xf0}, + {0x90d3, 0x90}, + {0x90d4, 0xd2}, + {0x90d5, 0x0a}, + {0x90d6, 0x02}, + {0x90d7, 0x28}, + {0x90d8, 0xb4}, + {0xc938, 0x28}, + {0xc939, 0xb1}, + {0xc93a, 0x80}, + {0xc93b, 0xd9}, + {0x90d9, 0x90}, + {0x90da, 0x83}, + {0x90db, 0xba}, + {0x90dc, 0xe0}, + {0x90dd, 0xff}, + {0x90de, 0x90}, + {0x90df, 0xd2}, + {0x90e0, 0x08}, + {0x90e1, 0xe0}, + {0x90e2, 0xe4}, + {0x90e3, 0xef}, + {0x90e4, 0xf0}, + {0x90e5, 0xa3}, + {0x90e6, 0xe0}, + {0x90e7, 0x74}, + {0x90e8, 0xff}, + {0x90e9, 0xf0}, + {0x90ea, 0x90}, + {0x90eb, 0xd2}, + {0x90ec, 0x0a}, + {0x90ed, 0x02}, + {0x90ee, 0x28}, + {0x90ef, 0xb4}, + {0xc93c, 0x29}, + {0xc93d, 0x79}, + {0xc93e, 0x80}, + {0xc93f, 0xf0}, + {0x90f0, 0xf0}, + {0x90f1, 0x90}, + {0x90f2, 0xd2}, + {0x90f3, 0x0e}, + {0x90f4, 0x02}, + {0x90f5, 0x29}, + {0x90f6, 0x7f}, + {0xc940, 0x29}, + {0xc941, 0x7c}, + {0xc942, 0x80}, + {0xc943, 0xf7}, + {0x90f7, 0x90}, + {0x90f8, 0x83}, + {0x90f9, 0xba}, + {0x90fa, 0xe0}, + {0x90fb, 0xff}, + {0x90fc, 0x90}, + {0x90fd, 0xd2}, + {0x90fe, 0x0c}, + {0x90ff, 0xe0}, + {0x9100, 0xe4}, + {0x9101, 0xef}, + {0x9102, 0xf0}, + {0x9103, 0xa3}, + {0x9104, 0xe0}, + {0x9105, 0x74}, + {0x9106, 0xff}, + {0x9107, 0xf0}, + {0x9108, 0x90}, + {0x9109, 0xd2}, + {0x910a, 0x0e}, + {0x910b, 0x02}, + {0x910c, 0x29}, + {0x910d, 0x7f}, + {0xc944, 0x2a}, + {0xc945, 0x42}, + {0xc946, 0x81}, + {0xc947, 0x0e}, + {0x910e, 0xf0}, + {0x910f, 0x90}, + {0x9110, 0xd2}, + {0x9111, 0x12}, + {0x9112, 0x02}, + {0x9113, 0x2a}, + {0x9114, 0x48}, + {0xc948, 0x2a}, + {0xc949, 0x45}, + {0xc94a, 0x81}, + {0xc94b, 0x15}, + {0x9115, 0x90}, + {0x9116, 0x83}, + {0x9117, 0xba}, + {0x9118, 0xe0}, + {0x9119, 0xff}, + {0x911a, 0x90}, + {0x911b, 0xd2}, + {0x911c, 0x10}, + {0x911d, 0xe0}, + {0x911e, 0xe4}, + {0x911f, 0xef}, + {0x9120, 0xf0}, + {0x9121, 0xa3}, + {0x9122, 0xe0}, + {0x9123, 0x74}, + {0x9124, 0xff}, + {0x9125, 0xf0}, + {0x9126, 0x90}, + {0x9127, 0xd2}, + {0x9128, 0x12}, + {0x9129, 0x02}, + {0x912a, 0x2a}, + {0x912b, 0x48}, + {0xc900, 0x01} +}; + +static const unsigned short patch_p2[][2] = { + {0x806f, 0x01}, + {0x058c, 0x01} +}; + +static const unsigned short patch_run_setup[][2] = { + {0x2596, 0x01}, /* U first */ + {0x1d18, 0x00}, /* Enableconstrainedwhitebalance */ + {0x200d, 0x3c}, /* Damper PeakGain Output MSB */ + {0x200e, 0x66}, /* Damper PeakGain Output LSB */ + {0x1f03, 0x65}, /* Damper Low MSB */ + {0x1f04, 0xd1}, /* Damper Low LSB */ + {0x1f07, 0x66}, /* Damper High MSB */ + {0x1f08, 0x62}, /* Damper High LSB */ + {0x1f0b, 0x00}, /* Damper Min output MSB */ + {0x1f0c, 0x00}, /* Damper Min output LSB */ + {0x2600, 0x00}, /* Nora fDisable */ + {0x2602, 0x04}, /* Nora usage */ + {0x260d, 0x63}, /* Damper Low MSB Changed 0x63 to 0x65 */ + {0x260e, 0xd1}, /* Damper Low LSB */ + {0x2611, 0x68}, /* Damper High MSB */ + {0x2612, 0xdd}, /* Damper High LSB */ + {0x2615, 0x3a}, /* Damper Min output MSB */ + {0x2616, 0x00}, /* Damper Min output LSB */ + {0x2480, 0x00}, /* Disable */ + {0x1d8a, 0x30}, /* MAXWeightHigh */ + {0x1d91, 0x62}, /* fpDamperLowThresholdHigh MSB */ + {0x1d92, 0x4a}, /* fpDamperLowThresholdHigh LSB */ + {0x1d95, 0x65}, /* fpDamperHighThresholdHigh MSB */ + {0x1d96, 0x0e}, /* fpDamperHighThresholdHigh LSB */ + {0x1da1, 0x3a}, /* fpMinimumDamperOutputLow MSB */ + {0x1da2, 0xb8}, /* fpMinimumDamperOutputLow LSB */ + {0x1e08, 0x06}, /* MAXWeightLow */ + {0x1e0a, 0x0a}, /* MAXWeightHigh */ + {0x1601, 0x3a}, /* Red A MSB */ + {0x1602, 0x14}, /* Red A LSB */ + {0x1605, 0x3b}, /* Blue A MSB */ + {0x1606, 0x85}, /* BLue A LSB */ + {0x1609, 0x3b}, /* RED B MSB */ + {0x160a, 0x85}, /* RED B LSB */ + {0x160d, 0x3a}, /* Blue B MSB */ + {0x160e, 0x14}, /* Blue B LSB */ + {0x1611, 0x30}, /* Max Distance from Locus MSB */ + {0x1612, 0x8f}, /* Max Distance from Locus MSB */ + {0x1614, 0x01} /* Enable constrainer */ +}; + + --- /dev/null +++ git/drivers/bmi/pims/camera/vs6624_regs.h @@ -0,0 +1,467 @@ +/* + * File: drivers/media/video/mxc/capture/_vs6624.h + * Author: Peter Giacomini + * + * This is the header file for the ST VS6624 camera on the + * MX31 BUG platform. It is derived from the following + * Blackfin and MX31 sources: + * + */ + +/* + * + * + * Based on: drivers/media/video/blackfin/vs6624.h + * Author: Michael Hennerich + * + * Created: + * Description: Command driver for STM VS6624 sensor + * + * + * Modified: + * Copyright 2004-2007 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup Camera Sensor Drivers + */ + +/*! + * @file mt9v111.h + * + * @brief MT9V111 Camera Header file + * + * It include all the defines for bitmaps operations, also two main structure + * one for IFP interface structure, other for sensor core registers. + * + * @ingroup Camera + */ + +#ifndef _VS6624_H +#define _VS6624_H + +/* I2C Slave Address */ +#define VS6624_I2C_ADDRESS 0x10 // 7-bit address +#define SENSOR_NAME "VS6624" + +//pjg #define USE_ITU656 + +/* VS6624 register definitions */ +#define VS6624_ID 624 +#define PWR_MAN_SETUP_MODE_SELECT 0xc003 /* (7:0) */ +#define PWR_MAN_DIO_ENABLE 0xc044 /* (7:0) */ +#define uwDeviceId 0x0001 /* (7:0) IndexLo 0x0002 */ +#define DeviceID_MSB 0x0001 /* (7:0) */ +#define DeviceID_LSB 0x0002 /* (7:0) */ +#define bFirmwareVsnMajor 0x0004 /* (7:0) */ +#define bFirmwareVsnMinor 0x0006 /* (7:0) */ +#define bPatchVsnMajor 0x0008 /* (7:0) */ +#define bPatchVsnMinor 0x000a /* (7:0) */ + +#define bUserCommand 0x0180 /* (7:0) */ +#define bManualNextState 0x0186 /* (7:0) */ + +#define bNextState 0x0200 /* (7:0) */ +#define bState 0x0202 /* (7:0) */ + +#define fMeteringOn 0x0280 /* (7:0) */ +#define fExitOnStable 0x0282 /* (7:0) */ +#define bStreamLength 0x0284 /* (7:0) */ + +#define fIsColdStart 0x0300 /* (7:0) */ +#define bNonViewLive_ActivePipeSetupBank 0x0302 /* (7:0) */ +#define bSnapShoot_ActivePipeSetupBank 0x0304 /* (7:0) */ +#define fSnapShoot_NoWaiting 0x0306 /* (7:0) */ +#define SensorMode 0x0308 /* (7:0) */ + +#define bImageSize0 0x0380 /* (7:0) */ +#define uwManualHSize0 0x0383 /* (7:0) IndexLo 0x0384 */ +#define uwManualHSizeMSB0 0x0383 /* (7:0) */ +#define uwManualHSizeLSB0 0x0384 /* (7:0) */ +#define uwManualVSize0 0x0387 /* (7:0) IndexLo 0x0388 */ +#define uwManualVSizeMSB0 0x0387 /* (7:0) */ +#define uwManualVSizeLSB0 0x0388 /* (7:0) */ +#define uwZoomStepHSize0 0x038b /* (7:0) IndexLo 0x038c */ +#define uwZoomStepHSizeMSB0 0x038b /* (7:0) */ +#define uwZoomStepHSizeLSB0 0x038c /* (7:0) */ +#define uwZoomStepVSize0 0x038f /* (7:0) IndexLo 0x0390 */ +#define uwZoomStepVSizeMSB0 0x038f /* (7:0) */ +#define uwZoomStepVSizeLSB0 0x0390 /* (7:0) */ +#define bZoomControl0 0x0392 /* (7:0) */ +#define uwPanStepHSize0 0x0395 /* (7:0) IndexLo 0x0396 */ +#define uwPanStepHSizeMSB0 0x0395 /* (7:0) */ +#define uwPanStepHSizeLSB0 0x0396 /* (7:0) */ +#define uwPanStepVSize0 0x0399 /* (7:0) IndexLo 0x039a */ +#define uwPanStepVSizeMSB0 0x0399 /* (7:0) */ +#define uwPanStepVSizeLSB0 0x039a /* (7:0) */ +#define bPanControl0 0x039c /* (7:0) */ +#define bCropControl0 0x039e /* (7:0) */ +#define uwManualCropHorizontalStart0 0x03a1 /* (7:0) IndexLo 0x03a2 */ +#define uwManualCropHorizontalSize0 0x03a5 /* (7:0) IndexLo 0x03a6 */ +#define uwManualCropVerticalStart0 0x03a9 /* (7:0) IndexLo 0x03aa */ +#define uwManualCropVerticalSize0 0x03ad /* (7:0) IndexLo 0x03ae */ +#define bCropHStartMSB0 0x03a1 /* (7:0) */ +#define bCropHStartLSB0 0x03a2 /* (7:0) */ +#define bCropVStartMSB0 0x03a9 /* (7:0) */ +#define bCropVStartLSB0 0x03aa /* (7:0) */ +#define bCropHSizeMSB0 0x03a5 /* (7:0) */ +#define bCropHSizeLSB0 0x03a6 /* (7:0) */ +#define bCropVSizeMSB0 0x03ad /* (7:0) */ +#define bCropVSizeLSB0 0x03ae /* (7:0) */ +#define bDataFormat0 0x03b0 /* (7:0) */ +#define bBayerOutputAlignment0 0x03b2 /* (7:0) */ +#define bContrast0 0x03b4 /* (7:0) */ +#define bColourSaturation0 0x03b6 /* (7:0) */ +#define bGamma0 0x03b8 /* (7:0) */ +#define fHorizontalMirror0 0x03ba /* (7:0) */ +#define fVerticalFlip0 0x03bc /* (7:0) */ +#define bChannelID0 0x03be /* (7:0) */ + +#define bImageSize1 0x0400 /* (7:0) */ +#define uwManualHSize1 0x0403 /* (7:0) IndexLo 0x0404 */ +#define uwManualVSize1 0x0407 /* (7:0) IndexLo 0x0408 */ +#define uwZoomStepHSize1 0x040b /* (7:0) IndexLo 0x040c */ +#define uwZoomStepVSize1 0x040f /* (7:0) IndexLo 0x0410 */ +#define bZoomControl1 0x0412 /* (7:0) */ +#define uwPanStepHSize1 0x0415 /* (7:0) IndexLo 0x0416 */ +#define uwPanStepVSize1 0x0419 /* (7:0) IndexLo 0x041a */ +#define bPanControl1 0x041c /* (7:0) */ +#define bCropControl1 0x041e /* (7:0) */ +#define uwManualCropHorizontalStart1 0x0421 /* (7:0) IndexLo 0x0422 */ +#define uwManualCropHorizontalSize1 0x0425 /* (7:0) IndexLo 0x0426 */ +#define uwManualCropVerticalStart1 0x0429 /* (7:0) IndexLo 0x042a */ +#define uwManualCropVerticalSize1 0x042d /* (7:0) IndexLo 0x042e */ +#define bCropHStartMSB1 0x0421 /* (7:0) */ +#define bCropHStartLSB1 0x0422 /* (7:0) */ +#define bCropVStartMSB1 0x0429 /* (7:0) */ +#define bCropVStartLSB1 0x042a /* (7:0) */ +#define bCropHSizeMSB1 0x0425 /* (7:0) */ +#define bCropHSizeLSB1 0x0426 /* (7:0) */ +#define bCropVSizeMSB1 0x042d /* (7:0) */ +#define bCropVSizeLSB1 0x042e /* (7:0) */ +#define bDataFormat1 0x0430 /* (7:0) */ +#define bBayerOutputAlignment1 0x0432 /* (7:0) */ +#define bContrast1 0x0434 /* (7:0) */ +#define bColourSaturation1 0x0436 /* (7:0) */ +#define bGamma1 0x0438 /* (7:0) */ +#define fHorizontalMirror1 0x043a /* (7:0) */ +#define fVerticalFlip1 0x043c /* (7:0) */ +#define bChannelID1 0x043e /* (7:0) */ + +#define fEnable 0x0480 /* (7:0) */ +#define bInitialPipeSetupBank 0x0482 /* (7:0) */ + +#define CurrentPipeSetupBank 0x0500 /* (7:0) */ + +#define bTimeToPowerdown 0x0580 /* (7:0) */ +#define fVRegSleep 0x058a /* (7:0) */ +#define fSmoothLineReading 0x058c /* (7:0) */ + +#define uwExternalClockFrequencyMhzNumerator 0x0605 /* (7:0) IndexLo 0x0606 */ +#define uwExternalClockFrequencyMhzNumeratorMSB 0x0605 /* (7:0) IndexLo 0x0606 */ +#define uwExternalClockFrequencyMhzNumeratorLSB 0x0606 /* (7:0) IndexLo 0x0606 */ +#define bExternalClockFrequencyMhzDenominator 0x0608 /* (7:0) */ + +#define fpExternalClockFrequencyMhz 0x0681 /* (7:0) IndexLo 0x0682 */ + +#define bSysClkMode 0x0880 /* (7:0) */ +#define bMode 0x0882 /* (7:0) */ +#define bLightingFrequencyHz 0x0c80 /* (7:0) */ +#define fFlickerCompatibleFrameLength 0x0c82 /* (7:0) */ + +#define fpFlickerFreePeriod_us 0x0d05 /* (7:0) IndexLo 0x0d06 */ +#define fAntiFlickerEnabled 0x0d08 /* (7:0) */ + +#define uwDesiredFrameRate_Num 0x0d81 /* (7:0) IndexLo 0x0d82 */ +#define uwDesiredFrameRate_Num_MSB 0x0d81 /* (7:0) */ +#define uwDesiredFrameRate_Num_LSB 0x0d82 /* (7:0) */ +#define bDesiredFrameRate_Den 0x0d84 /* (7:0) */ + +#define fpRequestedFrameRate_Hz 0x0e01 /* (7:0) IndexLo 0x0e02 */ +#define fpRequestedFrameRate_Hz_MSB 0x0e01 /* (7:0) */ +#define fpRequestedFrameRate_Hz_LSB 0x0e02 /* (7:0) */ +#define fpMaxFrameRate_Hz 0x0e05 /* (7:0) IndexLo 0x0e06 */ +#define fpMinFrameRate_Hz 0x0e09 /* (7:0) IndexLo 0x0e0a */ +#define fChangePending 0x0e0c /* (7:0) */ +#define uwRequiredFrameLength_lines 0x0e0f /* (7:0) IndexLo 0x0e10 */ +#define ClipFrameRate 0x0e12 /* (7:0) */ + +#define fDisableFrameRateDamper 0x0e80 /* (7:0) */ +#define bImpliedGainThresholdLow_num 0x0e82 /* (7:0) */ +#define bImpliedGainThresholdLow_den 0x0e84 /* (7:0) */ +#define bImpliedGainThresholdHigh_num 0x0e86 /* (7:0) */ +#define bImpliedGainThresholdHigh_den 0x0e88 /* (7:0) */ +#define bUserMinimumFrameRate_Hz 0x0e8a /* (7:0) */ +#define bUserMaximumFrameRate_Hz 0x0e8c /* (7:0) */ +#define bRelativeChange_num 0x0e8e /* (7:0) */ +#define bRelativeChange_den 0x0e90 /* (7:0) */ +#define fDivorceMinFrameRateFromMaxIntegration 0x0e92 /* (7:0) */ + +#define fpImpliedGain 0x0f01 /* (7:0) IndexLo 0x0f02 */ +#define uwMaximumFrameLength_lines 0x0f05 /* (7:0) IndexLo 0x0f06 */ +#define uwMinimumFrameLength_lines 0x0f09 /* (7:0) IndexLo 0x0f0a */ +#define uwFrameLengthChange_lines 0x0f0d /* (7:0) IndexLo 0x0f0e */ +#define fpDesiredAutomaticFrameRate_Hz 0x0f11 /* (7:0) IndexLo 0x0f12 */ +#define uwCurrentFrameLength_lines 0x0f15 /* (7:0) IndexLo 0x0f16 */ +#define uwDesiredFrameLength_lines 0x0f19 /* (7:0) IndexLo 0x0f1a */ +#define fAutomaticFrameRateStable 0x0f1c /* (7:0) */ +#define fAutomaticFrameRateClip 0x0f1e /* (7:0) */ + +#define uwXOffset 0x0f81 /* (7:0) IndexLo 0x0f82 */ +#define uwYOffset 0x0f85 /* (7:0) IndexLo 0x0f86 */ +#define uwXSize 0x0f89 /* (7:0) IndexLo 0x0f8a */ +#define uwYSize 0x0f8d /* (7:0) IndexLo 0x0f8e */ + +#define ExposureControls_bMode 0x1180 /* (7:0) */ +#define bExposureMetering 0x1182 /* (7:0) */ +#define bManualExposureTime_s_num 0x1184 /* (7:0) */ +#define bManualExposureTime_s_den 0x1186 /* (7:0) */ +#define fpManualDesiredExposureTime_us 0x1189 /* (7:0) IndexLo 0x118a */ +#define iExposureCompensation 0x1190 /* (7:0) Signed */ +#define uwDirectModeCoarseIntegration_lines 0x1195 /* (7:0) IndexLo 0x1196 */ +#define uwDirectModeFineIntegration_pixels 0x1199 /* (7:0) IndexLo 0x119a */ +#define uwDirectModeCodedAnalogGain 0x119d /* (7:0) IndexLo 0x119e */ +#define fpDirectModeDigitalGain 0x11a1 /* (7:0) IndexLo 0x11a2 */ +#define uwFlashGunModeCoarseIntegration_lines 0x11a5 /* (7:0) IndexLo 0x11a6 */ +#define uwFlashGunModeFineIntegration_pixels 0x11a9 /* (7:0) IndexLo 0x11aa */ +#define uwFlashGunModeCodedAnalogGain 0x11ad /* (7:0) IndexLo 0x11ae */ +#define fpFlashGunModeDigitalGain 0x11b1 /* (7:0) IndexLo 0x11b2 */ +#define fFreezeAutoExposure 0x11b4 /* (7:0) */ +#define fpUserMaximumIntegrationTime_us 0x11b7 /* (7:0) IndexLo 0x11b8 */ +#define fpRecommendFlashGunAnalogGainThreshold 0x11bb /* (7:0) IndexLo 0x11bc */ +#define fEnableHighClipForDesiredExposureTime 0x11be /* (7:0) */ +#define bAntiFlickerMode 0x11c0 /* (7:0) */ + +#define fpMaximumStep 0x1201 /* (7:0) IndexLo 0x1202 */ +#define fpMinimumStep 0x1205 /* (7:0) IndexLo 0x1206 */ +#define fpMinimumDesiredExposureTime_us 0x1209 /* (7:0) IndexLo 0x120a */ +#define fpStepProportion 0x120d /* (7:0) IndexLo 0x120e */ +#define fpMaximumNegativeStepThreshold 0x1211 /* (7:0) IndexLo 0x1212 */ +#define fpRelativeOnTargetStabilityThreshold 0x1215 /* (7:0) IndexLo 0x1216 */ +#define fpDigitalGainFloor 0x1219 /* (7:0) IndexLo 0x121a */ +#define fpDigitalGainCeiling 0x121d /* (7:0) IndexLo 0x121e */ +#define fpRelativeIntTimeHysThreshold 0x1221 /* (7:0) IndexLo 0x1222 */ +#define fpRelativeDigitalGainHysThreshold 0x1225 /* (7:0) IndexLo 0x1226 */ +#define fpRelativeCompilationProblemThreshold 0x1229 /* (7:0) IndexLo 0x122a */ +#define fpRoundUpBunchFudge 0x122d /* (7:0) IndexLo 0x122e */ +#define fpFineClampThreshold 0x1231 /* (7:0) IndexLo 0x1232 */ +#define fpMaximumManualExposureTime_s 0x1235 /* (7:0) IndexLo 0x1236 */ +#define fpRelativeStabilityThresholdForAutoFocus 0x1239 /* (7:0) IndexLo 0x123a */ +#define bLeakShift 0x123c /* (7:0) */ + +#define fpLeakyEnergy 0x1281 /* (7:0) IndexLo 0x1282 */ +#define fpRelativeStep 0x1285 /* (7:0) IndexLo 0x1286 */ + +#define uwCoarseIntegrationPending_lines 0x1309 /* (7:0) IndexLo 0x130a */ +#define uwFineIntegrationPending_pixels 0x130d /* (7:0) IndexLo 0x130e */ +#define fpAnalogGainPending 0x1311 /* (7:0) IndexLo 0x1312 */ +#define fpDigitalGainPending 0x1315 /* (7:0) IndexLo 0x1316 */ +#define fpDesiredExposureTime_us 0x1319 /* (7:0) IndexLo 0x131a */ +#define fpCompiledExposureTime_us 0x131d /* (7:0) IndexLo 0x131e */ +#define uwCodedAnalogGainPending 0x132b /* (7:0) IndexLo 0x132c */ + +#define bWhiteBalanceMode 0x1480 /* (7:0) */ +#define bManualRedGain 0x1482 /* (7:0) */ +#define bManualGreenGain 0x1484 /* (7:0) */ +#define bManualBlueGain 0x1486 /* (7:0) */ +#define fpFlashRedGain 0x148b /* (7:0) IndexLo 0x148c */ +#define fpFlashGreenGain 0x148f /* (7:0) IndexLo 0x1490 */ +#define fpFlashBlueGain 0x1493 /* (7:0) IndexLo 0x1494 */ + +#define bStatus 0x1500 /* (7:0) */ +#define fpRedGain 0x1505 /* (7:0) IndexLo 0x1506 */ +#define fpGreenGain 0x1509 /* (7:0) IndexLo 0x150a */ +#define fpBlueGain 0x150d /* (7:0) IndexLo 0x150e */ + +#define fpStableTotalStepThreshold 0x1581 /* (7:0) IndexLo 0x1582 */ +#define fpMinimumRelativeStep 0x1585 /* (7:0) IndexLo 0x1586 */ +#define fpMaximumRelativeStep 0x1589 /* (7:0) IndexLo 0x158a */ +/*#define fpStepProportion 0x158d*/ /* (7:0) IndexLo 0x158e */ + +#define fpRedA 0x1601 /* (7:0) IndexLo 0x1602 */ +#define fpBlueA 0x1605 /* (7:0) IndexLo 0x1606 */ +#define fpRedB 0x1609 /* (7:0) IndexLo 0x160a */ +#define fpBlueB 0x160d /* (7:0) IndexLo 0x160e */ +#define fpMaximumDistanceAllowedFromLocus 0x1611 /* (7:0) IndexLo 0x1612 */ +#define fEnableConstrainedWhiteBalance 0x1614 /* (7:0) */ +#define bACCSRCCtrl 0x1616 /* (7:0) */ + +#define fpOutputRedGain 0x1681 /* (7:0) IndexLo 0x1682 */ +#define fpOutputGreenGain 0x1685 /* (7:0) IndexLo 0x1686 */ +#define fpOutputBlueGain 0x1689 /* (7:0) IndexLo 0x168a */ +#define fAreGainsConstrained 0x168c /* (7:0) */ + +#define fpGradientOfLocusAB 0x1701 /* (7:0) IndexLo 0x1702 */ +#define fpDistanceOfInputPointFromLocusAB 0x1705 /* (7:0) IndexLo 0x1706 */ +#define fpConstrainedRedPoint 0x1709 /* (7:0) IndexLo 0x170a */ +#define fpConstrainedBluePoint 0x170d /* (7:0) IndexLo 0x170e */ + +#define bMaxNumberOfFramesToWaitForStability 0x1880 /* (7:0) */ + +#define fWhiteBalanceStable 0x1900 /* (7:0) */ +#define fExposureStable 0x1902 /* (7:0) */ +#define fDarkCalStable 0x1904 /* (7:0) */ +#define fStable 0x1906 /* (7:0) */ +#define fForcedStablility 0x1908 /* (7:0) */ + +#define fpRedTilt 0x1985 /* (7:0) IndexLo 0x1986 */ +#define fpGreenTilt 0x1989 /* (7:0) IndexLo 0x198a */ +#define fpBlueTilt 0x198d /* (7:0) IndexLo 0x198e */ +#define bBlackCorrectionOffset 0x1990 /* (7:0) */ + +#define uwSensorAnalogGainFloor 0x1a01 /* (7:0) IndexLo 0x1a02 */ +#define uwSensorAnalogGainCeiling 0x1a05 /* (7:0) IndexLo 0x1a06 */ + +#define bFlashMode 0x1a80 /* (7:0) */ +#define uwFlashOffLine 0x1a83 /* (7:0) IndexLo 0x1a84 */ + +#define fFlashRecommended 0x1b00 /* (7:0) */ +#define fFlashGrabComplete 0x1b02 /* (7:0) */ + +#define uwHorizontalOffset 0x1d01 /* (7:0) IndexLo 0x1d02 */ +#define uwVerticalOffset 0x1d05 /* (7:0) IndexLo 0x1d06 */ +#define iR2RCoefficient 0x1d08 /* (7:0) Signed */ +#define iR2GRCoefficient 0x1d0a /* (7:0) Signed */ +#define iR2GBCoefficient 0x1d0c /* (7:0) Signed */ +#define iR2BCoefficient 0x1d0e /* (7:0) Signed */ +#define iR4RCoefficient 0x1d10 /* (7:0) Signed */ +#define iR4GRCoefficient 0x1d12 /* (7:0) Signed */ +#define iR4GBCoefficient 0x1d14 /* (7:0) Signed */ +#define iR4BCoefficient 0x1d16 /* (7:0) Signed */ + +#define ScythefDisableFilter 0x1d80 /* (7:0) */ +#define JackfDisableFilter 0x1e00 /* (7:0) */ + +#define bAntiAliasFilterSuppress 0x1e80 /* (7:0) */ + +#define ColourMatrixDamperfDisable 0x1f00 /* (7:0) */ +#define fpLowThreshold 0x1f03 /* (7:0) IndexLo 0x1f04 */ +#define fpHighThreshold 0x1f07 /* (7:0) IndexLo 0x1f08 */ +#define fpMinimumOutput 0x1f0b /* (7:0) IndexLo 0x1f0c */ + +#define fpGInR 0x1f81 /* (7:0) IndexLo 0x1f82 */ +#define fpBInR 0x1f85 /* (7:0) IndexLo 0x1f86 */ +#define fpRInG 0x1f89 /* (7:0) IndexLo 0x1f8a */ +#define fpBInG 0x1f8d /* (7:0) IndexLo 0x1f8e */ +#define fpRInB 0x1f91 /* (7:0) IndexLo 0x1f92 */ +#define fpGInB 0x1f95 /* (7:0) IndexLo 0x1f96 */ + +#define bUserPeakGain 0x2000 /* (7:0) */ +#define fDisableGainDamping 0x2002 /* (7:0) */ +#define fpDamperLowThreshold_Gain 0x2005 /* (7:0) IndexLo 0x2006 */ +#define fpDamperHighThreshold_Gain 0x2009 /* (7:0) IndexLo 0x200a */ +#define fpMinimumDamperOutput_Gain 0x200d /* (7:0) IndexLo 0x200e */ +#define bUserPeakLoThresh 0x2010 /* (7:0) */ +#define fDisableCoringDamping 0x2012 /* (7:0) */ +#define bUserPeakHiThresh 0x2014 /* (7:0) */ +#define fpDamperLowThreshold_Coring 0x2017 /* (7:0) IndexLo 0x2018 */ +#define fpDamperHighThreshold_Coring 0x201b /* (7:0) IndexLo 0x201c */ +#define fpMinimumDamperOutput_Coring 0x201f /* (7:0) IndexLo 0x2020 */ +#define bBlockControl 0x2022 /* (7:0) */ + +#define fGammaManuCtrl0 0x2280 /* (7:0) */ +#define bRPeakGamma0 0x2282 /* (7:0) */ +#define bGPeakGamma0 0x2284 /* (7:0) */ +#define bBPeakGamma0 0x2286 /* (7:0) */ +#define bRUnPeakGamma0 0x2288 /* (7:0) */ +#define bGUnPeakGamma0 0x228a /* (7:0) */ +#define bBUnPeakGamma0 0x228c /* (7:0) */ + +#define fGammaManuCtrl1 0x2300 /* (7:0) */ +#define bRPeakGamma1 0x2302 /* (7:0) */ +#define bGPeakGamma1 0x2304 /* (7:0) */ +#define bBPeakGamma1 0x2306 /* (7:0) */ +#define bRUnPeakGamma1 0x2308 /* (7:0) */ +#define bGUnPeakGamma1 0x230a /* (7:0) */ +#define bBUnPeakGamma1 0x230c /* (7:0) */ + +#define uwLumaExcursion0 0x2381 /* (7:0) IndexLo 0x2382 */ +#define uwLumaMidpointTimes20 0x2385 /* (7:0) IndexLo 0x2386 */ +#define uwChromaExcursion0 0x2389 /* (7:0) IndexLo 0x238a */ +#define uwChromaMidpointTimes20 0x238d /* (7:0) IndexLo 0x238e */ + +#define uwLumaExcursion1 0x2401 /* (7:0) IndexLo 0x2402 */ +#define uwLumaMidpointTimes21 0x2405 /* (7:0) IndexLo 0x2406 */ +#define uwChromaExcursion1 0x2409 /* (7:0) IndexLo 0x240a */ +#define uwChromaMidpointTimes21 0x240d /* (7:0) IndexLo 0x240e */ + +#define FadeToBlackfDisable 0x2480 /* (7:0) */ +#define fpBlackValue 0x2483 /* (7:0) IndexLo 0x2484 */ +#define fpDamperLowThreshold 0x2487 /* (7:0) IndexLo 0x2488 */ +#define fpDamperHighThreshold 0x248b /* (7:0) IndexLo 0x248c */ +#define fpDamperOutput 0x248f /* (7:0) IndexLo 0x2490 */ + +#define bCodeCheckEn 0x2580 /* (7:0) */ +#define bBlankFormat 0x2582 /* (7:0) */ +#define bSyncCodeSetup 0x2584 /* (7:0) */ +#define bHSyncSetup 0x2586 /* (7:0) */ +#define bVSyncSetup 0x2588 /* (7:0) */ +#define bPClkSetup 0x258a /* (7:0) */ +#define fPclkEn 0x258c /* (7:0) */ +#define bOpfSpSetup 0x258e /* (7:0) */ +#define bBlankData_MSB 0x2590 /* (7:0) */ +#define bBlankData_LSB 0x2592 /* (7:0) */ +#define bRgbSetup 0x2594 /* (7:0) */ +#define bYuvSetup 0x2596 /* (7:0) */ +#define bVsyncRisingCoarseH 0x2598 /* (7:0) */ +#define bVsyncRisingCoarseL 0x259a /* (7:0) */ +#define bVsyncRisingFineH 0x259c /* (7:0) */ +#define bVsyncRisingFineL 0x259e /* (7:0) */ +#define bVsyncFallingCoarseH 0x25a0 /* (7:0) */ +#define bVsyncFallingCoarseL 0x25a2 /* (7:0) */ +#define bVsyncFallingFineH 0x25a4 /* (7:0) */ +#define bVsyncFallingFineL 0x25a6 /* (7:0) */ +#define bHsyncRisingH 0x25a8 /* (7:0) */ +#define bHsyncRisingL 0x25aa /* (7:0) */ +#define bHsyncFallingH 0x25ac /* (7:0) */ +#define bHsyncFallingL 0x25ae /* (7:0) */ +#define bOutputInterface 0x25b0 /* (7:0) */ +#define bCCPExtraData 0x25b2 /* (7:0) */ + +#define NoRAfDisable 0x2600 /* (7:0) */ +#define bUsage 0x2602 /* (7:0) */ +#define bSplit_Kn 0x2604 /* (7:0) */ +#define bSplit_Nl 0x2606 /* (7:0) */ +#define bTight_Green 0x2608 /* (7:0) */ +#define fDisableNoraPromoting 0x260a /* (7:0) */ +#define DamperLowThreshold 0x260d /* (7:0) IndexLo 0x260e */ +#define DamperHighThreshold 0x2611 /* (7:0) IndexLo 0x2612 */ +#define MinimumDamperOutput 0x2615 /* (7:0) IndexLo 0x2616 */ + + + + +#endif /* VS6624_H */ + --- /dev/null +++ git/drivers/bmi/pims/factory_test/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS - Factory Test Module +# + +obj-$(CONFIG_BUG_FACTORY_TEST) += factory_test.o + --- /dev/null +++ git/drivers/bmi/pims/factory_test/factory_test.c @@ -0,0 +1,952 @@ +/* + * factory_test.c + * + * BIG factory test board device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <../arch/arm/mach-mx3/iomux.h> + +#define BUG_FACTORY_TEST_VERSION "1.0" +#define BUF_MAX_SIZE 0x20 +#define FT_ERR -1 +#define MX31_GPIO_PORT1 0 + +static int major; + // IOX I2C transfer structure +struct iox_i2c_xfer { + unsigned char addr; + unsigned char offset; + unsigned char data; +} iox_i2c_xfer; + + // SPI transfer structure +struct spi_xfer { + unsigned char addr; + unsigned char data[2]; +} spi_xfer; + +enum sig_bus { + MX31, + CPLD +}; + // MX31 signals +enum msig { + CTS0, + RTS0, + CTS3, + RTS3, + CAM, + VSYNC1, + I2S_RXD0, + I2S_RXD1, + I2S_RXD2, + I2S_RXD3 +}; + + // CPLD signals +enum csig { + GPIO0, + GPIO1, + GPIO2, + GPIO3, + PRES0, + PRES1, + PRES2, + PRES3, + CAM_IF, + CAM_LOCK_STATUS, + LCD +}; + + // MX31/CPLD signal structure +struct mc_signal { + enum sig_bus bus; + unsigned int signal; + unsigned char funct; + unsigned int value; +} mc_signal; + + // IOCTL commands for Factory Test Module +#define FT_READ_IOX _IOR('f', 0x1, struct iox_i2c_xfer *) // read IOX +#define FT_WRITE_IOX _IOR('f', 0x2, struct iox_i2c_xfer *) // write IOX +#define FT_READ_SPI _IOR('f', 0x3, struct spi_xfer *) // read SPI +#define FT_WRITE_SPI _IOR('f', 0x4, struct spi_xfer *) // write SPI +#define FT_SIGNAL _IOR('f', 0x5, struct mc_signal *) // MX31/CPLD signals + + // private device structure +struct bug_ft +{ + struct cdev cdev; // character device (4 minor numbers) + unsigned int active; // at lease 1 bdev active + struct bmi_device *bdev[4]; // BMI device per slot + int open_flag[4]; // force single open on each device + struct spi_device *spi[4]; // SPI device + char rbuf[BUF_MAX_SIZE]; // SPI read buffer + char wbuf[BUF_MAX_SIZE]; // SPI write buffer +}; + +static struct bug_ft bug_ft; // global private data + +/* + * SPI function + */ + +static int spi_rw(struct spi_device *spi, u8 * buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + + spi_message_add_tail(&t, &m); + if (spi_sync(spi, &m) != 0 || m.status != 0) + return FT_ERR; + + return m.actual_length; +} + +/*! + * This function allows writing 1 register on a SPI device. + * + * @param buf pointer on the buffer + * @param count size of the buffer + * @return This function returns the number of written bytes. + */ +static ssize_t spi_write_reg(struct bug_ft *priv, char *buf, int slot) +{ + int res = 0; + + memset(priv->wbuf, 0, BUF_MAX_SIZE); + priv->wbuf[0] = buf[0]; + priv->wbuf[1] = buf[1]; + priv->wbuf[2] = buf[2]; + priv->wbuf[3] = buf[3]; + if (res > 0) { + return -EFAULT; + } + res = spi_rw(priv->spi[slot], priv->wbuf, 1); + + return res; +} + +/*! + * This function allows reading 1 register from a SPI device. + * + * @param buf pointer on the buffer + * @param off offset in the buffer + + * @return This function returns the number of read bytes. + */ +static ssize_t spi_read_reg(struct bug_ft *priv, char *buf, int slot) +{ + spi_write_reg(priv, buf, slot); + + memset(priv->rbuf, 0, BUF_MAX_SIZE); + buf[0] = priv->wbuf[3]; + buf[1] = priv->wbuf[2]; + buf[2] = priv->wbuf[1]; + buf[3] = priv->wbuf[0]; + + return 4; +} + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bug_ft_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_FACTORY_TEST, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bug_ft_tbl); + +int bug_ft_probe(struct bmi_device *bdev); +void bug_ft_remove(struct bmi_device *bdev); + + // BMI driver structure +static struct bmi_driver bug_ft_driver = +{ + .name = "bug_ft_control", + .id_table = bug_ft_tbl, + .probe = bug_ft_probe, + .remove = bug_ft_remove, +}; + +/* + * I2C set up + * + * IOX A on slots 1 & 3 + * IOX B on slots 0 & 4 + */ + + // I2C IOX register addresses +#define IOX0 (0xE8) // I2C port A +#define IOX1 (0xEA) // I2C port A +#define IOX2 (0xEC) // I2C port A +#define IOX3 (0xEE) // I2C port A +#define IOX4 (0xE8) // I2C port B +#define IOX5 (0xEA) // I2C port B +#define IOX6 (0xEC) // I2C port B +#define IOX7 (0xEE) // I2C port B + + // I2C IOX register offset addresses +#define IOX_INPUT0_REG 0x0 +#define IOX_INPUT1_REG 0x1 +#define IOX_OUTPUT0_REG 0x2 +#define IOX_OUTPUT1_REG 0x3 +#define IOX_POLARITY0_REG 0x4 +#define IOX_POLARITY1_REG 0x5 +#define IOX_CONTROL0 0x6 +#define IOX_CONTROL1 0x7 + + // read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char addr, + unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + rmsg[0].addr = addr; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = addr; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = FT_ERR; + } + return ret; +} + + // write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char addr, + unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + wmsg[0].addr = addr; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = addr; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = FT_ERR; + } + return ret; +} + +/* + * control device operations + */ + +// open +int bug_ft_open(struct inode *inode, struct file *filp) +{ + int slot = MINOR(inode->i_rdev); + + if (bug_ft.open_flag[slot]) { + return - EBUSY; + } + bug_ft.open_flag[slot] = 1; + filp->private_data = &bug_ft; + return 0; +} + +// release +int bug_ft_release(struct inode *inode, struct file *filp) +{ + int slot = MINOR(inode->i_rdev); + + bug_ft.open_flag[slot] = 0; + return 0; +} + +/* + * ioctl and support functions + */ + + // do_mx31_sig +int do_mx31_sig(struct mc_signal * mc_signal) +{ + int read_value; + + switch(mc_signal->signal) { + case CTS0: // GPIO2_7 + if(mc_signal->funct != 'W') { + printk("do_mx31_sig(): CTS0 is a Write-Only signal\n"); + return -1; + } + iomux_config_mux(MX31_PIN_CTS1, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO); + mxc_set_gpio_direction(MX31_PIN_CTS1, 0); + mxc_set_gpio_dataout(MX31_PIN_CTS1, mc_signal->value); + break; + case RTS0: // GPIO2_6 + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): RTS0 is a Read-Only signal\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_RTS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + read_value = mxc_get_gpio_datain(MX31_PIN_RTS1); + mc_signal->value = read_value; + break; + case CTS3: // GPIO3_29 + if(mc_signal->funct != 'W') { + printk("do_mx31_sig(): CTS3 is a Write-Only signal\n"); + return -1; + } + iomux_config_mux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO); + mxc_set_gpio_direction(MX31_PIN_ATA_DIOW, 0); + mxc_set_gpio_dataout(MX31_PIN_ATA_DIOW, mc_signal->value); + break; + case RTS3: // GPIO3_27 + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): RTS3 is a Read-Only signal\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + read_value = mxc_get_gpio_datain(MX31_PIN_ATA_CS1); + mc_signal->value = read_value; + break; + case CAM: // camera interface -> GPIO + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): CAM is a Read-Only signal\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + mxc_request_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + + mc_signal->value = mxc_get_gpio_datain(MX31_PIN_CSI_HSYNC) << 9; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_VSYNC) << 8; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D15) << 7; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D14) << 6; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D13) << 5; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D12) << 4; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D11) << 3; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D10) << 2; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D9) << 1; + mc_signal->value |= mxc_get_gpio_datain(MX31_PIN_CSI_D8); + + mxc_request_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + mxc_request_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); + break; + case VSYNC1: // read VSYNC1 state + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): VSYNC1 is a Read-Only signal\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_SRST0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO); + mc_signal->value = mxc_get_gpio_datain(MX31_PIN_SRST0); + mxc_request_iomux(MX31_PIN_SRST0, OUTPUTCONFIG_FUNC, INPUTCONFIG_ALT2); + break; + case I2S_RXD0: // GPIO1_20 + case I2S_RXD2: + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): I2S_RXD[02] are Read-Only signals\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_SRXD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + read_value = mxc_get_gpio_datain(MX31_PIN_SRXD4); + mc_signal->value = read_value; + break; + case I2S_RXD1: // GPIO1_22 + case I2S_RXD3: + if(mc_signal->funct != 'R') { + printk("do_mx31_sig(): I2S_RXD[13] are Read-Only signals\n"); + return -1; + } + mxc_request_iomux(MX31_PIN_SRXD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_GPIO); + read_value = mxc_get_gpio_datain(MX31_PIN_SRXD5); + mc_signal->value = read_value; + break; + default: + printk("do_mx31_sig(): unknown signal\n"); + return -1; + } + return 0; +} + + // do_cpld_sig +int do_cpld_sig(struct mc_signal * mc_signal) +{ + int read_value; + + switch(mc_signal->signal) { + case GPIO0: + if(mc_signal->funct == 'R') { + // set GPIO to input + cpld_set_module_gpio_dir(CPLD_M1, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 3, CPLD_GPIO_IN); + // read GPIO + read_value = cpld_read_gpio_data_reg(CPLD_M1); + // read interrupt + read_value |= cpld_interrupt_status(INT_M1_INT) << 4; + mc_signal->value = read_value; + } else if(mc_signal->funct == 'H') { + // write GPIO + cpld_set_module_gpio_data(CPLD_M1, 0, mc_signal->value & 0x1); + cpld_set_module_gpio_data(CPLD_M1, 1, (mc_signal->value & 0x2) >> 1); + cpld_set_module_gpio_data(CPLD_M1, 2, (mc_signal->value & 0x4) >> 2); + cpld_set_module_gpio_data(CPLD_M1, 3, (mc_signal->value & 0x8) >> 3); + // set GPIO to output + cpld_set_module_gpio_dir(CPLD_M1, 0, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M1, 1, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M1, 2, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M1, 3, CPLD_GPIO_OUT); + } else { + printk("do_cpld_sig(): Illegal function for CPLD GPIO\n"); + return -1; + } + break; + case GPIO1: + if(mc_signal->funct == 'R') { + // set GPIO to input + cpld_set_module_gpio_dir(CPLD_M2, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 3, CPLD_GPIO_IN); + // read GPIO + read_value = cpld_read_gpio_data_reg(CPLD_M2); + // read interrupt + read_value |= cpld_interrupt_status(INT_M2_INT) << 4; + mc_signal->value = read_value; + } else if(mc_signal->funct == 'H') { + // write GPIO + cpld_set_module_gpio_data(CPLD_M2, 0, mc_signal->value & 0x1); + cpld_set_module_gpio_data(CPLD_M2, 1, (mc_signal->value & 0x2) >> 1); + cpld_set_module_gpio_data(CPLD_M2, 2, (mc_signal->value & 0x4) >> 2); + cpld_set_module_gpio_data(CPLD_M2, 3, (mc_signal->value & 0x8) >> 3); + // set GPIO to output + cpld_set_module_gpio_dir(CPLD_M2, 0, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M2, 1, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M2, 2, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M2, 3, CPLD_GPIO_OUT); + } else { + printk("do_cpld_sig(): Illegal function for CPLD GPIO\n"); + return -1; + } + break; + case GPIO2: + if(mc_signal->funct == 'R') { + // set GPIO to input + cpld_set_module_gpio_dir(CPLD_M3, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 3, CPLD_GPIO_IN); + // read GPIO + read_value = cpld_read_gpio_data_reg(CPLD_M3); + // read interrupt + read_value |= cpld_interrupt_status(INT_M3_INT) << 4; + mc_signal->value = read_value; + } else if(mc_signal->funct == 'H') { + // write GPIO + cpld_set_module_gpio_data(CPLD_M3, 0, mc_signal->value & 0x1); + cpld_set_module_gpio_data(CPLD_M3, 1, (mc_signal->value & 0x2) >> 1); + cpld_set_module_gpio_data(CPLD_M3, 2, (mc_signal->value & 0x4) >> 2); + cpld_set_module_gpio_data(CPLD_M3, 3, (mc_signal->value & 0x8) >> 3); + // set GPIO to output + cpld_set_module_gpio_dir(CPLD_M3, 0, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M3, 1, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M3, 2, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M3, 3, CPLD_GPIO_OUT); + } else { + printk("do_cpld_sig(): Illegal function for CPLD GPIO\n"); + return -1; + } + break; + case GPIO3: + if(mc_signal->funct == 'R') { + // set GPIO to input + cpld_set_module_gpio_dir(CPLD_M4, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 3, CPLD_GPIO_IN); + // read GPIO + read_value = cpld_read_gpio_data_reg(CPLD_M4); + // read interrupt + read_value |= cpld_interrupt_status(INT_M4_INT) << 4; + mc_signal->value = read_value; + } else if(mc_signal->funct == 'H') { + // write GPIO + cpld_set_module_gpio_data(CPLD_M4, 0, mc_signal->value & 0x1); + cpld_set_module_gpio_data(CPLD_M4, 1, (mc_signal->value & 0x2) >> 1); + cpld_set_module_gpio_data(CPLD_M4, 2, (mc_signal->value & 0x4) >> 2); + cpld_set_module_gpio_data(CPLD_M4, 3, (mc_signal->value & 0x8) >> 3); + // set GPIO to output + cpld_set_module_gpio_dir(CPLD_M4, 0, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M4, 1, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M4, 2, CPLD_GPIO_OUT); + cpld_set_module_gpio_dir(CPLD_M4, 3, CPLD_GPIO_OUT); + } else { + return -1; + } + break; + case PRES0: + if(mc_signal->funct != 'R') { + printk("do_cpld_sig(): Illegal function for CPLD PRES signal\n"); + return -1; + } + read_value = (cpld_read_module_present_status(CPLD_M1) >> 2) & 0x1; + mc_signal->value = read_value; + break; + case PRES1: + if(mc_signal->funct != 'R') { + printk("do_cpld_sig(): Illegal function for CPLD PRES signal\n"); + return -1; + } + read_value = (cpld_read_module_present_status(CPLD_M2) >> 2) & 0x1; + mc_signal->value = read_value; + break; + case PRES2: + if(mc_signal->funct != 'R') { + printk("do_cpld_sig(): Illegal function for CPLD PRES signal\n"); + return -1; + } + read_value = (cpld_read_module_present_status(CPLD_M3) >> 2) & 0x1; + mc_signal->value = read_value; + break; + case PRES3: + if(mc_signal->funct != 'R') { + printk("do_cpld_sig(): Illegal function for CPLD PRES signal\n"); + return -1; + } + read_value = (cpld_read_module_present_status(CPLD_M4) >> 2) & 0x1; + mc_signal->value = read_value; + break; + case CAM_IF: + if(mc_signal->funct != 'W') { + printk("do_cpld_sig(): Illegal function for CPLD CAM_IF signal\n"); + return -1; + } + cpld_sensor_active(CAM_CLK_RISE); + break; + case CAM_LOCK_STATUS: + if(mc_signal->funct != 'R') { + printk("do_cpld_sig(): Illegal function for CPLD CAM_LOCK_STATUS signal\n"); + return -1; + } + read_value = (int) __raw_readw(CPLD_BASE_ADDRESS+CPLD_CAM); + read_value = cpld_sensor_lock_status(); + mc_signal->value = read_value; + break; + case LCD: + if(mc_signal->funct != 'W') { + printk("do_cpld_sig(): Illegal function for CPLD LCD signal\n"); + return -1; + } + cpld_lcd_inactive(0); + cpld_lcd_inactive(1); + cpld_lcd_active(0, 0, LCD_MODE_I80); + cpld_lcd_active(1, 0, LCD_MODE_I80); + break; + default: + printk("do_cpld_sig(): unknown signal\n"); + return -1; + } + return 0; +} + + // ioctl +int bug_ft_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int slot = MINOR(inode->i_rdev); + struct bug_ft *bug_ft = (struct bug_ft *) filp->private_data; + struct i2c_adapter *adap = &bug_ft->bdev[slot]->adap; + unsigned char iox_data[1]; + struct iox_i2c_xfer iox_i2c_xfer; + struct spi_xfer spi_xfer; + struct mc_signal mc_signal; + u8 buf[4]; + int ret = 0; + + // error if no ft active. + if(bug_ft->active == -1) + return -ENODEV; + + // error if no BMI device + if(bug_ft->bdev[slot] == 0) + return -ENODEV; + + // get I2C transfer structure + if((cmd == FT_READ_IOX) || (cmd == FT_WRITE_IOX)) + if(copy_from_user(&iox_i2c_xfer, (struct iox_i2c_xfer *) arg, sizeof(struct iox_i2c_xfer))) { + printk(KERN_INFO "factory_test ioctl(%d): copy_from_user #1 = %d\n", slot, ret); + return -EFAULT; + } + + // get SPI transfer structure + if((cmd == FT_READ_SPI) || (cmd == FT_WRITE_SPI)) + if(copy_from_user(&spi_xfer, (struct spi_xfer *) arg, sizeof(struct spi_xfer))) { + printk(KERN_INFO "factory_test ioctl(%d): copy_from_user #1 = %d\n", slot, ret); + return -EFAULT; + } + + // get signal structure + if(cmd == FT_SIGNAL) + if(copy_from_user(&mc_signal, (struct mc_signal *) arg, sizeof(struct mc_signal))) { + printk(KERN_INFO "factory_test ioctl(%d): copy_from_user #1 = %d\n", slot, ret); + return -EFAULT; + } + + // ioctl's + switch (cmd) { + // read IOX + case FT_READ_IOX: + ret = ReadByte_IOX(adap, iox_i2c_xfer.addr, iox_i2c_xfer.offset, iox_data); + if(ret == 0) { + iox_i2c_xfer.data = *iox_data; + if(copy_to_user((struct iox_i2c_xfer *) arg, &iox_i2c_xfer, sizeof(struct iox_i2c_xfer))) + ret = -EFAULT; + } + break; + // write IOX + case FT_WRITE_IOX: + *iox_data = iox_i2c_xfer.data; + ret = WriteByte_IOX(adap, iox_i2c_xfer.addr, iox_i2c_xfer.offset, *iox_data); + if(ret == 0) { + if(copy_to_user((struct iox_i2c_xfer *) arg, &iox_i2c_xfer, sizeof(struct iox_i2c_xfer))) { + ret = -EFAULT; + } + } + break; + // read SPI + case FT_READ_SPI: + // READ + buf[3] = 0xC0 | ((spi_xfer.addr & 0x3F) >> 1); + buf[2] = 0x00 | ((spi_xfer.addr & 0x1) << 7); + buf[1] = 0x00; + buf[0] = 0x00; + ret = spi_read_reg(bug_ft, buf, slot); + if(ret == 4) { + spi_xfer.data[1] = ((buf[2] & 0x7F) << 1) | ((buf[1] & 0x80) >> 7); + spi_xfer.data[0] = ((buf[1] & 0x7F) << 1) | ((buf[0] & 0x80) >> 7); + if(copy_to_user((struct spi_xfer *) arg, &spi_xfer, sizeof(struct spi_xfer))) + ret = -EFAULT; + else + ret = 0; + } else + ret = FT_ERR; + break; + // write SPI + case FT_WRITE_SPI: + // EWEN + buf[3] = 0x98; + buf[2] = 0x00; + buf[1] = 0x00; + buf[0] = 0x00; + ret = spi_write_reg(bug_ft, buf, slot); + if(ret != 1) { + ret = FT_ERR; + break; + } + // WRITE + buf[3] = 0xA0 | ((spi_xfer.addr & 0x3F) >> 1); + buf[2] = 0x00 | ((spi_xfer.addr & 0x1) << 7) | (spi_xfer.data[1] >> 1); + buf[1] = ((spi_xfer.data[1] & 0x1) << 7) | (spi_xfer.data[0] >> 1); + buf[0] = spi_xfer.data[0] << 0x7; + ret = spi_write_reg(bug_ft, buf, slot); + if(ret == 1) { + if(copy_to_user((struct spi_xfer *) arg, &spi_xfer, sizeof(struct spi_xfer))) + ret = -EFAULT; + else + ret = 0; + } else + ret = FT_ERR; + + break; + case FT_SIGNAL: + if(mc_signal.bus == MX31) { + ret = do_mx31_sig(&mc_signal); + if((mc_signal.funct == 'R') && (ret == 0)) { + if(copy_to_user((struct mc_signal *) arg, &mc_signal, sizeof(struct mc_signal))) { + ret = -EFAULT; + } else { + ret = 0; + } + } + } else if(mc_signal.bus == CPLD) { + ret = do_cpld_sig(&mc_signal); + if((mc_signal.funct == 'R') && (ret == 0)) { + if(copy_to_user((struct mc_signal *) arg, &mc_signal, sizeof(struct mc_signal))) { + ret = -EFAULT; + } else { + ret = 0; + } + } + } else { + ret = FT_ERR; + } + break; + default: + return -ENOTTY; + } + + return ret; +} + +/* + * BMI functions + */ + +static const struct file_operations bug_ft_fops = { + .owner = THIS_MODULE, + .ioctl = bug_ft_ioctl, + .open = bug_ft_open, + .release = bug_ft_release, +}; + +int bug_ft_probe(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + struct cdev *cdev_ptr; + dev_t dev_id; + int ret; + unsigned long speed = 1000000; + unsigned char mode = SPI_MODE_2 | SPI_CS_HIGH; + unsigned char bits_per_word = 32; + + + printk(KERN_INFO "factory_test.c: probe slot %d\n", slot); + + cdev_ptr = &bug_ft.cdev; + cdev_init(cdev_ptr, &bug_ft_fops); + + dev_id = MKDEV(major, bdev->info->slot); + ret = cdev_add(cdev_ptr, dev_id, 1); + if(ret) + return ret; + + switch(slot) { + case 0: + cpld_set_module_gpio_dir(CPLD_M1, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M1, 3, CPLD_GPIO_IN); + ret = bmi_device_spi_setup(bdev, speed, mode, bits_per_word); + if (ret) { + printk (KERN_ERR "bug_ft_probe() - bmi_device_spi_setup(0) failed.\n"); + return ret; + } + bug_ft.spi[0] = &bdev->spi; + break; + case 1: + cpld_set_module_gpio_dir(CPLD_M2, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M2, 3, CPLD_GPIO_IN); + ret = bmi_device_spi_setup(bdev, speed, mode, bits_per_word); + if (ret) { + printk (KERN_ERR "bug_ft_probe() - bmi_device_spi_setup(2) failed.\n"); + return ret; + } + bug_ft.spi[1] = &bdev->spi; + break; + case 2: + cpld_set_module_gpio_dir(CPLD_M3, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M3, 3, CPLD_GPIO_IN); + ret = bmi_device_spi_setup(bdev, speed, mode, bits_per_word); + if (ret) { + printk (KERN_ERR "bug_ft_probe() - bmi_device_spi_setup(3) failed.\n"); + return ret; + } + bug_ft.spi[2] = &bdev->spi; + break; + case 3: + cpld_set_module_gpio_dir(CPLD_M4, 0, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 1, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 2, CPLD_GPIO_IN); + cpld_set_module_gpio_dir(CPLD_M4, 3, CPLD_GPIO_IN); + ret = bmi_device_spi_setup(bdev, speed, mode, bits_per_word); + if (ret) { + printk (KERN_ERR "bug_ft_probe() - bmi_device_spi_setup(3) failed.\n"); + return ret; + } + bug_ft.spi[3] = &bdev->spi; + break; + } + if(ret) { + cdev_del(cdev_ptr); + return ret; + } + + bmi_device_set_drvdata(bdev, &bug_ft); + + bug_ft.bdev[slot] = bdev; + + bug_ft.active = 1; + + return 0; +} + +void bug_ft_remove(struct bmi_device *bdev) +{ + struct bug_ft *bug_ft = (struct bug_ft*)(bmi_device_get_drvdata (bdev)); + int slot = bdev->info->slot; + struct cdev *cdev_ptr; + dev_t dev_id; + + switch(slot) { + case 0: + bmi_device_spi_cleanup(bdev); + break; + case 1: + bmi_device_spi_cleanup(bdev); + break; + case 2: + bmi_device_spi_cleanup(bdev); + break; + case 3: + bmi_device_spi_cleanup(bdev); + break; + } + + bug_ft->bdev[slot] = 0; + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata(&bdev[slot], 0); + + if((bug_ft->bdev[0]==0) && (bug_ft->bdev[1]==0) && + (bug_ft->bdev[2]==0) && (bug_ft->bdev[3]==0)) { + bug_ft->active = -1; + cdev_ptr = &bug_ft->cdev; + dev_id = MKDEV(major, bdev->info->slot); + cdev_del(cdev_ptr); + } + + return; +} + +/* + * Module functions + */ + +static __init int bug_ft_init(void) +{ + dev_t dev_id; + int retval; + + // No ft is active. + bug_ft.active = -1; + + // alloc char driver with 4 minor numbers + retval = alloc_chrdev_region(&dev_id, 0, 4, "BUG Factory Test Driver"); + if (retval) { + return -1; + } + + major = MAJOR(dev_id); + + printk("factory_test.c: BUG FACTORY TEST Driver v%s (major = %d)\n", BUG_FACTORY_TEST_VERSION, major); + + return bmi_register_driver(&bug_ft_driver); +} + +static void __exit bug_ft_clean(void) +{ + dev_t dev_id = MKDEV(major, 0); + + unregister_chrdev_region(dev_id, 4); + bmi_unregister_driver(&bug_ft_driver); + + return; +} + +module_init(bug_ft_init); +module_exit(bug_ft_clean); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BUG Factory Test board device driver"); +MODULE_SUPPORTED_DEVICE("bug_ft_control"); + --- /dev/null +++ git/drivers/bmi/pims/gps/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BMI_GPS) += bmi_gps.o + --- /dev/null +++ git/drivers/bmi/pims/gps/bmi_gps.c @@ -0,0 +1,468 @@ +/* + * bmi_gps.c + * + * BMI gps device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + + +//REWORK: Which are not needed ? +#include +#include +#include +#include +#include +#include +//#include +#include +//#include +#include +//#include + +//MTW +#include + + +#define BMIGPS_VERSION "1.1" + + +// private device structure +struct bmi_gps +{ + struct bmi_device *bdev; // BMI device + struct cdev cdev; + struct device *class_dev; + int open_flag; // single open flag + struct i2c_client *iox; +}; + +static struct bmi_gps bmi_gps[4]; +static int major; + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_gps_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_GPS_J32, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_gps_tbl); + +int bmi_gps_probe(struct bmi_device *bdev); +void bmi_gps_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_gps_driver = +{ + .name = "bmi_gps", + .id_table = bmi_gps_tbl, + .probe = bmi_gps_probe, + .remove = bmi_gps_remove, +}; + +/* + * I2C set up + */ + +// I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address + +// I2C IOX register addresses +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + +static struct i2c_board_info iox_info = { + I2C_BOARD_INFO("VH_IOX", BMI_IOX_I2C_ADDRESS), +}; + +// read byte from I2C IO expander + +static int ReadByte_IOX (struct i2c_client *client, unsigned char offset, unsigned char *data) +{ + int ret = 0; + + ret = i2c_master_send(client, &offset, 1); + if (ret == 1) + ret = i2c_master_recv(client, data, 1); + if (ret < 0) + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed...%d\n",ret); + return ret; +} + +// write byte to I2C IO expander +static int WriteByte_IOX (struct i2c_client *client, unsigned char offset, unsigned char data) +{ + int ret = 0; + unsigned char msg[2]; + + msg[0] = offset; + msg[1] = data; + ret = i2c_master_send(client, msg, sizeof(msg)); + + if (ret < 0) + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed...%d\n",ret); + + return ret; +} + + + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *file) +{ + struct bmi_gps *gps; + + gps = container_of(inode->i_cdev, struct bmi_gps, cdev); + + // Enforce single-open behavior + + if (gps->open_flag) { + return -EBUSY; + } + gps->open_flag = 1; + + // Save gps_dev pointer for later. + + file->private_data = gps; + return 0; + +} + +// release +int cntl_release(struct inode *inode, struct file *file) +{ + struct bmi_gps *gps; + + gps = (struct bmi_gps *)(file->private_data); + gps->open_flag = 0; + return 0; +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + + unsigned char gpio_mask; + struct bmi_gps *gps; + int slot; + + gps = (struct bmi_gps *)(file->private_data); + + // error if gps not present + if(gps->bdev == 0) + return -ENODEV; + + slot = gps->bdev->slot->slotnum; + adap = gps->bdev->slot->adap; + + // ioctl's + gpio_mask = bmi_slot_gpio_get(slot); + switch (cmd) { + case BMI_GPS_RLEDOFF: + bmi_slot_gpio_set (slot, (gpio_mask | RED_LED)); // Red LED=OFF + break; + + case BMI_GPS_RLEDON: + bmi_slot_gpio_set (slot, (gpio_mask & ~RED_LED)); // Red LED=ON + break; + + case BMI_GPS_GLEDOFF: + bmi_slot_gpio_set (slot, (gpio_mask | GREEN_LED)); // Greem LED=OFF + break; + + case BMI_GPS_GLEDON: + bmi_slot_gpio_set (slot, (gpio_mask & ~GREEN_LED)); // Greem LED=ON + break; + + case BMI_GPS_SETBOOT: + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data |= 0x08; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + if(ReadByte_IOX (gps->iox, IOX_CONTROL, &iox_data)) // IOX[3]=BOOT=0, IOX[4]=WAKEUP=0 + return -ENODEV; + iox_data |= 0x08; + if(WriteByte_IOX (gps->iox, IOX_CONTROL, iox_data)) // IOX[3]=BOOT=0, IOX[4]=WAKEUP=0 + return -ENODEV; + break; + + case BMI_GPS_CLRBOOT: + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data &= ~0x08; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + break; + + case BMI_GPS_SETWAKE: + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data |= 0x10; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + if(ReadByte_IOX (gps->iox, IOX_CONTROL, &iox_data)) // IOX[3]=BOOT=0, IOX[4]=WAKEUP=0 + return -ENODEV; + iox_data |= 0x10; + if(WriteByte_IOX (gps->iox, IOX_CONTROL, iox_data)) // IOX[3]=BOOT=0, IOX[4]=WAKEUP=0 + return -ENODEV; + break; + + case BMI_GPS_CLRWAKE: + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data &= ~0x10; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + break; + + case BMI_GPS_SETRST: + bmi_slot_gpio_set (slot, (gpio_mask & ~GPIO_1)); // RST = 0; + + break; + + case BMI_GPS_CLRRST: + bmi_slot_gpio_set (slot, (gpio_mask | GPIO_1)); // RST=tristate + break; + + case BMI_GPS_GETSTAT: + { + int read_data; + + if(ReadByte_IOX (gps->iox, IOX_INPUT_REG, &iox_data)) + return -ENODEV; + + read_data = iox_data | (bmi_slot_gpio_get(slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_GPS_ACTIVE_ANT : + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data &= ~0x40; + iox_data |= 0x80; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + break; + + case BMI_GPS_PASSIVE_ANT: + if(ReadByte_IOX (gps->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + iox_data &= ~0x80; + iox_data |= 0x40; + if(WriteByte_IOX (gps->iox, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + break; + + default: + return -ENOTTY; + } + + return 0; +} + +// control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + +/* + * Module functions + */ + +/* + * BMI functions + */ + +int bmi_gps_probe(struct bmi_device *bdev) +{ + int err; + int slot; + struct bmi_gps *gps; + struct i2c_adapter *adap; + struct cdev *cdev; + struct class *bmi_class; + dev_t dev_id; + + err = 0; + slot = bdev->slot->slotnum; + adap = bdev->slot->adap; + gps = &bmi_gps[slot]; + + + gps->bdev = 0; + gps->open_flag = 0; + + //Create 1 minor device + cdev = &gps->cdev; + cdev_init(cdev, &cntl_fops); + + dev_id = MKDEV(major, slot); + err = cdev_add(cdev, dev_id, 1); + if (err) { + return err; + } + + //Create class device + bmi_class = bmi_get_class (); + gps->class_dev = device_create(bmi_class, NULL, MKDEV(major, slot), gps, "bmi_gps_control_m%i", slot+1); + + if (IS_ERR(gps->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_gps_m%i; errno = %ld\n", + slot+1, PTR_ERR(gps->class_dev)); + gps->class_dev = NULL; + } + + //bind driver and bmi_device + gps->bdev = bdev; + bmi_device_set_drvdata(bdev, gps); + + printk(KERN_INFO "bmi_gps.c: probe slot %d\n", slot); + + // configure IOX - leave ouputs as inputs unless needed + + gps->iox = i2c_new_device(bdev->slot->adap, &iox_info); + if (gps->iox == NULL) + printk(KERN_ERR "IOX NULL...\n"); + if(WriteByte_IOX(gps->iox, IOX_OUTPUT_REG, 0x40)) { // + return -ENODEV; + } + if(WriteByte_IOX(gps->iox, IOX_CONTROL, 0x3F)) { // IOX[4:3]=OUT, IOX[7:5,2:0]=IN + return -ENODEV; + } + + + // Initialize GPIOs (turn LED's on ) + +// bmi_slot_gpio_configure_as_output (int slot, int gpio, int data) + + bmi_slot_gpio_configure(slot, RED_LED | GREEN_LED | GPIO_1); // Red, Green LEDS and GPIO_1 outputs + bmi_slot_gpio_set(slot, 0); + + //Enable uart transceiver + bmi_slot_uart_enable (slot); + + mdelay(275); + + // release reset to J32 device + bmi_slot_gpio_set ( slot, RED_LED | GREEN_LED | GPIO_1); // reset high, Red, Green LEDS off + + return 0; +} + + +void bmi_gps_remove(struct bmi_device *bdev) +{ + int slot; + struct bmi_gps *gps; + struct class *bmi_class; + + slot = bdev->slot->slotnum; + gps = &bmi_gps[slot]; + + //Disable uart transceiver + bmi_slot_uart_disable (slot); + bmi_slot_gpio_configure(slot, 0); + + bmi_class = bmi_get_class (); + device_destroy(bmi_class, MKDEV(major, slot)); + + gps->class_dev = 0; + + cdev_del (&gps->cdev); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + gps->bdev = 0; + + return; +} + + + + + +static void __exit bmi_gps_cleanup(void) +{ + dev_t dev_id; + + bmi_unregister_driver (&bmi_gps_driver); + + dev_id = MKDEV(major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + + + + +static int __init bmi_gps_init(void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI GPS Driver"); + + if (retval) { + return -1; + } + + major = MAJOR(dev_id); + retval = bmi_register_driver (&bmi_gps_driver); + + if (retval) { + unregister_chrdev_region(dev_id, 4); + return -1; + } + printk("bmi_gps.c: BMI_GPS Driver v%s \n", BMIGPS_VERSION); + + return 0; +} + + +module_init(bmi_gps_init); +module_exit(bmi_gps_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BMI gps device driver"); +MODULE_SUPPORTED_DEVICE("bmi_gps_control"); + --- /dev/null +++ git/drivers/bmi/pims/gsm/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BMI_GSM) += bmi_gsm.o + --- /dev/null +++ git/drivers/bmi/pims/gsm/bmi_gsm.c @@ -0,0 +1,301 @@ +/* + * bmi_gsm.c + * + * BMI GSM device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG +#undef DEBUG + +#define BMIGSM_VERSION "1.0" // driver version + + +// private device structure +struct bmi_gsm +{ + struct bmi_device *bdev; // BMI device + struct cdev cdev; // control device + struct device *class_dev; // control class device + int open_flag; +}; + +static struct bmi_gsm bmi_gsm_priv[4]; // per slot device structure +static int major; // control device major + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_gsm_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_GSM, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_gsm_tbl); + + +int bmi_gsm_probe(struct bmi_device *bdev); +void bmi_gsm_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_gsm_driver = +{ + .name = "bmi_gsm", + .id_table = bmi_gsm_tbl, + .probe = bmi_gsm_probe, + .remove = bmi_gsm_remove, +}; + + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *file) +{ + struct bmi_gsm *gsmod; + + gsmod = container_of (inode->i_cdev, struct bmi_gsm, cdev); + + // Enforce single-open behavior + + if (gsmod->open_flag) { + return -EBUSY; + } + gsmod->open_flag = 1; + + // Save gsm_dev pointer for later. + + file->private_data = gsmod; + return 0; + +} + +// release +int cntl_release(struct inode *inode, struct file *file) +{ + struct bmi_gsm *gsmod; + + gsmod = (struct bmi_gsm *)(file->private_data); + gsmod->open_flag = 0; + return 0; +} + + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bmi_gsm *gsmod; + unsigned char temp = 0; + int slot; + + + gsmod = (struct bmi_gsm *)(filp->private_data); + + if(gsmod->bdev == 0) + return -ENODEV; + + slot = gsmod->bdev->slot->slotnum; + + // ioctl's + temp = bmi_slot_gpio_get(slot); + switch (cmd) { + case BMI_GSM_RLEDOFF: + bmi_slot_gpio_set(slot, temp & (~RED_LED)); // Red LED=OFF + break; + case BMI_GSM_RLEDON: + bmi_slot_gpio_set(slot, temp | RED_LED); // Red LED=ON + break; + case BMI_GSM_GLEDOFF: + bmi_slot_gpio_set(slot, temp & (~GREEN_LED)); // Green LED=OFF + break; + case BMI_GSM_GLEDON: + bmi_slot_gpio_set(slot, temp | GREEN_LED); // Green LED=ON + break; + } + return 0; +} + +// control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + + +// Probe +int bmi_gsm_probe(struct bmi_device *bdev) +{ + int slot; + int status; + int temp = 0; + dev_t dev_id; + struct cdev *cdev; + struct class *bmi_class; + struct bmi_gsm *gsmod; + + + slot = bdev->slot->slotnum; + gsmod = &bmi_gsm_priv[slot]; + gsmod->bdev = 0; + gsmod->open_flag = 0; + + cdev = &gsmod->cdev; + cdev_init(cdev, &cntl_fops); + dev_id = MKDEV(major, slot); + status = cdev_add(cdev, dev_id, 1); + if ( status) + return status; + + bmi_class = bmi_get_class(); + gsmod->class_dev = device_create (bmi_class, NULL, MKDEV(major, slot), NULL, "bmi_gsm_ctl_m%i", slot + 1); + + if (IS_ERR(gsmod->class_dev)) + { + printk(KERN_ERR "Unable to create class device for bmi_gsm_ctl_m%i...", slot + 1); + gsmod->class_dev = NULL; + cdev_del(&gsmod->cdev); + return -ENODEV; + } + + // configure GPIO + + // set GPIO direction + bmi_slot_gpio_configure (slot, RED_LED | GREEN_LED | GPIO_0); + + // turn LED's on + bmi_slot_gpio_set (slot, ~(RED_LED | GREEN_LED | GPIO_0)); + mdelay(500); + // turn LED's off + bmi_slot_gpio_set (slot, (RED_LED | GREEN_LED)); + + // Check if SIM is present... + // gpio_reg = bmi_read_gpio_data_reg(slot); + // turn mini-card on + printk(KERN_INFO "Turning Sierra Wireless Card On...\n"); + temp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set(slot, temp & (~GPIO_0)); + + // set up bdev/pbmi_gsm pointers + gsmod->bdev = bdev; + bmi_device_set_drvdata(bdev, &gsmod); + + return 0; +} + +// remove +void bmi_gsm_remove(struct bmi_device *bdev) +{ + int slot; + struct bmi_gsm *gsmod; + struct class *bmi_class; + + slot = bdev->slot->slotnum; + + gsmod = &bmi_gsm_priv[slot]; + + bmi_slot_gpio_configure(slot, 0); + + bmi_class = bmi_get_class (); + device_destroy (bmi_class, MKDEV(major, slot)); + + gsmod->class_dev = 0; + + cdev_del (&gsmod->cdev); + + // de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + gsmod->bdev = 0; + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, NULL); + + return; +} + + +static __init int bmi_gsm_init(void) +{ + + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + retval = alloc_chrdev_region (&dev_id, 0, 4, "BMI GSM Driver"); + if (retval) { + return -ENODEV; + } + + major = MAJOR(dev_id); + retval = bmi_register_driver (&bmi_gsm_driver); + if (retval) { + unregister_chrdev_region(dev_id, 4); + return -ENODEV; + } + + printk("bmi_gsm.c: BMI_GSM Driver v%s \n", BMIGSM_VERSION); + + return 0; +} + +static void __exit bmi_gsm_clean(void) +{ + dev_t dev_id; + + bmi_unregister_driver (&bmi_gsm_driver); + + dev_id = MKDEV(major, 0); + unregister_chrdev_region (dev_id, 4); + return; +} + +module_init(bmi_gsm_init); +module_exit(bmi_gsm_clean); + +MODULE_AUTHOR("Matt Isaacs "); +MODULE_DESCRIPTION("BMI gsm device driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/drivers/bmi/pims/lcd/Makefile @@ -0,0 +1,9 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_VIDEO_BMI_LCD) += bmi_lcd_core.o +bmi_lcd_core-objs := bmi_lcd.o acc.o + +obj-$(CONFIG_VIDEO_BMI_LCD_S320X240) += bmi_s320x240.o + --- /dev/null +++ git/drivers/bmi/pims/lcd/acc.c @@ -0,0 +1,114 @@ +#include "acc.h" +#include +#include +#include + +#define BMI_SLOT_NUM 4 + +static dev_t acc_dev_number; + +static struct file_operations acc_fops = { + .owner = THIS_MODULE, + .open = acc_open, + .read = acc_read, + .release = acc_release +}; + + +int acc_open(struct inode *inode, struct file *file) +{ + struct acc_dev * acc; + + printk(KERN_DEBUG "ACC_OPEN\n"); + + acc = container_of(inode->i_cdev, struct acc_dev, cdev); + + file->private_data = acc; + + return 0; +} + +int acc_release(struct inode *inode, struct file *file) +{ + printk(KERN_DEBUG "ACC_RELEASE"); + + return 0; +} + +int acc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct acc_dev * acc = file->private_data; + int result = 0; + + if(count < 6) { + return -EINVAL; + } + + if(wait_event_interruptible(acc->wq, acc->flag != 0)) { + return -ERESTARTSYS; + } + + result = copy_to_user(buf, acc->sample, 6); + acc->flag = 0; + if(result) { + return -EFAULT; + } + + return 6; +} + +int acc_init() +{ + if(alloc_chrdev_region(&acc_dev_number, 0, BMI_SLOT_NUM, "bmi_lcd_acc") < 0) { + printk(KERN_DEBUG "Unable to register accelerometer device\n"); + return -1; + } + + return 0; +} + +int acc_clean() +{ + unregister_chrdev_region(MAJOR(acc_dev_number), BMI_SLOT_NUM); + return 0; +} + +/* BMI Functions */ +int acc_probe (struct acc_dev *acc, int slot) +{ + struct class * bmi_class; + struct cdev * cdev; + int ret; + + printk(__FUNCTION__); + cdev = &acc->cdev; + printk(KERN_DEBUG "\nAbout to cdev_init acc=%p cdev=%p\n acc_fops=%p\n", acc, cdev, &acc_fops); + cdev_init(cdev, &acc_fops); + printk(KERN_DEBUG "After cdev_init\n"); + + ret = cdev_add(cdev, acc_dev_number + slot, 1); + printk(KERN_DEBUG "After cdev_add" ); + + bmi_class = (struct class *) bmi_get_bmi_class(); + printk(KERN_DEBUG "After bmi_get_bmi_class" ); + acc->class_dev = device_create(bmi_class, NULL, acc_dev_number + slot, acc, "bmi_lcd_acc_m%d", slot + 1); + printk(KERN_DEBUG "After class_device_create" ); + + init_waitqueue_head(&acc->wq); + printk(KERN_DEBUG "After init_waitqueue_head" ); + return ret; +} + +void acc_remove (struct acc_dev *acc, int slot) +{ + struct class *bmi_class; + int acc_major = MAJOR(acc_dev_number); + + bmi_class = (struct class *) bmi_get_bmi_class(); + device_destroy (bmi_class, MKDEV(acc_major, slot)); + + acc->class_dev = 0; + + cdev_del (&acc->cdev); +} + --- /dev/null +++ git/drivers/bmi/pims/lcd/acc.h @@ -0,0 +1,35 @@ +#ifndef _ACC_H_ +#define _ACC_H_ + +#include +#include +#include +#include +#include + +/* Accelerometer device structure */ +struct acc_dev { + struct cdev cdev; + u8 sample[6]; + u8 flag; + struct device * class_dev; + wait_queue_head_t wq; +}; + + +int acc_open(struct inode *inode, struct file *file); + +int acc_release(struct inode *inode, struct file *file); + +int acc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); + +int acc_init(void); + +int acc_clean(void); + +/*BMI Functions */ +void acc_remove(struct acc_dev *acc, int slot); + +int acc_probe(struct acc_dev *acc, int slot); + +#endif //_ACC_H_ --- /dev/null +++ git/drivers/bmi/pims/lcd/bmi_lcd.c @@ -0,0 +1,1790 @@ +/* + * bmi_lcd.c + * + * BMI LCD device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef ACCELEROMETER +#define ACCELEROMETER + +#ifdef ACCELEROMETER +#include "acc.h" +#endif //ACCELEROMETER + +#define DEBUG +#undef DEBUG + +#define BMILCD_VERSION "1.2" // driver version +#define BUF_MAX_SIZE 0x20 // spi buffer size +#define WORK_DELAY (1) // interrupt work handler delay +#define DEBOUNCE 10 // touch screen debounce +#define X_PLATE 400 // touch screen X plate resistance //pjg - This is not the correct value +#define BMI_SLOT_NUM (4) // number of BMI slots +#define MAX_STRG (40) // Max string buffer size + +#define VSYNC_DISABLE 0x0 +#define VSYNC_ENABLE 0x1 + + // lcd +struct lcd_interface { + char lcd_type[MAX_STRG]; // text description of LCD type + u8 suspended; // power management state + u8 rotation; // screen rotation + u8 disp; // display number (DISP0 or DISP1) + u8 addr_mode; // display addressing mode + u8 vsync_mode; // VSYNC signal enable (VSYNC_ENABLE | VSYNC_DISABLE) + u8 bus_if_type; // bus type (XY | FullWoBE | FullWithBE) + ipu_adc_sig_cfg_t adc_sig; // IPU ADC set-up parameters + ipu_di_signal_cfg_t di_sig; // IPU DI set-up parameters +}; + + +struct lcd_ctl +{ + int slot; + struct cdev cdev; + struct device *class_dev; +}; + + +static struct lcd_interface s320x240_lcd_interface = { + .lcd_type = "MXCFB_SHARP_320X240", + .suspended = 0, + .rotation = IPU_ROTATE_NONE, + .disp = DISP0, + .vsync_mode = VSYNC_DISABLE, + .bus_if_type = XY, + .adc_sig = { 0, 0, 0, 0, 0, 0, 0, 0, IPU_ADC_BURST_WCS, IPU_ADC_IFC_MODE_SYS80_TYPE2, + 16, 0, 0, IPU_ADC_SER_NO_RW }, + .di_sig = { 0,0,0,0,0,0,0,0 }, //pjg - reserved for multiple LCD driver +}; + +extern void s320x240_config(int disp); +extern void s320x240_disp_off(int disp); +extern void s320x240_disp_on(int disp); + + +struct bmi_lcd; + +struct bmi_lcd_ops { + void *(*config) (int disp); // LCD configuration/initialization + void *(*reset) (int slot); // LCD reset + int *(*suspend) (struct bmi_lcd *blcd); // power management + int *(*resume) (struct bmi_lcd *blcd); // power management + int *(*disp_on) (int disp); // display on + int *(*disp_off) (int disp); // display off + int (*activate) (struct bmi_lcd *lcd, int slot); // enable LCD backlight, touchscreen, accelerometer, ... + int (*deactivate) (struct bmi_lcd *lcd, int slot); // disable LCD backlight, touchscreen, accelerometer, ... +}; + +struct bmi_lcd_ops s320x240_bmi_lcd_ops; + +struct bmi_lcd { + struct lcd_interface interface; // pointer to this struct is returned by config() + struct bmi_lcd_ops lcd_ops; // function pointers +}; + +static struct bmi_lcd s320x240_bmi_lcd; + +int register_bmi_lcd(struct bmi_lcd *blcd, int slot); +int unregister_bmi_lcd(struct bmi_lcd *blcd, int slot); + + // private device structure +struct pbmi_lcd +{ + unsigned int lcd_cnt; // number of LCD's present + unsigned int active; // indication of LCD presence + unsigned int activated[BMI_SLOT_NUM]; // indication of LCD presence + + struct bmi_lcd *blcd[BMI_SLOT_NUM]; // BMI LCD structure - placeholder for multiple display types + struct bmi_device *bdev[BMI_SLOT_NUM]; // BMI device per slot + unsigned int interrupt[BMI_SLOT_NUM]; // input device interrupt handlers + char int_name[MAX_STRG]; // interrupt name + + struct input_dev *input_dev[BMI_TS_NUM]; // input device (touch screen and accelerometer) + struct timer_list timer[BMI_SLOT_NUM]; // touch timer + struct lcd_ctl ctl_dev[BMI_SLOT_NUM]; + int pen_down[BMI_SLOT_NUM]; + int scount[BMI_SLOT_NUM]; + + struct spi_device *spi[BMI_SLOT_NUM]; // touch screen device interface + struct semaphore sem[BMI_SLOT_NUM]; // spi semaphore + char rbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi read buffer + char wbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi write buffer + +#ifdef ACCELEROMETER + struct acc_dev acc[BMI_SLOT_NUM]; +#endif +}; + +static struct pbmi_lcd pbmi_lcd; // LCD device sructure + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_lcd_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_LCD_SHARP_320X240, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_lcd_tbl); + +/*printk(KERN_INFO "MDT: 0x%x\n", __mod_bmi_device_table);*/ + +int bmi_lcd_probe(struct bmi_device *bdev); +void bmi_lcd_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_lcd_driver = +{ + .name = "bmi_lcd", + .id_table = bmi_lcd_tbl, + .probe = bmi_lcd_probe, + .remove = bmi_lcd_remove, +}; + +//Accelerometer driver structure + + +/* + * I2C set up + */ + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define BMI_ACC_I2C_ADDRESS 0x17 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 // IOX input data register +#define IOX_OUTPUT_REG 0x1 // IOX output data register +#define IOX_POLARITY_REG 0x2 // IOX polarity data register +#define IOX_CONTROL 0x3 // IOX direction control register +#define IOX_B1 (0) // bit 0 - backlight control +#define IOX_A1_A2 (1) // bit 1 - backlight control +#define IOX_ACC_RST_N (2) // bit 2 - acceleromter reset +#define IOX_VSYNC_EN_N (3) // bit 3 - VSYNC output buffer enable +#define IOX_LCD_RST_N (4) // bit 4 - LCD reset +#define IOX_SERDES_PD_N (5) // bit 5 - SERDES power down +#define IOX_X_INT (6) // bit 6 - accelerometer interrupt +#define IOX_Y_INT (7) // bit 7 - accelerometer interrupt + + // I2C ACC register addresses - OKI +#define ACC_PAGESEL 0x1E // device ready status + // page 0 +#define ACC_DVRST 0x01 // device reset + #define ACC_DVRST_RST 0x3C // device reset + #define ACC_DVRST_EN 0xC3 // device enable +#define ACC_PDWN 0x02 // osc power down + #define ACC_PWDN_RST 0x01 // device reset + #define ACC_PWDN_EN 0x00 // device enable +#define ACC_CTRL0 0x03 // control 0 + #define ACC_CTRL0_CTSTR 0x40 // control 0 - temp sensor + #define ACC_CTRL0_CGSTRNC 0x08 // control 0 - 3-axis/no tilt + #define ACC_CTRL0_CGSTRC 0x04 // control 0 - 3-axis/tilt + #define ACC_CTRL0_CGAUTO 0x01 // control 0 - auto +#define ACC_MODE0 0x05 // control 0 + #define ACC_MODE0_PDOFF 0x80 // mode 0 - disable auto power down + #define ACC_MODE0_RVOFF 0x40 // mode 0 - disable temp compensation + #define ACC_MODE0_TMPOFF 0x20 // mode 0 - disable temp measurement + #define ACC_MODE0_AGCON 0x10 // mode 0 - enable auto mode pitch and roll + #define ACC_MODE0_MAUTO 0x04 // mode 0 - enable auto termination + #define ACC_MODE0_GDET00 0x00 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET01 0x01 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET10 0x02 // mode 0 - g detection threshold - see ML8953 data sheet +#define ACC_MODE1 0x06 // mode 1 + #define ACC_MODE1_MOFF 0x20 // mode 1 - disable 3-axis continuous mode + #define ACC_MODE1_ZAXIS 0x03 // mode 1 - Z axis + #define ACC_MODE1_YAXIS 0x02 // mode 1 - Y axis + #define ACC_MODE1_XAXIS 0x01 // mode 1 - X axis + #define ACC_MODE1_RAXIS 0x00 // mode 1 - Reference axis +#define ACC_INTRQ 0x07 // interrupt request (1 = request) +#define ACC_INTMSK 0x08 // interrupt mask (1 = masked) + #define ACC_INT_TREQ 0x20 // interrupt - temperature + #define ACC_INT_GREQ 0x08 // interrupt - acceleration/no tilt + #define ACC_INT_GCREQ 0x04 // interrupt - acceleration/tilt + #define ACC_INT_GAREQ 0x01 // interrupt - automatic +#define ACC_TMDL 0x09 // timer LSB = (1/6.2 MHz) x 2048 x TMD +#define ACC_TMDH 0x0A // timer MSB +#define ACC_CFG 0x0C // configuration + #define ACC_CFG_REGMD 0x80 // address auto-increment + #define ACC_CFG_SPI3M_3 0x40 // spi mode = 3-wire + #define ACC_CFG_SPI3M_4 0x00 // spi mode = 4-wire + #define ACC_CFG_SDOCFG_T 0x10 // sdo mode = totem-pole + #define ACC_CFG_SDOCFG_OC 0x00 // sdo mode = open-drain + #define ACC_CFG_INT1EN_G 0x08 // interrupt 1 mode = g only + #define ACC_CFG_INT1EN_ALL 0x00 // interrupt 1 mode = all + #define ACC_CFG_INTLVL 0x04 // interrupt level mode + #define ACC_CFG_INT1CFG_T 0x02 // interrupt 1 mode = totem-pole + #define ACC_CFG_INT1CFG_OC 0x00 // interrupt 1 mode = open-drain + #define ACC_CFG_INT0CFG_T 0x01 // interrupt 0 mode = totem-pole + #define ACC_CFG_INT0CFG_OC 0x00 // interrupt 0 mode = open-drain +#define ACC_INTOTM 0x0D // interrupt output conditions +#define ACC_GAAVE 0x0E // Data averaging - automatic mode +#define ACC_GNAVE 0x0F // Data averaging - normal mode +#define ACC_GDTCT0L 0x11 // threshold 0 LSB +#define ACC_GDTCT0H 0x12 // threshold 0 MSB +#define ACC_GDTCT1L 0x13 // threshold 1 LSB +#define ACC_GDTCT1H 0x14 // threshold 1 MSB +#define ACC_CPURDY 0x15 // device ready status (ready = 0x01) + // page 1 +#define ACC_STATUS 0x01 // measurment status + #define ACC_STATUS_ASTS 0x02 // acceleration measurement - automatic modes + #define ACC_STATUS_STS 0x01 // acceleration measurement - non-automatic modes +#define ACC_GAXL 0x02 // g vector +#define ACC_GAXH 0x03 // g vector +#define ACC_GAYL 0x04 // g vector +#define ACC_GAYH 0x05 // g vector +#define ACC_GAZL 0x06 // g vector +#define ACC_GAZH 0x07 // g vector +#define ACC_GASVL 0x08 // g vector +#define ACC_GASVH 0x09 // g vector +#define ACC_GNXL 0x0A // g vector +#define ACC_GNXH 0x0B // g vector +#define ACC_GNYL 0x0C // g vector +#define ACC_GNYH 0x0D // g vector +#define ACC_GNZL 0x0E // g vector +#define ACC_GNZH 0x0F // g vector +#define ACC_GNSVL 0x10 // g vector +#define ACC_GNSVH 0x11 // g vector +#define ACC_PITCHL 0x12 // pitch +#define ACC_PITCHH 0x13 // pitch +#define ACC_ROLLL 0x14 // roll +#define ACC_ROLLH 0x15 // roll +#define ACC_TEMPL 0x19 // temperature +#define ACC_TEMPH 0x1A // temperature + + // read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + // write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +#if defined ACCELEROMETER + // read byte from I2C acceleromter +static int ReadByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_ACC_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_ACC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + // write byte to I2C accelerometer +static int WriteByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_ACC_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_ACC_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} +#endif // ACCELEROMETER + +/* + * SPI functions + */ + + // TSC2046 touch screen controller command register bit definitons +#define SPI_START 0x80 // command start +#define SPI_AT0 0x00 // read temperature - not supported +#define SPI_AY 0x10 // read Y +#define SPI_ABAT 0x20 // read battery - not supported +#define SPI_AZ1 0x30 // read Z1 +#define SPI_AZ2 0x40 // read Z2 +#define SPI_AX 0x50 // read X +#define SPI_AAUX 0x60 // read AUX - not supported +#define SPI_AT1 0x70 // read temperature - not supported +#define SPI_MODE_12 0x00 // 12-bit mode - Preferred +#define SPI_MODE_8 0x08 // 8-bit mode +#define SPI_MODE_DFR 0x00 // differential mode - Preferred +#define SPI_MODE_SER 0x04 // single ended mode +#define SPI_PD 0x00 // power down - PENIRQ enabled +#define SPI_ADC 0x01 // ADC enabled +#define SPI_REF 0x02 // Vref enabled - unused +#define SPI_REF_ADC 0x03 // Vref & ADC enabled - unused + + // spi access +static int spi_rw(struct spi_device *spi, u8 * buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + + spi_message_add_tail(&t, &m); + if (spi_sync(spi, &m) != 0 || m.status != 0) + return -1; + + return m.actual_length; +} + + // spi write register +static ssize_t spi_lcd_write_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + up(&priv->sem[slot]); + + return res; +} + + // spi read register +static ssize_t spi_lcd_read_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + memset(priv->rbuf[slot], 0, BUF_MAX_SIZE); + buf[0] = priv->wbuf[slot][2]; + buf[1] = priv->wbuf[slot][1]; + + up(&priv->sem[slot]); + + return res; +} + + +// control file operations +static int lcd_ctl_open (struct inode *, struct file *); +static int lcd_ctl_release (struct inode *, struct file *); +static int lcd_ctl_ioctl (struct inode *, struct file *, unsigned int, unsigned long); + +struct file_operations lcd_ctl_fops = { + .owner = THIS_MODULE, + .ioctl = lcd_ctl_ioctl, + .open = lcd_ctl_open, + .release = lcd_ctl_release, +}; + +/* + * control device operations + */ +static int lcd_ctl_major; + +int lcd_ctl_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI LCD Control Driver"); + + if (retval) { + return -1; + } + lcd_ctl_major = MAJOR(dev_id); + return 0; +} + +void lcd_ctl_clean (void) +{ + dev_t dev_id; + + dev_id = MKDEV(lcd_ctl_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int lcd_ctl_probe (struct lcd_ctl *lcd_ctl, int slot) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + cdev = &lcd_ctl->cdev; + cdev_init (cdev, &lcd_ctl_fops); + + dev_id = MKDEV (lcd_ctl_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + bmi_class = bmi_get_bmi_class (); + + lcd_ctl->class_dev = device_create (bmi_class, NULL, MKDEV(lcd_ctl_major, slot), lcd_ctl, "bmi_lcd_ctl_m%i", slot+1); + + if (IS_ERR(lcd_ctl->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_lcd_ctl_m%i; errno = %ld\n", + slot+1, PTR_ERR(lcd_ctl->class_dev)); + lcd_ctl->class_dev = NULL; + } + lcd_ctl->slot = slot; + + return ret; +} + +void lcd_ctl_remove (struct lcd_ctl *lcd_ctl, int slot) +{ + struct class *bmi_class; + + bmi_class = bmi_get_bmi_class (); + device_destroy (bmi_class, MKDEV(lcd_ctl_major, slot)); + + lcd_ctl->class_dev = 0; + + cdev_del (&lcd_ctl->cdev); + return; +} + +// open +static int lcd_ctl_open (struct inode *inode, struct file *file) +{ + struct lcd_ctl *lcd_ctl; + + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + + + // Save ctl pointer for later. + + file->private_data = lcd_ctl; + return 0; +} + +// release +static int lcd_ctl_release (struct inode *inode, struct file *file) +{ + struct lcd_ctl *lcd_ctl; + + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + return 0; +} + +// ioctl +int lcd_ctl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct lcd_ctl *lcd_ctl; + struct i2c_adapter *adap; + unsigned char iox_data[1]; + int slot; + int bl = ((__user arg) & 0x70) >> 4; + + // error if no lcd active. + + if(cmd != BMI_LCD_GETSTAT) { + // i2c adapter + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + slot = lcd_ctl->slot; + if (slot < 0) { + return -ENODEV; + } + adap = &pbmi_lcd.bdev[slot]->adap; + } + + // ioctl's + switch (cmd) { + case BMI_LCD_RLEDOFF: + bmi_set_module_gpio_data(slot, 3, 1);// Red LED=OFF + break; + case BMI_LCD_RLEDON: + bmi_set_module_gpio_data(slot, 3, 0);// Red LED=ON + break; + case BMI_LCD_GLEDOFF: + bmi_set_module_gpio_data(slot, 2, 1);// Green LED=OFF + break; + case BMI_LCD_GLEDON: + bmi_set_module_gpio_data(slot, 2, 0);// Green LED=ON + break; + case BMI_LCD_VSYNC_DIS: // enable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_VSYNC_EN: // disable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_EN: // enable LCD component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_DIS: // disable LCD component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_EN: // enable Serializer component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_DIS: // disable Serializer component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SETRST: // overall module reset + bmi_set_module_gpio_data (slot, 1, 0); // RST=0 + break; + case BMI_LCD_CLRRST: // overall module enable + bmi_set_module_gpio_data (slot, 1, 1); // RST=1 + break; + case BMI_LCD_SET_BL: // set backlight brightness + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xFC) | bl; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_GETSTAT: + { + int *slot = ((int __user *) arg); + int read_data; + + *slot &= 0xF; + + // error if slot invalid + if((*slot < CPLD_M1) || (*slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[*slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[*slot]->adap; + + if(ReadByte_IOX(adap, IOX_INPUT_REG, iox_data)) + return -ENODEV; + + read_data = *iox_data | (bmi_read_gpio_data_reg(*slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + case BMI_LCD_ACTIVATE: //pjg fix/test + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + return -ENODEV; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + return -ENODEV; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + return -ENODEV; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + return -ENODEV; + } + break; + } + // activate + if((!pbmi_lcd.activated[slot]) && (pbmi_lcd.bdev[slot] != 0)) { + bmi_lcd_probe(pbmi_lcd.bdev[slot]); + } + break; + case BMI_LCD_DEACTIVATE: + if(pbmi_lcd.activated[slot]) { + disable_irq_nosync(pbmi_lcd.interrupt[slot]); + pbmi_lcd.activated[slot] = 0; + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8); + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + bmi_slot_power_off(slot); + } + break; + case BMI_LCD_SUSPEND: + printk(KERN_ERR "BMI_LCD_SUSPEND NOT IMPLEMENTED\n"); //pjg + break; + case BMI_LCD_RESUME: + printk(KERN_ERR "BMI_LCD_RESUME NOT IMPLEMENTED\n"); //pjg + break; + default: + return -ENOTTY; + } + return 0; +} + + +/* + * BMI functions + */ + +static irqreturn_t module_irq_handler(int irq, void *dummy); +void bmi_lcd_config(struct bmi_lcd *lcd, int disp); + + // probe +int bmi_lcd_probe(struct bmi_device *bdev) +{ +#if defined ACCELEROMETER + unsigned char acc_data[1]; +#endif // ACCELEROMETER + unsigned char iox_data[1]; + int slot = bdev->info->slot; + struct i2c_adapter *adap; + struct bmi_lcd *lcd; + char buf[4]; + /*int first_time = 1;*/ + + printk(KERN_INFO "bmi_lcd.c: probe slot %d\n", slot); + + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + /*bmi_slot_power_off(2);*/ + pbmi_lcd.bdev[0] = bdev; + /*bdev = pbmi_lcd.bdev[2]; + slot = 2; + first_time = 0;*/ + return 0; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + /*bmi_slot_power_off(3);*/ + pbmi_lcd.bdev[1] = bdev; + /*bdev = pbmi_lcd.bdev[3]; + slot = 3; + first_time = 0;*/ + return 0; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + /*bmi_slot_power_off(0);*/ + pbmi_lcd.bdev[2] = bdev; + /*bdev = pbmi_lcd.bdev[0]; + slot = 0; + first_time = 0;*/ + return 0; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + /*bmi_slot_power_off(1);*/ + pbmi_lcd.bdev[3] = bdev; + /*bdev = pbmi_lcd.bdev[1]; + slot = 1; + first_time = 0;*/ + return 0; + } + break; + } + + adap = &bdev->adap; + bmi_slot_power_on(slot); + + mdelay(500); + + if (lcd_ctl_probe(&pbmi_lcd.ctl_dev[slot], slot)) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d control device node error...\n", slot); + return -ENODEV; + } + + // configure IOX + // [7:6]=interrupts, [5]=SER_PD*, [4]=LCD_RST*, [3]=VSYNC_OE*, [2]=ACC_RST*, [1:0]=backlight + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFF)) // normal - no accelerometer interrupts + return -ENODEV; + + // normal operation - no accelerometer interrupts + if(WriteByte_IOX(adap, IOX_CONTROL, 0x00)) // IOX[7:0]=OUT + return -ENODEV; + + // clear interrupts + if(ReadByte_IOX(adap, IOX_INPUT_REG, iox_data)) + return -ENODEV; + + printk(KERN_INFO "bmi_lcd.c: probe slot %d iox data = %x\n", slot, *iox_data); + +#if defined ACCELEROMETER + // accelerometer + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x\n", slot, bdev->epraw.revision_msb); + + // check for PCB revision >= 1.2 + if(bdev->epraw.revision_msb >= 0x12) { + + // normal IOX operation - accelerometer interrupts + if(WriteByte_IOX(adap, IOX_CONTROL, 0xC0)) // IOX[7:6]=IN, IOX[5:0]=OUT + return -ENODEV; + + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFB)) // reset OKI accelerometer + return -ENODEV; + + mdelay(2); + + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFF)) // enable OKI accelerometer + return -ENODEV; + + mdelay(2); + + // write PAGESEL + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return -ENODEV; + + // read device to verify existance + if(ReadByte_ACC(adap, ACC_CPURDY, acc_data)) + return -ENODEV; + + // set TMD = 0x300 (~250 ms) + *acc_data = 0x5; + if(WriteByte_ACC(adap, ACC_TMDH, *acc_data)) + return -ENODEV; + + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_TMDL, *acc_data)) + return -ENODEV; + + // set INTOTM + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_INTOTM, *acc_data)) + return -ENODEV; + + // set GxAVE + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_GAAVE, *acc_data)) + return -ENODEV; + + // set GDTCT[01] + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT0L, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT0H, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT1L, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT1H, *acc_data)) + return -ENODEV; + + // set MODE0 + *acc_data = ACC_MODE0_PDOFF | ACC_MODE0_TMPOFF | ACC_MODE0_AGCON | ACC_MODE0_MAUTO | ACC_MODE0_GDET10; + if(WriteByte_ACC(adap, ACC_MODE0, *acc_data)) + return -ENODEV; + + // set CFG + *acc_data = ACC_CFG_REGMD | ACC_CFG_INTLVL; + if(WriteByte_ACC(adap, ACC_CFG, *acc_data)) + return -ENODEV; + + // set INTMSK + *acc_data = 0xFE; + if(WriteByte_ACC(adap, ACC_INTMSK, *acc_data)) + return -ENODEV; + + // set CTRL0 + *acc_data = ACC_CTRL0_CGAUTO; + if(WriteByte_ACC(adap, ACC_CTRL0, *acc_data)) + return -ENODEV; + + // write PAGESEL + *acc_data = 0x1; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return -ENODEV; + + acc_probe(&pbmi_lcd.acc[slot], slot); + + } else { + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x (accelerometer not supported)\n", slot, bdev->epraw.revision_msb); + } +#endif // ACCELEROMETER + + // reset serial link (master) + if((slot == 0) || (slot == 2)) { + bmi_lcd_inactive(0); + } else { + bmi_lcd_inactive(1); + } + + // configure GPIO + // turn LED's on + bmi_set_module_gpio_data(slot, 3, 0); // Red LED=ON + bmi_set_module_gpio_data(slot, 2, 0); // Green LED=ON + + // assert reset + bmi_set_module_gpio_data(slot, 1, 0); // RST=0 + + // set GPIO direction + bmi_set_module_gpio_dir(slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 0, BMI_GPIO_IN); // real-time pen int state + + mdelay(200); + + // turn LED's off + bmi_set_module_gpio_data(slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data(slot, 2, 1); // Green LED=OFF + + // deassert reset (module) + bmi_set_module_gpio_data(slot, 1, 1); // RST=1 + + mdelay(500); + + // unreset serial link (master) + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_active(0, 0x0, LCD_MODE_I80); + } else { + mdelay(2); + bmi_lcd_active(1, 0x0, LCD_MODE_I80); + } + + + // set up bdev/pbmi_lcd pointers + bmi_device_set_drvdata(bdev, &pbmi_lcd); + pbmi_lcd.bdev[slot] = bdev; + + // spi set-up + if (bmi_device_spi_setup(bdev, 2000000, SPI_MODE_2, 32)) { + printk(KERN_ERR "bmi_lcd.c: Unable to setup spi%d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + bmi_slot_power_off(slot); + return -EFAULT; + } + + bmi_slot_spi_enable(slot); + pbmi_lcd.spi[slot] = bmi_device_get_spi(bdev); + + + // check spi access and enable touch screen + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + if(spi_lcd_write_reg(&pbmi_lcd, buf, 1, slot) != 1) { + printk(KERN_WARNING "bmi_lcd.c: Unable set-up spi for bmi_lcd %d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_spi_disable(slot); + bmi_slot_power_off(slot); + return -EFAULT; + } + + + // complete pbmi_lcd set-up + pbmi_lcd.lcd_cnt++; + pbmi_lcd.active = 1; + pbmi_lcd.activated[slot] = 1; + + + mdelay(100); + + lcd = pbmi_lcd.blcd[slot]; + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_config(lcd, 0); + mdelay(2); + } else { + mdelay(2); + bmi_lcd_config(lcd, 1); + mdelay(2); + } + + + // request input event interrupt handler + pbmi_lcd.interrupt[0] = M1_IRQ; + pbmi_lcd.interrupt[1] = M2_IRQ; + pbmi_lcd.interrupt[2] = M3_IRQ; + pbmi_lcd.interrupt[3] = M4_IRQ; + snprintf(pbmi_lcd.int_name, sizeof(pbmi_lcd.int_name), "bmi_lcd%d", slot); + if (request_irq(pbmi_lcd.interrupt[slot], &module_irq_handler, 0, pbmi_lcd.int_name, &pbmi_lcd)) { + printk( KERN_ERR "bmi_lcd.c: Can't allocate irq %d or find lcd in slot %d\n", pbmi_lcd.interrupt[slot], slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_power_off(slot); + return -EBUSY; + } + + // check GPIO status + printk(KERN_INFO "bmi_lcd.c: slot %d gpio = %x\n", slot, bmi_read_gpio_data_reg(slot)); + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return 0; +} + +extern struct delayed_work bmilcd_work0; +extern struct delayed_work bmilcd_work1; +extern struct delayed_work bmilcd_work2; +extern struct delayed_work bmilcd_work3; + + // remove +void bmi_lcd_remove(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + + if(pbmi_lcd.activated[slot] == 0) + return; + + switch(slot) { + case 0: + cancel_delayed_work(&bmilcd_work0); + break; + case 1: + cancel_delayed_work(&bmilcd_work1); + break; + case 2: + cancel_delayed_work(&bmilcd_work2); + break; + case 3: + cancel_delayed_work(&bmilcd_work3); + break; + } + lcd_ctl_remove(&pbmi_lcd.ctl_dev[slot], slot); + + free_irq(pbmi_lcd.interrupt[slot], &pbmi_lcd); + + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); + + // bmi/spi clean-up + bmi_device_spi_cleanup(bdev); + pbmi_lcd.spi[slot] = NULL; + bmi_slot_spi_disable(slot); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, NULL); + + // deactivate + pbmi_lcd.activated[slot] = 0; + pbmi_lcd.bdev[slot] = 0; + pbmi_lcd.lcd_cnt--; + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[2] == 0)) { + bmi_lcd_inactive(0); // disable serializer + } + + if((pbmi_lcd.activated[1] == 0) && (pbmi_lcd.activated[3] == 0)) { + bmi_lcd_inactive(1); // disable serializer + } + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[1] == 0) && + (pbmi_lcd.activated[2] == 0) && (pbmi_lcd.activated[3] == 0)) { + pbmi_lcd.active = -1; + } + + // enable LCD on opposite side + switch(slot) { + case 0: + if(pbmi_lcd.bdev[2] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[2]); + break; + case 1: + if(pbmi_lcd.bdev[3] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[3]); + break; + case 2: + if(pbmi_lcd.bdev[0] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[0]); + break; + case 3: + if(pbmi_lcd.bdev[1] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[1]); + break; + } + +#ifdef ACCELEROMETER + acc_remove(&pbmi_lcd.acc[slot], slot); +#endif + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return; +} + +/* + * Input interrupt handler and support routines + */ + +static void update_pen_state(void *arg, int slot, int x, int y, int pressure) +{ + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; + int sync = 0; + + if (pressure) + { + if((slot == 0) || (slot == 2)) + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, pressure); + } + else + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, pressure); + } + + if (!pbmi_lcd->pen_down[slot]) + { + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 1); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 1); + } + + } + sync = 1; + } + else if (pbmi_lcd->pen_down[slot]) + { + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, 0); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, 0); + } + sync = 1; + } + + if (sync) + { + if((slot == 0) || (slot == 2)) + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M13]); + } + else + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M24]); + } + } + pbmi_lcd->pen_down[slot] = pressure ? 1 : 0; + +} + + +void bmilcd_input_work(void *arg, int slot); + +void bmilcd_input_work0(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 0); +} + +void bmilcd_input_work1(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 1); +} + +void bmilcd_input_work2(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 2); +} + +void bmilcd_input_work3(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 3); +} + +DECLARE_DELAYED_WORK(bmilcd_work0, bmilcd_input_work0); +DECLARE_DELAYED_WORK(bmilcd_work1, bmilcd_input_work1); +DECLARE_DELAYED_WORK(bmilcd_work2, bmilcd_input_work2); +DECLARE_DELAYED_WORK(bmilcd_work3, bmilcd_input_work3); + +// work handler +void bmilcd_input_work(void *arg, int slot) { + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; +#if defined ACCELEROMETER + struct i2c_adapter *adap = &pbmi_lcd->bdev[slot]->adap; + unsigned char acc_data[1]; + static int pitch = 0; + static int roll = 0; + static int gx = 0; + static int gy = 0; + +#endif // ACCELEROMETER + unsigned char buf[4]; + int x = 0; + int y = 0; + int z1 = 0; + int z2 = 0; + int pressure = 0; + int debounce; + int penirq; + +#if defined DEBUG + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d)\n", slot); +#endif + + if(pbmi_lcd->bdev[slot] == 0) { + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work called with no bdev active (slot %d)\n", slot); + return; + } + +#if defined ACCELEROMETER + if(pbmi_lcd->bdev[slot]->epraw.revision_msb >= 0x12) { + + // orientation + // read ROLL + if(ReadByte_ACC(adap, ACC_ROLLH, acc_data)) + goto touch; + roll = (0x0000 | *acc_data) << 8; + + if(ReadByte_ACC(adap, ACC_ROLLL, acc_data)) + goto touch; + roll = roll | *acc_data; + // read PITCH + if(ReadByte_ACC(adap, ACC_PITCHH, acc_data)) + goto touch; + pitch = (0x0000 | *acc_data) << 8; + + if(ReadByte_ACC(adap, ACC_PITCHL, acc_data)) + goto touch; + pitch = pitch | *acc_data; + + + + + if(ReadByte_ACC(adap, ACC_GAZH, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[0] = *acc_data; + + if(ReadByte_ACC(adap, ACC_GAZL, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[1] = *acc_data; + + if(ReadByte_ACC(adap, ACC_GAYH, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[2] = *acc_data; + gy = *acc_data << 8; + + if(ReadByte_ACC(adap, ACC_GAYL, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[3] = *acc_data; + gy = gy | *acc_data; + + if(ReadByte_ACC(adap, ACC_GAXH, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[4] = *acc_data; + gx = *acc_data << 8; + + if(ReadByte_ACC(adap, ACC_GAXL, acc_data)) + goto touch; + pbmi_lcd->acc[slot].sample[5] = *acc_data; + gx = gx | *acc_data; + + //wake up any read's + pbmi_lcd->acc[slot].flag = 1; + wake_up_interruptible(&pbmi_lcd->acc[slot].wq); + + // read STATUS + if(ReadByte_ACC(adap, ACC_STATUS, acc_data)) + goto touch; + + if((*acc_data & 0x1) == 0) { + + // write PAGESEL + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + goto touch; + + // read INTRQ + if(ReadByte_ACC(adap, ACC_INTRQ, acc_data)) + goto touch; + } + + // write PAGESEL + *acc_data = 0x1; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + goto touch; + + // report orientation + // printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d) pitch=0x%x, roll=0x%x, ABS_MISC=0x%x\n", + // slot, pitch, roll, pitch << 16 | roll); //pjg - debug + + input_report_abs(pbmi_lcd->input_dev[slot], ABS_MISC, (pitch << 16) | roll); + input_sync(pbmi_lcd->input_dev[slot]); + } +#endif // ACCELEROMETER + + + // read touch screen - X, Y, TOUCH, PRESSURE + touch: + penirq = bmi_slot_status_irq_state(slot); + /*printk(KERN_INFO "bmi_lcd.c: IRQ Status %d (slot %d) %d\n", penirq, slot,msecs_to_jiffies(10));*/ + + if (pbmi_lcd->activated[slot] && penirq) + { + + for(debounce = 0; debounce < DEBOUNCE; debounce++) + { + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AY | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + y = (((buf[0] << 5) | buf[1] >> 3)) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AX | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + x = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ1 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z1 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ2 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z2 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + mdelay(1); + } + + if(x && y && z1 && z2) + pressure = (X_PLATE * x / 4096) * ((z2 / z1) - 1); + + x = 4096 - x; + y = 4096 - y; + + if (pressure < 70) + { + if (pbmi_lcd->scount) + update_pen_state(arg, slot, x, y, pressure); + else + { + pbmi_lcd->scount[slot]++; + /*update_pen_state(arg, slot, 0, 0, pressure);*/ + } + } + + switch(slot) + { + case BMI_TS_M1: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + break; + case BMI_TS_M2: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + break; + case BMI_TS_M3: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + break; + case BMI_TS_M4: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + break; + } + /* printk(KERN_INFO "bmi_lcd.c: work scheduled on (slot %d)\n", slot); */ + /*buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot);*/ + } + + else + { + /*printk(KERN_INFO "bmi_lcd.c: Pen up on (slot %d)\n", slot);*/ + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot); + update_pen_state(arg,slot, 0, 0, 0); + enable_irq(pbmi_lcd->interrupt[slot]); + } + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + disable_irq_nosync(irq); + /*printk(KERN_INFO "bmi_lcd.c: Interupt on (slot %d)\n", irq);*/ + switch(irq) + { + case M1_IRQ: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M1] = 0; + break; + case M2_IRQ: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M2] = 0; + break; + case M3_IRQ: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M3] = 0; + break; + case M4_IRQ: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M4] = 0; + break; + } + return IRQ_HANDLED; +} + + + // BMI LCD fops +void bmi_lcd_config(struct bmi_lcd *lcd, int disp) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.config)) { + lcd->lcd_ops.config(disp); + } +} + +void bmi_lcd_reset(struct bmi_lcd *lcd, int slot) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.reset)) { + lcd->lcd_ops.reset(slot); + } +} + +int register_bmi_lcd(struct bmi_lcd *lcd, int slot) //pjg - placeholder for multiple LCD types +{ + if(!lcd) { + return -1; + } + if((slot < 0) || (slot > 3)) { + return -1; + } + if(pbmi_lcd.blcd[slot]) { + return -1; + } + else { + pbmi_lcd.blcd[slot] = lcd; + } + + if(lcd->lcd_ops.activate) { + lcd->lcd_ops.activate(lcd, slot); + } + + return 0; +} + +int unregister_bmi_lcd(struct bmi_lcd *lcd, int slot) //pjg - placeholder for multiple LCD types +{ + if (!lcd) { + return -1; + } + if ((slot < 0) || (slot > 3)) { + return -1; + } + if (pbmi_lcd.blcd[slot] != lcd) { + return -1; + } + else { + pbmi_lcd.blcd [slot] = 0; + lcd->lcd_ops.deactivate(lcd, slot); + } + return 0; +} + +/* + * Module functions + */ + +char const input_name0[MAX_STRG] = "bmi_lcd_ts0"; +char const input_name1[MAX_STRG] = "bmi_lcd_ts1"; +char const input_name2[MAX_STRG] = "bmi_lcd_ts2"; +char const input_name3[MAX_STRG] = "bmi_lcd_ts3"; +char const input_name4[MAX_STRG] = "bmi_lcd_ts4"; +char const input_name5[MAX_STRG] = "bmi_lcd_ts5"; +char const input_name6[MAX_STRG] = "bmi_lcd_ts6"; + +static __init int bmi_lcd_init(void) +{ + int ts; + int rc = 0; + + // No lcd is active. + pbmi_lcd.active = -1; + pbmi_lcd.activated[0] = 0; + pbmi_lcd.activated[1] = 0; + pbmi_lcd.activated[2] = 0; + pbmi_lcd.activated[3] = 0; + + // set up control character device - bmi_lcd_control + rc = lcd_ctl_init(); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't allocate bmi_lcd_control device\n"); + return rc; + } + + // Allocate and Register input device. - bmi_lcd_ts[BMI_TS_M1:BMI_TS_M1234] + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) { + pbmi_lcd.input_dev[ts] = input_allocate_device(); + if(!pbmi_lcd.input_dev[ts]) { + printk(KERN_ERR "bmi_lcd_init: Can't allocate input_dev[ts]\n"); + return -ENOMEM; + } + + // set up input device + switch(ts) { + case BMI_TS_M1: + pbmi_lcd.input_dev[BMI_TS_M1]->name = input_name0; + pbmi_lcd.input_dev[BMI_TS_M1]->phys = input_name0; + break; + case BMI_TS_M2: + pbmi_lcd.input_dev[BMI_TS_M2]->name = input_name1; + pbmi_lcd.input_dev[BMI_TS_M2]->phys = input_name1; + break; + case BMI_TS_M3: + pbmi_lcd.input_dev[BMI_TS_M3]->name = input_name2; + pbmi_lcd.input_dev[BMI_TS_M3]->phys = input_name2; + break; + case BMI_TS_M4: + pbmi_lcd.input_dev[BMI_TS_M4]->name = input_name3; + pbmi_lcd.input_dev[BMI_TS_M4]->phys = input_name3; + break; + case BMI_TS_M13: + pbmi_lcd.input_dev[BMI_TS_M13]->name = input_name4; + pbmi_lcd.input_dev[BMI_TS_M13]->phys = input_name4; + break; + case BMI_TS_M24: + pbmi_lcd.input_dev[BMI_TS_M24]->name = input_name5; + pbmi_lcd.input_dev[BMI_TS_M24]->phys = input_name5; + break; + case BMI_TS_M1234: + pbmi_lcd.input_dev[BMI_TS_M1234]->name = input_name6; + pbmi_lcd.input_dev[BMI_TS_M1234]->phys = input_name6; + break; + } + pbmi_lcd.input_dev[ts]->id.bustype = BUS_BMI; + //pbmi_lcd.input_dev[ts]->private = &pbmi_lcd; + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); + pbmi_lcd.input_dev[ts]->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_X)] |= BIT_MASK(ABS_X); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_Y)] |= BIT_MASK(ABS_Y); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_PRESSURE)] |= BIT_MASK(ABS_PRESSURE); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_X, BMI_LCD_MIN_XC, BMI_LCD_MAX_XC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_Y, BMI_LCD_MIN_YC, BMI_LCD_MAX_YC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_PRESSURE, 0, 1024, 0, 0); + + // register input device + if(input_register_device(pbmi_lcd.input_dev[ts])) { + int tts; + printk(KERN_ERR "bmi_lcd_init() - input_register_device failed.\n"); + + for(tts = BMI_TS_M1; tts < ts; tts++) + input_unregister_device(pbmi_lcd.input_dev[tts]); + + lcd_ctl_clean(); + + return -ENODEV; + } + } + + pbmi_lcd.lcd_cnt = 0; + + // hardware specfic set-up + s320x240_bmi_lcd.interface = s320x240_lcd_interface, + s320x240_bmi_lcd_ops.config = (void(*)) &s320x240_config; + s320x240_bmi_lcd_ops.reset = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.suspend = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.resume = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_on = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_off = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.activate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.deactivate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd.lcd_ops = s320x240_bmi_lcd_ops; + pbmi_lcd.blcd[0] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[1] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[2] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[3] = &s320x240_bmi_lcd; + + sema_init(&pbmi_lcd.sem[0], 1); + sema_init(&pbmi_lcd.sem[1], 1); + sema_init(&pbmi_lcd.sem[2], 1); + sema_init(&pbmi_lcd.sem[3], 1); + + +#ifdef ACCELEROMETER + acc_init(); +#endif + /*s320x240_config(0); + s320x240_config(1);*/ + + // register with BMI + rc = bmi_register_driver(&bmi_lcd_driver); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't register bmi_lcd_driver\n"); + + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + lcd_ctl_clean(); + + return rc; + } + + printk("bmi_lcd.c: BMI_LCD Driver v%s \n", BMILCD_VERSION); + + return 0; +} + +static void __exit bmi_lcd_clean(void) +{ + int ts; + + // delete timers + del_timer(&pbmi_lcd.timer[0]); + del_timer(&pbmi_lcd.timer[1]); + del_timer(&pbmi_lcd.timer[2]); + del_timer(&pbmi_lcd.timer[3]); + + // remove input devices + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + // remove control device + lcd_ctl_clean(); + + // remove bmi driver + bmi_unregister_driver(&bmi_lcd_driver); + +#ifdef ACCELEROMETER + acc_clean(); +#endif + return; +} + +module_init(bmi_lcd_init); +module_exit(bmi_lcd_clean); + +// Exported symbols +EXPORT_SYMBOL(register_bmi_lcd); +EXPORT_SYMBOL(unregister_bmi_lcd); + + +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BMI lcd device driver"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_control"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_ts"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_acc"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/drivers/bmi/pims/lcd/bmi_lcd_inf.c @@ -0,0 +1,1775 @@ +/* + * bmi_lcd.c + * + * BMI LCD device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef ACCELEROMETER +#define ACCELEROMETER + +#ifdef ACCELEROMETER +#include "acc.h" +#endif //ACCELEROMETER + +#define DEBUG +#undef DEBUG + +#define BMILCD_VERSION "1.2" // driver version +#define BUF_MAX_SIZE 0x20 // spi buffer size +#define WORK_DELAY (1) // interrupt work handler delay +#define DEBOUNCE 10 // touch screen debounce +#define X_PLATE 400 // touch screen X plate resistance //pjg - This is not the correct value +#define BMI_SLOT_NUM (4) // number of BMI slots +#define MAX_STRG (40) // Max string buffer size + +#define VSYNC_DISABLE 0x0 +#define VSYNC_ENABLE 0x1 + + // lcd +struct lcd_interface { + char lcd_type[MAX_STRG]; // text description of LCD type + u8 suspended; // power management state + u8 rotation; // screen rotation + u8 disp; // display number (DISP0 or DISP1) + u8 addr_mode; // display addressing mode + u8 vsync_mode; // VSYNC signal enable (VSYNC_ENABLE | VSYNC_DISABLE) + u8 bus_if_type; // bus type (XY | FullWoBE | FullWithBE) + ipu_adc_sig_cfg_t adc_sig; // IPU ADC set-up parameters + ipu_di_signal_cfg_t di_sig; // IPU DI set-up parameters +}; + +static struct lcd_interface s320x240_lcd_interface = { + .lcd_type = "MXCFB_SHARP_320X240", + .suspended = 0, + .rotation = IPU_ROTATE_NONE, + .disp = DISP0, + .vsync_mode = VSYNC_DISABLE, + .bus_if_type = XY, + .adc_sig = { 0, 0, 0, 0, 0, 0, 0, 0, IPU_ADC_BURST_WCS, IPU_ADC_IFC_MODE_SYS80_TYPE2, + 16, 0, 0, IPU_ADC_SER_NO_RW }, + .di_sig = { 0,0,0,0,0,0,0,0 }, //pjg - reserved for multiple LCD driver +}; + + +struct bmi_lcd; + +struct bmi_lcd_ops { + void *(*config) (int disp); // LCD configuration/initialization + void *(*reset) (int slot); // LCD reset + int *(*suspend) (struct bmi_lcd *blcd); // power management + int *(*resume) (struct bmi_lcd *blcd); // power management + int *(*disp_on) (int disp); // display on + int *(*disp_off) (int disp); // display off + int (*activate) (struct bmi_lcd *lcd, int slot); // enable LCD backlight, touchscreen, accelerometer, ... + int (*deactivate) (struct bmi_lcd *lcd, int slot); // disable LCD backlight, touchscreen, accelerometer, ... +}; + + +struct bmi_lcd { + struct lcd_interface interface; // pointer to this struct is returned by config() + struct bmi_lcd_ops lcd_ops; // function pointers +}; + + +int register_bmi_lcd(struct bmi_lcd *blcd, int slot); +int unregister_bmi_lcd(struct bmi_lcd *blcd, int slot); + + // private device structure +struct pbmi_lcd +{ + int open_flag; // force single open + unsigned int lcd_cnt; // number of LCD's present + unsigned int active; // indication of LCD presence + unsigned int activated[BMI_SLOT_NUM]; // indication of LCD presence + + struct bmi_lcd *blcd[BMI_SLOT_NUM]; // BMI LCD structure - placeholder for multiple display types + struct bmi_device *bdev[BMI_SLOT_NUM]; // BMI device per slot + unsigned int interrupt[BMI_SLOT_NUM]; // input device interrupt handlers + char int_name[MAX_STRG]; // interrupt name + + struct input_dev *input_dev[BMI_TS_NUM]; // input device (touch screen and accelerometer) + struct timer_list timer[BMI_SLOT_NUM]; // touch timer + + int pen_down[BMI_SLOT_NUM]; + int scount[BMI_SLOT_NUM]; + + struct spi_device *spi[BMI_SLOT_NUM]; // touch screen device interface + struct semaphore sem[BMI_SLOT_NUM]; // spi semaphore + char rbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi read buffer + char wbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi write buffer + +#ifdef ACCELEROMETER + struct acc_dev acc[BMI_SLOT_NUM]; +#endif +}; + +static struct pbmi_lcd pbmi_lcd; // LCD device sructure + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_lcd_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_LCD_SHARP_320X240, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_lcd_tbl); + +/*printk(KERN_INFO "MDT: 0x%x\n", __mod_bmi_device_table);*/ + +int bmi_lcd_probe(struct bmi_device *bdev); +void bmi_lcd_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_lcd_driver = +{ + .name = "bmi_lcd", + .id_table = bmi_lcd_tbl, + .probe = bmi_lcd_probe, + .remove = bmi_lcd_remove, +}; + +//Accelerometer driver structure + + +/* + * I2C set up + */ + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define BMI_ACC_I2C_ADDRESS 0x17 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 // IOX input data register +#define IOX_OUTPUT_REG 0x1 // IOX output data register +#define IOX_POLARITY_REG 0x2 // IOX polarity data register +#define IOX_CONTROL 0x3 // IOX direction control register +#define IOX_B1 (0) // bit 0 - backlight control +#define IOX_A1_A2 (1) // bit 1 - backlight control +#define IOX_ACC_RST_N (2) // bit 2 - acceleromter reset +#define IOX_VSYNC_EN_N (3) // bit 3 - VSYNC output buffer enable +#define IOX_LCD_RST_N (4) // bit 4 - LCD reset +#define IOX_SERDES_PD_N (5) // bit 5 - SERDES power down +#define IOX_X_INT (6) // bit 6 - accelerometer interrupt +#define IOX_Y_INT (7) // bit 7 - accelerometer interrupt + + // I2C ACC register addresses - OKI +#define ACC_PAGESEL 0x1E // device ready status + // page 0 +#define ACC_DVRST 0x01 // device reset + #define ACC_DVRST_RST 0x3C // device reset + #define ACC_DVRST_EN 0xC3 // device enable +#define ACC_PDWN 0x02 // osc power down + #define ACC_PWDN_RST 0x01 // device reset + #define ACC_PWDN_EN 0x00 // device enable +#define ACC_CTRL0 0x03 // control 0 + #define ACC_CTRL0_CTSTR 0x40 // control 0 - temp sensor + #define ACC_CTRL0_CGSTRNC 0x08 // control 0 - 3-axis/no tilt + #define ACC_CTRL0_CGSTRC 0x04 // control 0 - 3-axis/tilt + #define ACC_CTRL0_CGAUTO 0x01 // control 0 - auto +#define ACC_MODE0 0x05 // control 0 + #define ACC_MODE0_PDOFF 0x80 // mode 0 - disable auto power down + #define ACC_MODE0_RVOFF 0x40 // mode 0 - disable temp compensation + #define ACC_MODE0_TMPOFF 0x20 // mode 0 - disable temp measurement + #define ACC_MODE0_AGCON 0x10 // mode 0 - enable auto mode pitch and roll + #define ACC_MODE0_MAUTO 0x04 // mode 0 - enable auto termination + #define ACC_MODE0_GDET00 0x00 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET01 0x01 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET10 0x02 // mode 0 - g detection threshold - see ML8953 data sheet +#define ACC_MODE1 0x06 // mode 1 + #define ACC_MODE1_MOFF 0x20 // mode 1 - disable 3-axis continuous mode + #define ACC_MODE1_ZAXIS 0x03 // mode 1 - Z axis + #define ACC_MODE1_YAXIS 0x02 // mode 1 - Y axis + #define ACC_MODE1_XAXIS 0x01 // mode 1 - X axis + #define ACC_MODE1_RAXIS 0x00 // mode 1 - Reference axis +#define ACC_INTRQ 0x07 // interrupt request (1 = request) +#define ACC_INTMSK 0x08 // interrupt mask (1 = masked) + #define ACC_INT_TREQ 0x20 // interrupt - temperature + #define ACC_INT_GREQ 0x08 // interrupt - acceleration/no tilt + #define ACC_INT_GCREQ 0x04 // interrupt - acceleration/tilt + #define ACC_INT_GAREQ 0x01 // interrupt - automatic +#define ACC_TMDL 0x09 // timer LSB = (1/6.2 MHz) x 2048 x TMD +#define ACC_TMDH 0x0A // timer MSB +#define ACC_CFG 0x0C // configuration + #define ACC_CFG_REGMD 0x80 // address auto-increment + #define ACC_CFG_SPI3M_3 0x40 // spi mode = 3-wire + #define ACC_CFG_SPI3M_4 0x00 // spi mode = 4-wire + #define ACC_CFG_SDOCFG_T 0x10 // sdo mode = totem-pole + #define ACC_CFG_SDOCFG_OC 0x00 // sdo mode = open-drain + #define ACC_CFG_INT1EN_G 0x08 // interrupt 1 mode = g only + #define ACC_CFG_INT1EN_ALL 0x00 // interrupt 1 mode = all + #define ACC_CFG_INTLVL 0x04 // interrupt level mode + #define ACC_CFG_INT1CFG_T 0x02 // interrupt 1 mode = totem-pole + #define ACC_CFG_INT1CFG_OC 0x00 // interrupt 1 mode = open-drain + #define ACC_CFG_INT0CFG_T 0x01 // interrupt 0 mode = totem-pole + #define ACC_CFG_INT0CFG_OC 0x00 // interrupt 0 mode = open-drain +#define ACC_INTOTM 0x0D // interrupt output conditions +#define ACC_GAAVE 0x0E // Data averaging - automatic mode +#define ACC_GNAVE 0x0F // Data averaging - normal mode +#define ACC_GDTCT0L 0x11 // threshold 0 LSB +#define ACC_GDTCT0H 0x12 // threshold 0 MSB +#define ACC_GDTCT1L 0x13 // threshold 1 LSB +#define ACC_GDTCT1H 0x14 // threshold 1 MSB +#define ACC_CPURDY 0x15 // device ready status (ready = 0x01) + // page 1 +#define ACC_STATUS 0x01 // measurment status + #define ACC_STATUS_ASTS 0x02 // acceleration measurement - automatic modes + #define ACC_STATUS_STS 0x01 // acceleration measurement - non-automatic modes +#define ACC_GAXL 0x02 // g vector +#define ACC_GAXH 0x03 // g vector +#define ACC_GAYL 0x04 // g vector +#define ACC_GAYH 0x05 // g vector +#define ACC_GAZL 0x06 // g vector +#define ACC_GAZH 0x07 // g vector +#define ACC_GASVL 0x08 // g vector +#define ACC_GASVH 0x09 // g vector +#define ACC_GNXL 0x0A // g vector +#define ACC_GNXH 0x0B // g vector +#define ACC_GNYL 0x0C // g vector +#define ACC_GNYH 0x0D // g vector +#define ACC_GNZL 0x0E // g vector +#define ACC_GNZH 0x0F // g vector +#define ACC_GNSVL 0x10 // g vector +#define ACC_GNSVH 0x11 // g vector +#define ACC_PITCHL 0x12 // pitch +#define ACC_PITCHH 0x13 // pitch +#define ACC_ROLLL 0x14 // roll +#define ACC_ROLLH 0x15 // roll +#define ACC_TEMPL 0x19 // temperature +#define ACC_TEMPH 0x1A // temperature + + // read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + int retries = 0; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + while (retries < 5) + { + ret = i2c_transfer (adap, rmsg, num_msgs); + if (ret == 2) + break; + else + retries++; + } + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + // write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +#if defined ACCELEROMETER + // read byte from I2C acceleromter +static int ReadByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + int retries = 0; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_ACC_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_ACC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + while (retries < 5) + { + ret = i2c_transfer (adap, rmsg, num_msgs); + if (ret == 2) + break; + else + retries++; + mdelay(1); + } + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +static int ReadByteLock_ACC(struct pbmi_lcd *priv, unsigned char offset, unsigned char *data, int slot) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + int retries = 0; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_ACC_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_ACC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + while (retries < 5) + { + ret = i2c_transfer (adap, rmsg, num_msgs); + if (ret == 2) + break; + else + retries++; + mdelay(1); + } + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + // write byte to I2C accelerometer +static int WriteByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_ACC_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_ACC_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} +#endif // ACCELEROMETER + +/* + * SPI functions + */ + + // TSC2046 touch screen controller command register bit definitons +#define SPI_START 0x80 // command start +#define SPI_AT0 0x00 // read temperature - not supported +#define SPI_AY 0x10 // read Y +#define SPI_ABAT 0x20 // read battery - not supported +#define SPI_AZ1 0x30 // read Z1 +#define SPI_AZ2 0x40 // read Z2 +#define SPI_AX 0x50 // read X +#define SPI_AAUX 0x60 // read AUX - not supported +#define SPI_AT1 0x70 // read temperature - not supported +#define SPI_MODE_12 0x00 // 12-bit mode - Preferred +#define SPI_MODE_8 0x08 // 8-bit mode +#define SPI_MODE_DFR 0x00 // differential mode - Preferred +#define SPI_MODE_SER 0x04 // single ended mode +#define SPI_PD 0x00 // power down - PENIRQ enabled +#define SPI_ADC 0x01 // ADC enabled +#define SPI_REF 0x02 // Vref enabled - unused +#define SPI_REF_ADC 0x03 // Vref & ADC enabled - unused + + // spi access +static int spi_rw(struct spi_device *spi, u8 * buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + + spi_message_add_tail(&t, &m); + if (spi_sync(spi, &m) != 0 || m.status != 0) + return -1; + + return m.actual_length; +} + + // spi write register +static ssize_t spi_lcd_write_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + up(&priv->sem[slot]); + + return res; +} + + // spi read register +static ssize_t spi_lcd_read_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + memset(priv->rbuf[slot], 0, BUF_MAX_SIZE); + buf[0] = priv->wbuf[slot][2]; + buf[1] = priv->wbuf[slot][1]; + + up(&priv->sem[slot]); + + return res; +} + +/* + * BMI functions + */ + +static irqreturn_t module_irq_handler(int irq, void *dummy); +void bmi_lcd_config(struct bmi_lcd *lcd, int disp); + + // probe +int bmi_lcd_probe(struct bmi_device *bdev) +{ +#if defined ACCELEROMETER + unsigned char acc_data[1]; +#endif // ACCELEROMETER + unsigned char iox_data[1]; + int slot = bdev->info->slot; + struct i2c_adapter *adap; + struct bmi_lcd *lcd; + char buf[4]; + /*int first_time = 1;*/ + + printk(KERN_INFO "bmi_lcd.c: probe slot %d\n", slot); + + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + /*bmi_slot_power_off(2);*/ + pbmi_lcd.bdev[0] = bdev; + /*bdev = pbmi_lcd.bdev[2]; + slot = 2; + first_time = 0;*/ + return 0; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + /*bmi_slot_power_off(3);*/ + pbmi_lcd.bdev[1] = bdev; + /*bdev = pbmi_lcd.bdev[3]; + slot = 3; + first_time = 0;*/ + return 0; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + /*bmi_slot_power_off(0);*/ + pbmi_lcd.bdev[2] = bdev; + /*bdev = pbmi_lcd.bdev[0]; + slot = 0; + first_time = 0;*/ + return 0; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + /*bmi_slot_power_off(1);*/ + pbmi_lcd.bdev[3] = bdev; + /*bdev = pbmi_lcd.bdev[1]; + slot = 1; + first_time = 0;*/ + return 0; + } + break; + } + + adap = &bdev->adap; + bmi_slot_power_on(slot); + + mdelay(500); + + // configure IOX + // [7:6]=interrupts, [5]=SER_PD*, [4]=LCD_RST*, [3]=VSYNC_OE*, [2]=ACC_RST*, [1:0]=backlight + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFF)) // normal - no accelerometer interrupts + return -ENODEV; + + // normal operation - no accelerometer interrupts + if(WriteByte_IOX(adap, IOX_CONTROL, 0x00)) // IOX[7:0]=OUT + return -ENODEV; + + // clear interrupts + if(ReadByte_IOX(adap, IOX_INPUT_REG, iox_data)) + return -ENODEV; + + printk(KERN_INFO "bmi_lcd.c: probe slot %d iox data = %x\n", slot, *iox_data); + +#if defined ACCELEROMETER + // accelerometer + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x\n", slot, bdev->epraw.revision_msb); + + // check for PCB revision >= 1.2 + if(bdev->epraw.revision_msb >= 0x12) { + + // normal IOX operation - accelerometer interrupts + if(WriteByte_IOX(adap, IOX_CONTROL, 0xC0)) // IOX[7:6]=IN, IOX[5:0]=OUT + return -ENODEV; + + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFB)) // reset OKI accelerometer + return -ENODEV; + + mdelay(2); + + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFF)) // enable OKI accelerometer + return -ENODEV; + + mdelay(2); + + // write PAGESEL + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return -ENODEV; + + // read device to verify existance + if(ReadByte_ACC(adap, ACC_CPURDY, acc_data)) + return -ENODEV; + + // set TMD = 0x300 (~250 ms) + *acc_data = 0x5; + if(WriteByte_ACC(adap, ACC_TMDH, *acc_data)) + return -ENODEV; + + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_TMDL, *acc_data)) + return -ENODEV; + + // set INTOTM + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_INTOTM, *acc_data)) + return -ENODEV; + + // set GxAVE + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_GAAVE, *acc_data)) + return -ENODEV; + + // set GDTCT[01] + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT0L, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT0H, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT1L, *acc_data)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByte_ACC(adap, ACC_GDTCT1H, *acc_data)) + return -ENODEV; + + // set MODE0 + *acc_data = ACC_MODE0_PDOFF | ACC_MODE0_TMPOFF | ACC_MODE0_AGCON | ACC_MODE0_MAUTO | ACC_MODE0_GDET10; + if(WriteByte_ACC(adap, ACC_MODE0, *acc_data)) + return -ENODEV; + + // set CFG + *acc_data = ACC_CFG_REGMD | ACC_CFG_INTLVL; + if(WriteByte_ACC(adap, ACC_CFG, *acc_data)) + return -ENODEV; + + // set INTMSK + *acc_data = 0xFE; + if(WriteByte_ACC(adap, ACC_INTMSK, *acc_data)) + return -ENODEV; + + // set CTRL0 + *acc_data = ACC_CTRL0_CGAUTO; + if(WriteByte_ACC(adap, ACC_CTRL0, *acc_data)) + return -ENODEV; + + // write PAGESEL + *acc_data = 0x1; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return -ENODEV; + + acc_probe(&pbmi_lcd.acc[slot], slot); + + } else { + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x (accelerometer not supported)\n", slot, bdev->epraw.revision_msb); + } +#endif // ACCELEROMETER + + // reset serial link (master) + if((slot == 0) || (slot == 2)) { + bmi_lcd_inactive(0); + } else { + bmi_lcd_inactive(1); + } + + // configure GPIO + // turn LED's on + bmi_set_module_gpio_data(slot, 3, 0); // Red LED=ON + bmi_set_module_gpio_data(slot, 2, 0); // Green LED=ON + + // assert reset + bmi_set_module_gpio_data(slot, 1, 0); // RST=0 + + // set GPIO direction + bmi_set_module_gpio_dir(slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 0, BMI_GPIO_IN); // real-time pen int state + + mdelay(200); + + // turn LED's off + bmi_set_module_gpio_data(slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data(slot, 2, 1); // Green LED=OFF + + // deassert reset (module) + bmi_set_module_gpio_data(slot, 1, 1); // RST=1 + + mdelay(500); + + // unreset serial link (master) + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_active(0, 0x0, LCD_MODE_I80); + } else { + mdelay(2); + bmi_lcd_active(1, 0x0, LCD_MODE_I80); + } + + + // set up bdev/pbmi_lcd pointers + bmi_device_set_drvdata(bdev, &pbmi_lcd); + pbmi_lcd.bdev[slot] = bdev; + + // spi set-up + if (bmi_device_spi_setup(bdev, 2000000, SPI_MODE_2, 32)) { + printk(KERN_ERR "bmi_lcd.c: Unable to setup spi%d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + bmi_slot_power_off(slot); + return -EFAULT; + } + + bmi_slot_spi_enable(slot); + pbmi_lcd.spi[slot] = bmi_device_get_spi(bdev); + + + // check spi access and enable touch screen + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + if(spi_lcd_write_reg(&pbmi_lcd, buf, 1, slot) != 1) { + printk(KERN_WARNING "bmi_lcd.c: Unable set-up spi for bmi_lcd %d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_spi_disable(slot); + bmi_slot_power_off(slot); + return -EFAULT; + } + + + // complete pbmi_lcd set-up + pbmi_lcd.lcd_cnt++; + pbmi_lcd.active = 1; + pbmi_lcd.activated[slot] = 1; + + + mdelay(100); + + lcd = pbmi_lcd.blcd[slot]; + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_config(lcd, 0); + mdelay(2); + } else { + mdelay(2); + bmi_lcd_config(lcd, 1); + mdelay(2); + } + + + // request input event interrupt handler + pbmi_lcd.interrupt[0] = M1_IRQ; + pbmi_lcd.interrupt[1] = M2_IRQ; + pbmi_lcd.interrupt[2] = M3_IRQ; + pbmi_lcd.interrupt[3] = M4_IRQ; + snprintf(pbmi_lcd.int_name, sizeof(pbmi_lcd.int_name), "bmi_lcd%d", slot); + if (request_irq(pbmi_lcd.interrupt[slot], &module_irq_handler, 0, pbmi_lcd.int_name, &pbmi_lcd)) { + printk( KERN_ERR "bmi_lcd.c: Can't allocate irq %d or find lcd in slot %d\n", pbmi_lcd.interrupt[slot], slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_power_off(slot); + return -EBUSY; + } + + // check GPIO status + printk(KERN_INFO "bmi_lcd.c: slot %d gpio = %x\n", slot, bmi_read_gpio_data_reg(slot)); + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return 0; +} + +extern struct delayed_work bmilcd_work0; +extern struct delayed_work bmilcd_work1; +extern struct delayed_work bmilcd_work2; +extern struct delayed_work bmilcd_work3; + + // remove +void bmi_lcd_remove(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + + if(pbmi_lcd.activated[slot] == 0) + return; + + switch(slot) { + case 0: + cancel_delayed_work(&bmilcd_work0); + break; + case 1: + cancel_delayed_work(&bmilcd_work1); + break; + case 2: + cancel_delayed_work(&bmilcd_work2); + break; + case 3: + cancel_delayed_work(&bmilcd_work3); + break; + } + + free_irq(pbmi_lcd.interrupt[slot], &pbmi_lcd); + + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); + + // bmi/spi clean-up + bmi_device_spi_cleanup(bdev); + pbmi_lcd.spi[slot] = NULL; + bmi_slot_spi_disable(slot); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (&bdev[slot], 0); + + // deactivate + pbmi_lcd.activated[slot] = 0; + pbmi_lcd.bdev[slot] = 0; + pbmi_lcd.lcd_cnt--; + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[2] == 0)) { + bmi_lcd_inactive(0); // disable serializer + } + + if((pbmi_lcd.activated[1] == 0) && (pbmi_lcd.activated[3] == 0)) { + bmi_lcd_inactive(1); // disable serializer + } + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[1] == 0) && + (pbmi_lcd.activated[2] == 0) && (pbmi_lcd.activated[3] == 0)) { + pbmi_lcd.active = -1; + } + + // enable LCD on opposite side + switch(slot) { + case 0: + if(pbmi_lcd.bdev[2] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[2]); + break; + case 1: + if(pbmi_lcd.bdev[3] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[3]); + break; + case 2: + if(pbmi_lcd.bdev[0] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[0]); + break; + case 3: + if(pbmi_lcd.bdev[1] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[1]); + break; + } + +#ifdef ACCELEROMETER + acc_remove(&pbmi_lcd.acc[slot], slot); +#endif + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return; +} + +/* + * Input interrupt handler and support routines + */ + +static void update_pen_state(void *arg, int slot, int x, int y, int pressure) +{ + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; + int sync = 0; + + if (pressure) + { + /*input_report_abs(pbmi_lcd->input_dev[slot], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_PRESSURE, pressure); + + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_PRESSURE, pressure);*/ + + if((slot == 0) || (slot == 2)) + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, pressure); + } + else + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, pressure); + } + + if (!pbmi_lcd->pen_down[slot]) + { + /*input_report_key(pbmi_lcd->input_dev[slot], BTN_TOUCH, 1); + input_report_key(pbmi_lcd->input_dev[BMI_TS_M1234], BTN_TOUCH, 1);*/ + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 1); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 1); + } + + } + sync = 1; + } + else if (pbmi_lcd->pen_down[slot]) + { + /*input_report_key(pbmi_lcd->input_dev[slot], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_PRESSURE, 0); + + input_report_key(pbmi_lcd->input_dev[BMI_TS_M1234], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_PRESSURE, 0); */ + + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, 0); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, 0); + } + sync = 1; + } + + if (sync) + { + /*input_sync(pbmi_lcd->input_dev[slot]); + input_sync(pbmi_lcd->input_dev[BMI_TS_M1234]);*/ + if((slot == 0) || (slot == 2)) + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M13]); + } + else + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M24]); + } + } + pbmi_lcd->pen_down[slot] = pressure ? 1 : 0; + +} + + +void bmilcd_input_work(void *arg, int slot); + +void bmilcd_input_work0(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 0); +} + +void bmilcd_input_work1(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 1); +} + +void bmilcd_input_work2(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 2); +} + +void bmilcd_input_work3(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 3); +} + +DECLARE_DELAYED_WORK(bmilcd_work0, bmilcd_input_work0); +DECLARE_DELAYED_WORK(bmilcd_work1, bmilcd_input_work1); +DECLARE_DELAYED_WORK(bmilcd_work2, bmilcd_input_work2); +DECLARE_DELAYED_WORK(bmilcd_work3, bmilcd_input_work3); + +// work handler +void bmilcd_input_work(void *arg, int slot) { + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; +#if defined ACCELEROMETER + struct i2c_adapter *adap = &pbmi_lcd->bdev[slot]->adap; + unsigned char acc_data[1]; + static int pitch = 0; + static int roll = 0; + static int gx = 0; + static int gy = 0; + +#endif // ACCELEROMETER + unsigned char buf[4]; + int x = 0; + int y = 0; + int z1 = 0; + int z2 = 0; + int pressure = 0; + int debounce; + int penirq; + +#if defined DEBUG + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d)\n", slot); +#endif + + if(pbmi_lcd->bdev[slot] == 0) { + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work called with no bdev active (slot %d)\n", slot); + return; + } + +#if defined ACCELEROMETER + if(pbmi_lcd->bdev[slot]->epraw.revision_msb >= 0x12) { + + // orientation + // read ROLL + if(ReadByte_ACC(adap, ACC_ROLLH, acc_data)) + return; + roll = (0x0000 | *acc_data) << 8; + + if(ReadByte_ACC(adap, ACC_ROLLL, acc_data)) + return; + roll = roll | *acc_data; + // read PITCH + if(ReadByte_ACC(adap, ACC_PITCHH, acc_data)) + return; + pitch = (0x0000 | *acc_data) << 8; + + if(ReadByte_ACC(adap, ACC_PITCHL, acc_data)) + return; + pitch = pitch | *acc_data; + + + + + if(ReadByte_ACC(adap, ACC_GAZH, acc_data)) + return; + pbmi_lcd->acc[slot].sample[0] = *acc_data; + + if(ReadByte_ACC(adap, ACC_GAZL, acc_data)) + return; + pbmi_lcd->acc[slot].sample[1] = *acc_data; + + if(ReadByte_ACC(adap, ACC_GAYH, acc_data)) + return; + pbmi_lcd->acc[slot].sample[2] = *acc_data; + gy = *acc_data << 8; + + if(ReadByte_ACC(adap, ACC_GAYL, acc_data)) + return; + pbmi_lcd->acc[slot].sample[3] = *acc_data; + gy = gy | *acc_data; + + if(ReadByte_ACC(adap, ACC_GAXH, acc_data)) + return; + pbmi_lcd->acc[slot].sample[4] = *acc_data; + gx = *acc_data << 8; + + if(ReadByte_ACC(adap, ACC_GAXL, acc_data)) + return; + pbmi_lcd->acc[slot].sample[5] = *acc_data; + gx = gx | *acc_data; + + //wake up any read's + pbmi_lcd->acc[slot].flag = 1; + wake_up_interruptible(&pbmi_lcd->acc[slot].wq); + + // read STATUS + if(ReadByte_ACC(adap, ACC_STATUS, acc_data)) + return; + + if((*acc_data & 0x1) == 0) { + + // write PAGESEL + *acc_data = 0x0; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return; + + // read INTRQ + if(ReadByte_ACC(adap, ACC_INTRQ, acc_data)) + return; + } + + // write PAGESEL + *acc_data = 0x1; + if(WriteByte_ACC(adap, ACC_PAGESEL, *acc_data)) + return; + + // report orientation + // printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d) pitch=0x%x, roll=0x%x, ABS_MISC=0x%x\n", + // slot, pitch, roll, pitch << 16 | roll); //pjg - debug + + input_report_abs(pbmi_lcd->input_dev[slot], ABS_MISC, (pitch << 16) | roll); + input_sync(pbmi_lcd->input_dev[slot]); + } +#endif // ACCELEROMETER + + + // read touch screen - X, Y, TOUCH, PRESSURE + + penirq = bmi_slot_status_irq_state(slot); + /*printk(KERN_INFO "bmi_lcd.c: IRQ Status %d (slot %d) %d\n", penirq, slot,msecs_to_jiffies(10));*/ + + if (pbmi_lcd->activated[slot] && penirq) + { + + for(debounce = 0; debounce < DEBOUNCE; debounce++) + { + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AY | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + y = (((buf[0] << 5) | buf[1] >> 3)) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AX | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + x = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ1 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z1 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ2 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z2 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + mdelay(1); + } + + if(x && y && z1 && z2) + pressure = (X_PLATE * x / 4096) * ((z2 / z1) - 1); + + x = 4096 - x; + y = 4096 - y; + + if (pressure < 70) + { + if (pbmi_lcd->scount) + update_pen_state(arg, slot, x, y, pressure); + else + { + pbmi_lcd->scount[slot]++; + /*update_pen_state(arg, slot, 0, 0, pressure);*/ + } + } + /* else + { + update_pen_state(arg, slot, 0, 0, pressure); + }*/ + + switch(slot) + { + case BMI_TS_M1: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + break; + case BMI_TS_M2: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + break; + case BMI_TS_M3: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + break; + case BMI_TS_M4: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + break; + } + /* printk(KERN_INFO "bmi_lcd.c: work scheduled on (slot %d)\n", slot); */ + /*buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot);*/ + } + + else + { + /*printk(KERN_INFO "bmi_lcd.c: Pen up on (slot %d)\n", slot);*/ + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot); + update_pen_state(arg,slot, 0, 0, 0); + enable_irq(pbmi_lcd->interrupt[slot]); + } + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + disable_irq(irq); + /*printk(KERN_INFO "bmi_lcd.c: Interupt on (slot %d)\n", irq);*/ + switch(irq) + { + case M1_IRQ: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M1] = 0; + break; + case M2_IRQ: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M2] = 0; + break; + case M3_IRQ: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M3] = 0; + break; + case M4_IRQ: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M4] = 0; + break; + } + return IRQ_HANDLED; +} + +/* + * control device operations + */ + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *filp) +{ + if(pbmi_lcd.open_flag) { + return - EBUSY; + } + pbmi_lcd.open_flag = 1; + filp->private_data = &pbmi_lcd; + return 0; +} + +// release +int cntl_release(struct inode *inode, struct file *filp) +{ + pbmi_lcd.open_flag = 0; + return 0; +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data[1]; + int slot = (__user arg) & 0xF; + int bl = ((__user arg) & 0x70) >> 4; + + // error if no lcd active. + if(pbmi_lcd.active == -1) + return -ENODEV; + + if(cmd != BMI_LCD_GETSTAT) { + + // error if slot invalid + if((slot < CPLD_M1) || (slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[slot]->adap; + } + + // ioctl's + switch (cmd) { + case BMI_LCD_RLEDOFF: + bmi_set_module_gpio_data(slot, 3, 1);// Red LED=OFF + break; + case BMI_LCD_RLEDON: + bmi_set_module_gpio_data(slot, 3, 0);// Red LED=ON + break; + case BMI_LCD_GLEDOFF: + bmi_set_module_gpio_data(slot, 2, 1);// Green LED=OFF + break; + case BMI_LCD_GLEDON: + bmi_set_module_gpio_data(slot, 2, 0);// Green LED=ON + break; + case BMI_LCD_VSYNC_DIS: // enable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_VSYNC_EN: // disable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_EN: // enable LCD component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_DIS: // disable LCD component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_EN: // enable Serializer component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_DIS: // disable Serializer component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SETRST: // overall module reset + bmi_set_module_gpio_data (slot, 1, 0); // RST=0 + break; + case BMI_LCD_CLRRST: // overall module enable + bmi_set_module_gpio_data (slot, 1, 1); // RST=1 + break; + case BMI_LCD_SET_BL: // set backlight brightness + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8) | bl; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_GETSTAT: + { + int *slot = ((int __user *) arg); + int read_data; + + *slot &= 0xF; + + // error if slot invalid + if((*slot < CPLD_M1) || (*slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[*slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[*slot]->adap; + + if(ReadByte_IOX(adap, IOX_INPUT_REG, iox_data)) + return -ENODEV; + + read_data = *iox_data | (bmi_read_gpio_data_reg(*slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + case BMI_LCD_ACTIVATE: //pjg fix/test + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + return -ENODEV; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + return -ENODEV; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + return -ENODEV; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + return -ENODEV; + } + break; + } + // activate + if((!pbmi_lcd.activated[slot]) && (pbmi_lcd.bdev[slot] != 0)) { + bmi_lcd_probe(pbmi_lcd.bdev[slot]); + } + break; + case BMI_LCD_DEACTIVATE: + if(pbmi_lcd.activated[slot]) { + disable_irq_nosync(pbmi_lcd.interrupt[slot]); + pbmi_lcd.activated[slot] = 0; + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8); + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + bmi_slot_power_off(slot); + } + break; + case BMI_LCD_SUSPEND: + printk(KERN_ERR "BMI_LCD_SUSPEND NOT IMPLEMENTED\n"); //pjg + break; + case BMI_LCD_RESUME: + printk(KERN_ERR "BMI_LCD_RESUME NOT IMPLEMENTED\n"); //pjg + break; + default: + return -ENOTTY; + } + return 0; +} + + // control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + + // BMI LCD fops +void bmi_lcd_config(struct bmi_lcd *lcd, int disp) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.config)) { + lcd->lcd_ops.config(disp); + } +} + +void bmi_lcd_reset(struct bmi_lcd *lcd, int slot) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.reset)) { + lcd->lcd_ops.reset(slot); + } +} + +int register_bmi_lcd(struct bmi_lcd *lcd, int slot) //pjg - placeholder for multiple LCD types +{ + if(!lcd) { + return -1; + } + if((slot < 0) || (slot > 3)) { + return -1; + } + if(pbmi_lcd.blcd[slot]) { + return -1; + } + else { + pbmi_lcd.blcd[slot] = lcd; + } + + if(lcd->lcd_ops.activate) { + lcd->lcd_ops.activate(lcd, slot); + } + + return 0; +} + +int unregister_bmi_lcd(struct bmi_lcd *lcd, int slot) //pjg - placeholder for multiple LCD types +{ + if (!lcd) { + return -1; + } + if ((slot < 0) || (slot > 3)) { + return -1; + } + if (pbmi_lcd.blcd[slot] != lcd) { + return -1; + } + else { + pbmi_lcd.blcd [slot] = 0; + lcd->lcd_ops.deactivate(lcd, slot); + } + return 0; +} + +static struct miscdevice cntl_dev = { + MISC_DYNAMIC_MINOR, + "bmi_lcd_control", + &cntl_fops +}; + +/* + * Module functions + */ + +char const input_name0[MAX_STRG] = "bmi_lcd_ts0"; +char const input_name1[MAX_STRG] = "bmi_lcd_ts1"; +char const input_name2[MAX_STRG] = "bmi_lcd_ts2"; +char const input_name3[MAX_STRG] = "bmi_lcd_ts3"; +char const input_name4[MAX_STRG] = "bmi_lcd_ts4"; +char const input_name5[MAX_STRG] = "bmi_lcd_ts5"; +char const input_name6[MAX_STRG] = "bmi_lcd_ts6"; + +static __init int bmi_lcd_init(void) +{ + int ts; + int rc = 0; + + // No lcd is active. + pbmi_lcd.active = -1; + pbmi_lcd.activated[0] = 0; + pbmi_lcd.activated[1] = 0; + pbmi_lcd.activated[2] = 0; + pbmi_lcd.activated[3] = 0; + + // set up control character device - bmi_lcd_control + rc = misc_register(&cntl_dev); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't allocate bmi_lcd_control device\n"); + return rc; + } + + // Allocate and Register input device. - bmi_lcd_ts[BMI_TS_M1:BMI_TS_M1234] + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) { + pbmi_lcd.input_dev[ts] = input_allocate_device(); + if(!pbmi_lcd.input_dev[ts]) { + printk(KERN_ERR "bmi_lcd_init: Can't allocate input_dev[ts]\n"); + return -ENOMEM; + } + + // set up input device + switch(ts) { + case BMI_TS_M1: + pbmi_lcd.input_dev[BMI_TS_M1]->name = input_name0; + pbmi_lcd.input_dev[BMI_TS_M1]->phys = input_name0; + break; + case BMI_TS_M2: + pbmi_lcd.input_dev[BMI_TS_M2]->name = input_name1; + pbmi_lcd.input_dev[BMI_TS_M2]->phys = input_name1; + break; + case BMI_TS_M3: + pbmi_lcd.input_dev[BMI_TS_M3]->name = input_name2; + pbmi_lcd.input_dev[BMI_TS_M3]->phys = input_name2; + break; + case BMI_TS_M4: + pbmi_lcd.input_dev[BMI_TS_M4]->name = input_name3; + pbmi_lcd.input_dev[BMI_TS_M4]->phys = input_name3; + break; + case BMI_TS_M13: + pbmi_lcd.input_dev[BMI_TS_M13]->name = input_name4; + pbmi_lcd.input_dev[BMI_TS_M13]->phys = input_name4; + break; + case BMI_TS_M24: + pbmi_lcd.input_dev[BMI_TS_M24]->name = input_name5; + pbmi_lcd.input_dev[BMI_TS_M24]->phys = input_name5; + break; + case BMI_TS_M1234: + pbmi_lcd.input_dev[BMI_TS_M1234]->name = input_name6; + pbmi_lcd.input_dev[BMI_TS_M1234]->phys = input_name6; + break; + } + pbmi_lcd.input_dev[ts]->id.bustype = BUS_BMI; + pbmi_lcd.input_dev[ts]->private = &pbmi_lcd; + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); + pbmi_lcd.input_dev[ts]->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_X)] |= BIT_MASK(ABS_X); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_Y)] |= BIT_MASK(ABS_Y); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_PRESSURE)] |= BIT_MASK(ABS_PRESSURE); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_X, BMI_LCD_MIN_XC, BMI_LCD_MAX_XC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_Y, BMI_LCD_MIN_YC, BMI_LCD_MAX_YC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_PRESSURE, 0, 1024, 0, 0); + + // register input device + if(input_register_device(pbmi_lcd.input_dev[ts])) { + int tts; + printk(KERN_ERR "bmi_lcd_init() - input_register_device failed.\n"); + + for(tts = BMI_TS_M1; tts < ts; tts++) + input_unregister_device(pbmi_lcd.input_dev[tts]); + + misc_deregister(&cntl_dev); + + return -ENODEV; + } + } + + pbmi_lcd.lcd_cnt = 0; + + // hardware specfic set-up + s320x240_bmi_lcd.interface = s320x240_lcd_interface; + s320x240_bmi_lcd_ops.config = (void(*)) &s320x240_config; + s320x240_bmi_lcd_ops.reset = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.suspend = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.resume = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_on = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_off = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.activate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.deactivate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd.lcd_ops = s320x240_bmi_lcd_ops; + pbmi_lcd.blcd[0] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[1] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[2] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[3] = &s320x240_bmi_lcd; + + sema_init(&pbmi_lcd.sem[0], 1); + sema_init(&pbmi_lcd.sem[1], 1); + sema_init(&pbmi_lcd.sem[2], 1); + sema_init(&pbmi_lcd.sem[3], 1); + + + acc_init(); + + // register with BMI + rc = bmi_register_driver(&bmi_lcd_driver); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't register bmi_lcd_driver\n"); + + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + misc_deregister(&cntl_dev); + + return rc; + } + + printk("bmi_lcd.c: BMI_LCD Driver v%s \n", BMILCD_VERSION); + + return 0; +} + + +static void __exit bmi_lcd_clean(void) +{ + int ts; + + // remove input devices + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + // remove control device + misc_deregister(&cntl_dev); + + // remove bmi driver + bmi_unregister_driver(&bmi_lcd_driver); + acc_clean(); + return; +} + +module_init(bmi_lcd_init); +module_exit(bmi_lcd_clean); + +// Exported symbols +EXPORT_SYMBOL(register_bmi_lcd); +EXPORT_SYMBOL(unregister_bmi_lcd); + + +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BMI lcd device driver"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_control"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_ts"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_acc"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/drivers/bmi/pims/lcd/bmi_lcd_mi.c @@ -0,0 +1,1855 @@ +/* + * bmi_lcd.c + * + * BMI LCD device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acc.h" + + +#define DEBUG +#undef DEBUG + +#define BMILCD_VERSION "1.2" // driver version +#define BUF_MAX_SIZE 0x20 // spi buffer size +#define WORK_DELAY (1) // interrupt work handler delay +#define DEBOUNCE 10 // touch screen debounce +#define X_PLATE 400 // touch screen X plate resistance //pjg - This is not the correct value +#define BMI_SLOT_NUM (4) // number of BMI slots +#define MAX_STRG (40) // Max string buffer size + +#define VSYNC_DISABLE 0x0 +#define VSYNC_ENABLE 0x1 + + // lcd +struct lcd_interface { + char lcd_type[MAX_STRG]; // text description of LCD type + u8 suspended; // power management state + u8 rotation; // screen rotation + u8 disp; // display number (DISP0 or DISP1) + u8 addr_mode; // display addressing mode + u8 vsync_mode; // VSYNC signal enable (VSYNC_ENABLE | VSYNC_DISABLE) + u8 bus_if_type; // bus type (XY | FullWoBE | FullWithBE) + ipu_adc_sig_cfg_t adc_sig; // IPU ADC set-up parameters + ipu_di_signal_cfg_t di_sig; // IPU DI set-up parameters +}; + +static struct lcd_interface s320x240_lcd_interface = { + .lcd_type = "MXCFB_SHARP_320X240", + .suspended = 0, + .rotation = IPU_ROTATE_NONE, + .disp = DISP0, + .vsync_mode = VSYNC_DISABLE, + .bus_if_type = XY, + .adc_sig = { 0, 0, 0, 0, 0, 0, 0, 0, IPU_ADC_BURST_WCS, IPU_ADC_IFC_MODE_SYS80_TYPE2, + 16, 0, 0, IPU_ADC_SER_NO_RW }, + .di_sig = { 0,0,0,0,0,0,0,0 }, //pjg - reserved for multiple LCD driver +}; + +extern void s320x240_config(int disp); +extern void s320x240_disp_off(int disp); +extern void s320x240_disp_on(int disp); + + +struct bmi_lcd; + +struct bmi_lcd_ops { + void *(*config) (int disp); // LCD configuration/initialization + void *(*reset) (int slot); // LCD reset + int *(*suspend) (struct bmi_lcd *blcd); // power management + int *(*resume) (struct bmi_lcd *blcd); // power management + int *(*disp_on) (int disp); // display on + int *(*disp_off) (int disp); // display off + int (*activate) (struct bmi_lcd *lcd, int slot); // enable LCD backlight, touchscreen, accelerometer, ... + int (*deactivate) (struct bmi_lcd *lcd, int slot); // disable LCD backlight, touchscreen, accelerometer, ... +}; + +struct bmi_lcd_ops s320x240_bmi_lcd_ops; + +struct bmi_lcd { + struct lcd_interface interface; // pointer to this struct is returned by config() + struct bmi_lcd_ops lcd_ops; // function pointers +}; + +static struct bmi_lcd s320x240_bmi_lcd; + +int register_bmi_lcd(struct bmi_lcd *blcd, int slot); +int unregister_bmi_lcd(struct bmi_lcd *blcd, int slot); + + // private device structure +struct pbmi_lcd +{ + int open_flag; // force single open + unsigned int lcd_cnt; // number of LCD's present + unsigned int active; // indication of LCD presence + unsigned int activated[BMI_SLOT_NUM]; // indication of LCD presence + + struct bmi_lcd *blcd[BMI_SLOT_NUM]; // BMI LCD structure - placeholder for multiple display types + struct bmi_device *bdev[BMI_SLOT_NUM]; // BMI device per slot + unsigned int interrupt[BMI_SLOT_NUM]; // input device interrupt handlers + char int_name[MAX_STRG]; // interrupt name + + struct input_dev *input_dev[BMI_TS_NUM]; // input device (touch screen and accelerometer) + struct timer_list timer[BMI_SLOT_NUM]; // touch timer + + int pen_down[BMI_SLOT_NUM]; + int scount[BMI_SLOT_NUM]; + + struct semaphore i2c_sem[BMI_SLOT_NUM]; + + struct spi_device *spi[BMI_SLOT_NUM]; // touch screen device interface + struct semaphore sem[BMI_SLOT_NUM]; // spi semaphore + char rbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi read buffer + char wbuf[BMI_SLOT_NUM][BUF_MAX_SIZE]; // spi write buffer + + struct acc_dev acc[BMI_SLOT_NUM]; + +}; + +static struct pbmi_lcd pbmi_lcd; // LCD device sructure + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_lcd_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_LCD_SHARP_320X240, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_lcd_tbl); + +/*printk(KERN_INFO "MDT: 0x%x\n", __mod_bmi_device_table);*/ + +int bmi_lcd_probe(struct bmi_device *bdev); +void bmi_lcd_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_lcd_driver = +{ + .name = "bmi_lcd", + .id_table = bmi_lcd_tbl, + .probe = bmi_lcd_probe, + .remove = bmi_lcd_remove, +}; + +//Accelerometer driver structure + + +/* + * I2C set up + */ + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define BMI_ACC_I2C_ADDRESS 0x17 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 // IOX input data register +#define IOX_OUTPUT_REG 0x1 // IOX output data register +#define IOX_POLARITY_REG 0x2 // IOX polarity data register +#define IOX_CONTROL 0x3 // IOX direction control register +#define IOX_B1 (0) // bit 0 - backlight control +#define IOX_A1_A2 (1) // bit 1 - backlight control +#define IOX_ACC_RST_N (2) // bit 2 - acceleromter reset +#define IOX_VSYNC_EN_N (3) // bit 3 - VSYNC output buffer enable +#define IOX_LCD_RST_N (4) // bit 4 - LCD reset +#define IOX_SERDES_PD_N (5) // bit 5 - SERDES power down +#define IOX_X_INT (6) // bit 6 - accelerometer interrupt +#define IOX_Y_INT (7) // bit 7 - accelerometer interrupt + + // I2C ACC register addresses - OKI +#define ACC_PAGESEL 0x1E // device ready status + // page 0 +#define ACC_DVRST 0x01 // device reset + #define ACC_DVRST_RST 0x3C // device reset + #define ACC_DVRST_EN 0xC3 // device enable +#define ACC_PDWN 0x02 // osc power down + #define ACC_PWDN_RST 0x01 // device reset + #define ACC_PWDN_EN 0x00 // device enable +#define ACC_CTRL0 0x03 // control 0 + #define ACC_CTRL0_CTSTR 0x40 // control 0 - temp sensor + #define ACC_CTRL0_CGSTRNC 0x08 // control 0 - 3-axis/no tilt + #define ACC_CTRL0_CGSTRC 0x04 // control 0 - 3-axis/tilt + #define ACC_CTRL0_CGAUTO 0x01 // control 0 - auto +#define ACC_MODE0 0x05 // control 0 + #define ACC_MODE0_PDOFF 0x80 // mode 0 - disable auto power down + #define ACC_MODE0_RVOFF 0x40 // mode 0 - disable temp compensation + #define ACC_MODE0_TMPOFF 0x20 // mode 0 - disable temp measurement + #define ACC_MODE0_AGCON 0x10 // mode 0 - enable auto mode pitch and roll + #define ACC_MODE0_MAUTO 0x04 // mode 0 - enable auto termination + #define ACC_MODE0_GDET00 0x00 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET01 0x01 // mode 0 - g detection threshold - see ML8953 data sheet + #define ACC_MODE0_GDET10 0x02 // mode 0 - g detection threshold - see ML8953 data sheet +#define ACC_MODE1 0x06 // mode 1 + #define ACC_MODE1_MOFF 0x20 // mode 1 - disable 3-axis continuous mode + #define ACC_MODE1_ZAXIS 0x03 // mode 1 - Z axis + #define ACC_MODE1_YAXIS 0x02 // mode 1 - Y axis + #define ACC_MODE1_XAXIS 0x01 // mode 1 - X axis + #define ACC_MODE1_RAXIS 0x00 // mode 1 - Reference axis +#define ACC_INTRQ 0x07 // interrupt request (1 = request) +#define ACC_INTMSK 0x08 // interrupt mask (1 = masked) + #define ACC_INT_TREQ 0x20 // interrupt - temperature + #define ACC_INT_GREQ 0x08 // interrupt - acceleration/no tilt + #define ACC_INT_GCREQ 0x04 // interrupt - acceleration/tilt + #define ACC_INT_GAREQ 0x01 // interrupt - automatic +#define ACC_TMDL 0x09 // timer LSB = (1/6.2 MHz) x 2048 x TMD +#define ACC_TMDH 0x0A // timer MSB +#define ACC_CFG 0x0C // configuration + #define ACC_CFG_REGMD 0x80 // address auto-increment + #define ACC_CFG_SPI3M_3 0x40 // spi mode = 3-wire + #define ACC_CFG_SPI3M_4 0x00 // spi mode = 4-wire + #define ACC_CFG_SDOCFG_T 0x10 // sdo mode = totem-pole + #define ACC_CFG_SDOCFG_OC 0x00 // sdo mode = open-drain + #define ACC_CFG_INT1EN_G 0x08 // interrupt 1 mode = g only + #define ACC_CFG_INT1EN_ALL 0x00 // interrupt 1 mode = all + #define ACC_CFG_INTLVL 0x04 // interrupt level mode + #define ACC_CFG_INT1CFG_T 0x02 // interrupt 1 mode = totem-pole + #define ACC_CFG_INT1CFG_OC 0x00 // interrupt 1 mode = open-drain + #define ACC_CFG_INT0CFG_T 0x01 // interrupt 0 mode = totem-pole + #define ACC_CFG_INT0CFG_OC 0x00 // interrupt 0 mode = open-drain +#define ACC_INTOTM 0x0D // interrupt output conditions +#define ACC_GAAVE 0x0E // Data averaging - automatic mode +#define ACC_GNAVE 0x0F // Data averaging - normal mode +#define ACC_GDTCT0L 0x11 // threshold 0 LSB +#define ACC_GDTCT0H 0x12 // threshold 0 MSB +#define ACC_GDTCT1L 0x13 // threshold 1 LSB +#define ACC_GDTCT1H 0x14 // threshold 1 MSB +#define ACC_CPURDY 0x15 // device ready status (ready = 0x01) + // page 1 +#define ACC_STATUS 0x01 // measurment status + #define ACC_STATUS_ASTS 0x02 // acceleration measurement - automatic modes + #define ACC_STATUS_STS 0x01 // acceleration measurement - non-automatic modes +#define ACC_GAXL 0x02 // g vector +#define ACC_GAXH 0x03 // g vector +#define ACC_GAYL 0x04 // g vector +#define ACC_GAYH 0x05 // g vector +#define ACC_GAZL 0x06 // g vector +#define ACC_GAZH 0x07 // g vector +#define ACC_GASVL 0x08 // g vector +#define ACC_GASVH 0x09 // g vector +#define ACC_GNXL 0x0A // g vector +#define ACC_GNXH 0x0B // g vector +#define ACC_GNYL 0x0C // g vector +#define ACC_GNYH 0x0D // g vector +#define ACC_GNZL 0x0E // g vector +#define ACC_GNZH 0x0F // g vector +#define ACC_GNSVL 0x10 // g vector +#define ACC_GNSVH 0x11 // g vector +#define ACC_PITCHL 0x12 // pitch +#define ACC_PITCHH 0x13 // pitch +#define ACC_ROLLL 0x14 // roll +#define ACC_ROLLH 0x15 // roll +#define ACC_TEMPL 0x19 // temperature +#define ACC_TEMPH 0x1A // temperature + + // read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +static int ReadByteLock_IOX(struct pbmi_lcd *priv, unsigned char offset, unsigned char *data, int slot) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + struct i2c_adapter *adap; + + /* Read Byte with Pointer */ + + adap = &priv->bdev[slot]->adap; + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + down(&priv->i2c_sem[slot]); + ret = i2c_transfer (adap, rmsg, num_msgs); + up(&priv->i2c_sem[slot]); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + + // write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +static int WriteByteLock_IOX(struct pbmi_lcd *priv, unsigned char offset, unsigned char data, int slot) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + struct i2c_adapter *adap; + /* Write Byte with Pointer */ + + adap = &priv->bdev[slot]->adap; + if (adap == NULL) + { + printk(KERN_INFO "WriteByteLock_IOX adap NULL\n"); + return -1; + } + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + down(&priv->i2c_sem[slot]); + printk(KERN_INFO "WriteByteLock_IOX attempting I2C xfer\n"); + ret = i2c_transfer (adap, wmsg, num_msgs); + up(&priv->i2c_sem[slot]); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByteLock_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + + // read byte from I2C acceleromter +static int ReadByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + int retries = 0; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_ACC_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_ACC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + while (retries < 5) + { + ret = i2c_transfer (adap, rmsg, num_msgs); + if (ret == 2) + break; + else + retries++; + mdelay(1); + } + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +static int ReadByteLock_ACC(struct pbmi_lcd *priv, unsigned char offset, unsigned char *data, int slot) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + struct i2c_adapter *adap; + + /* Read Byte with Pointer */ + + adap = &priv->bdev[slot]->adap; + + rmsg[0].addr = BMI_ACC_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_ACC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + + down(&priv->i2c_sem[slot]); + ret = i2c_transfer (adap, rmsg, num_msgs); + up(&priv->i2c_sem[slot]); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByteLock_ACC() - i2c_transfer() failed.0x%x\n",-ret); + ret = -1; + } + return ret; +} + + // write byte to I2C accelerometer +static int WriteByte_ACC(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_ACC_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_ACC_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +static int WriteByteLock_ACC(struct pbmi_lcd *priv, unsigned char offset, unsigned char data, int slot) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + struct i2c_adapter *adap; + /* Write Byte with Pointer */ + + adap = &priv->bdev[slot]->adap; + + wmsg[0].addr = BMI_ACC_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_ACC_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + down(&priv->i2c_sem[slot]); + ret = i2c_transfer (adap, wmsg, num_msgs); + up(&priv->i2c_sem[slot]); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByteLock_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +/* + * SPI functions + */ + + // TSC2046 touch screen controller command register bit definitons +#define SPI_START 0x80 // command start +#define SPI_AT0 0x00 // read temperature - not supported +#define SPI_AY 0x10 // read Y +#define SPI_ABAT 0x20 // read battery - not supported +#define SPI_AZ1 0x30 // read Z1 +#define SPI_AZ2 0x40 // read Z2 +#define SPI_AX 0x50 // read X +#define SPI_AAUX 0x60 // read AUX - not supported +#define SPI_AT1 0x70 // read temperature - not supported +#define SPI_MODE_12 0x00 // 12-bit mode - Preferred +#define SPI_MODE_8 0x08 // 8-bit mode +#define SPI_MODE_DFR 0x00 // differential mode - Preferred +#define SPI_MODE_SER 0x04 // single ended mode +#define SPI_PD 0x00 // power down - PENIRQ enabled +#define SPI_ADC 0x01 // ADC enabled +#define SPI_REF 0x02 // Vref enabled - unused +#define SPI_REF_ADC 0x03 // Vref & ADC enabled - unused + + // spi access +static int spi_rw(struct spi_device *spi, u8 * buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + + spi_message_add_tail(&t, &m); + if (spi_sync(spi, &m) != 0 || m.status != 0) + return -1; + + return m.actual_length; +} + + // spi write register +static ssize_t spi_lcd_write_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + up(&priv->sem[slot]); + + return res; +} + + // spi read register +static ssize_t spi_lcd_read_reg(struct pbmi_lcd *priv, char *buf, int len, int slot) +{ + int res = 0; + + down(&priv->sem[slot]); + + memset(priv->wbuf[slot], 0, BUF_MAX_SIZE); + priv->wbuf[slot][0] = buf[0]; + priv->wbuf[slot][1] = buf[1]; + priv->wbuf[slot][2] = buf[2]; + priv->wbuf[slot][3] = buf[3]; + res = spi_rw(priv->spi[slot], priv->wbuf[slot], len); + if (res != 1) { + up(&priv->sem[slot]); + return -EFAULT; + } + + memset(priv->rbuf[slot], 0, BUF_MAX_SIZE); + buf[0] = priv->wbuf[slot][2]; + buf[1] = priv->wbuf[slot][1]; + + up(&priv->sem[slot]); + + return res; +} + +/* + * BMI functions + */ + +static irqreturn_t module_irq_handler(int irq, void *dummy); +void bmi_lcd_config(struct bmi_lcd *lcd, int disp); + + // probe +int bmi_lcd_probe(struct bmi_device *bdev) +{ + + unsigned char acc_data[1]; + unsigned char iox_data[1]; + int slot = bdev->info->slot; + struct i2c_adapter *adap; + struct bmi_lcd *lcd; + char buf[4]; + + printk(KERN_INFO "bmi_lcd.c: probe slot %d\n", slot); + + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + pbmi_lcd.bdev[0] = bdev; + return 0; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + pbmi_lcd.bdev[1] = bdev; + return 0; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + pbmi_lcd.bdev[2] = bdev; + return 0; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + pbmi_lcd.bdev[3] = bdev; + return 0; + } + break; + } + + adap = &bdev->adap; + bmi_slot_power_on(slot); + + // set up bdev/pbmi_lcd pointers + bmi_device_set_drvdata(bdev, &pbmi_lcd); + pbmi_lcd.bdev[slot] = bdev; + + printk(KERN_INFO "Adap = 0x%x",adap); + + printk(KERN_INFO "Lock stuff = 0x%x", &(pbmi_lcd.bdev[slot]->adap)); + + mdelay(500); + + // configure IOX + // [7:6]=interrupts, [5]=SER_PD*, [4]=LCD_RST*, [3]=VSYNC_OE*, [2]=ACC_RST*, [1:0]=backlight + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, 0xFF)) // normal - no accelerometer interrupts + return -ENODEV; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, 0xFF, slot)) // normal - no accelerometer interrupts + return -ENODEV; + + // normal operation - no accelerometer interrupts + if(WriteByteLock_IOX(&pbmi_lcd, IOX_CONTROL, 0x00, slot)) // IOX[7:0]=OUT + return -ENODEV; + + // clear interrupts + if(ReadByteLock_IOX(&pbmi_lcd, IOX_INPUT_REG, iox_data, slot)) + return -ENODEV; + + printk(KERN_INFO "bmi_lcd.c: probe slot %d iox data = %x\n", slot, *iox_data); + + + // accelerometer + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x\n", slot, bdev->epraw.revision_msb); + + // check for PCB revision >= 1.2 + if(bdev->epraw.revision_msb >= 0x12) + { + + // normal IOX operation - accelerometer interrupts + if(WriteByteLock_IOX(&pbmi_lcd, IOX_CONTROL, 0xC0, slot)) // IOX[7:6]=IN, IOX[5:0]=OUT + return -ENODEV; + + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, 0xFB, slot)) // reset OKI accelerometer + return -ENODEV; + + mdelay(2); + + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, 0xFF, slot)) // enable OKI accelerometer + return -ENODEV; + + mdelay(2); + + // write PAGESEL + *acc_data = 0x0; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_PAGESEL, *acc_data, slot)) + return -ENODEV; + + // read device to verify existance + if(ReadByteLock_ACC(&pbmi_lcd, ACC_CPURDY, acc_data, slot)) + return -ENODEV; + + // set TMD = 0x300 (~250 ms) + *acc_data = 0x5; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_TMDH, *acc_data, slot)) + return -ENODEV; + + *acc_data = 0x0; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_TMDL, *acc_data, slot)) + return -ENODEV; + + // set INTOTM + *acc_data = 0x00; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_INTOTM, *acc_data, slot)) + return -ENODEV; + + // set GxAVE + *acc_data = 0x0; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_GAAVE, *acc_data, slot)) + return -ENODEV; + + // set GDTCT[01] + *acc_data = 0x00; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_GDTCT0L, *acc_data, slot)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_GDTCT0H, *acc_data, slot)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_GDTCT1L, *acc_data, slot)) + return -ENODEV; + + *acc_data = 0x00; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_GDTCT1H, *acc_data, slot)) + return -ENODEV; + + // set MODE0 + *acc_data = ACC_MODE0_PDOFF | ACC_MODE0_TMPOFF | ACC_MODE0_AGCON | ACC_MODE0_MAUTO | ACC_MODE0_GDET10; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_MODE0, *acc_data, slot)) + return -ENODEV; + + // set CFG + *acc_data = ACC_CFG_REGMD | ACC_CFG_INTLVL; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_CFG, *acc_data, slot)) + return -ENODEV; + + // set INTMSK + *acc_data = 0xFE; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_INTMSK, *acc_data, slot)) + return -ENODEV; + + // set CTRL0 + *acc_data = ACC_CTRL0_CGAUTO; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_CTRL0, *acc_data, slot)) + return -ENODEV; + + // write PAGESEL + *acc_data = 0x1; + if(WriteByteLock_ACC(&pbmi_lcd, ACC_PAGESEL, *acc_data, slot)) + return -ENODEV; + + acc_probe(&pbmi_lcd.acc[slot], slot); + } + else + { + printk(KERN_INFO "bmi_lcd.c: probe slot %d hardware version = 0x%x (accelerometer not supported)\n", slot, bdev->epraw.revision_msb); + } + + // reset serial link (master) + if((slot == 0) || (slot == 2)) + { + bmi_lcd_inactive(0); + } + else + { + bmi_lcd_inactive(1); + } + + // configure GPIO + // turn LED's on + bmi_set_module_gpio_data(slot, 3, 0); // Red LED=ON + bmi_set_module_gpio_data(slot, 2, 0); // Green LED=ON + + // assert reset + bmi_set_module_gpio_data(slot, 1, 0); // RST=0 + + // set GPIO direction + bmi_set_module_gpio_dir(slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_dir(slot, 0, BMI_GPIO_IN); // real-time pen int state + + mdelay(200); + + // turn LED's off + bmi_set_module_gpio_data(slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data(slot, 2, 1); // Green LED=OFF + + // deassert reset (module) + bmi_set_module_gpio_data(slot, 1, 1); // RST=1 + + mdelay(500); + + // unreset serial link (master) + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_active(0, 0x0, LCD_MODE_I80); + } else { + mdelay(2); + bmi_lcd_active(1, 0x0, LCD_MODE_I80); + } + + + + + // spi set-up + if (bmi_device_spi_setup(bdev, 2000000, SPI_MODE_2, 32)) { + printk(KERN_ERR "bmi_lcd.c: Unable to setup spi%d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + bmi_slot_power_off(slot); + return -EFAULT; + } + + bmi_slot_spi_enable(slot); + pbmi_lcd.spi[slot] = bmi_device_get_spi(bdev); + + + // check spi access and enable touch screen + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + if(spi_lcd_write_reg(&pbmi_lcd, buf, 1, slot) != 1) { + printk(KERN_WARNING "bmi_lcd.c: Unable set-up spi for bmi_lcd %d\n", slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_spi_disable(slot); + bmi_slot_power_off(slot); + return -EFAULT; + } + + + // complete pbmi_lcd set-up + pbmi_lcd.lcd_cnt++; + pbmi_lcd.active = 1; + pbmi_lcd.activated[slot] = 1; + + + mdelay(100); + + lcd = pbmi_lcd.blcd[slot]; + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_config(lcd, 0); + mdelay(2); + } else { + mdelay(2); + bmi_lcd_config(lcd, 1); + mdelay(2); + } + + + // request input event interrupt handler + pbmi_lcd.interrupt[0] = M1_IRQ; + pbmi_lcd.interrupt[1] = M2_IRQ; + pbmi_lcd.interrupt[2] = M3_IRQ; + pbmi_lcd.interrupt[3] = M4_IRQ; + snprintf(pbmi_lcd.int_name, sizeof(pbmi_lcd.int_name), "bmi_lcd%d", slot); + if (request_irq(pbmi_lcd.interrupt[slot], &module_irq_handler, 0, pbmi_lcd.int_name, &pbmi_lcd)) + { + printk( KERN_ERR "bmi_lcd.c: Can't allocate irq %d or find lcd in slot %d\n", pbmi_lcd.interrupt[slot], slot); + bmi_device_set_drvdata(bdev, NULL); + pbmi_lcd.bdev[slot] = NULL; + pbmi_lcd.spi[slot] = NULL; + bmi_device_spi_cleanup(bdev); + bmi_slot_power_off(slot); + return -EBUSY; + } + + // check GPIO status + printk(KERN_INFO "bmi_lcd.c: slot %d gpio = %x\n", slot, bmi_read_gpio_data_reg(slot)); + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return 0; +} + +extern struct delayed_work bmilcd_work0; +extern struct delayed_work bmilcd_work1; +extern struct delayed_work bmilcd_work2; +extern struct delayed_work bmilcd_work3; + + // remove +void bmi_lcd_remove(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + + if(pbmi_lcd.activated[slot] == 0) + return; + + switch(slot) { + case 0: + cancel_delayed_work(&bmilcd_work0); + break; + case 1: + cancel_delayed_work(&bmilcd_work1); + break; + case 2: + cancel_delayed_work(&bmilcd_work2); + break; + case 3: + cancel_delayed_work(&bmilcd_work3); + break; + } + + free_irq(pbmi_lcd.interrupt[slot], &pbmi_lcd); + + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); + + // bmi/spi clean-up + bmi_device_spi_cleanup(bdev); + pbmi_lcd.spi[slot] = NULL; + bmi_slot_spi_disable(slot); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (&bdev[slot], 0); + + // deactivate + pbmi_lcd.activated[slot] = 0; + pbmi_lcd.bdev[slot] = 0; + pbmi_lcd.lcd_cnt--; + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[2] == 0)) { + bmi_lcd_inactive(0); // disable serializer + } + + if((pbmi_lcd.activated[1] == 0) && (pbmi_lcd.activated[3] == 0)) { + bmi_lcd_inactive(1); // disable serializer + } + + if((pbmi_lcd.activated[0] == 0) && (pbmi_lcd.activated[1] == 0) && + (pbmi_lcd.activated[2] == 0) && (pbmi_lcd.activated[3] == 0)) { + pbmi_lcd.active = -1; + } + + // enable LCD on opposite side + switch(slot) { + case 0: + if(pbmi_lcd.bdev[2] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[2]); + break; + case 1: + if(pbmi_lcd.bdev[3] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[3]); + break; + case 2: + if(pbmi_lcd.bdev[0] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[0]); + break; + case 3: + if(pbmi_lcd.bdev[1] != 0) + bmi_lcd_probe(pbmi_lcd.bdev[1]); + break; + } + + acc_remove(&pbmi_lcd.acc[slot], slot); + printk(KERN_INFO "bmi_lcd.c: LCD count = %d\n", pbmi_lcd.lcd_cnt); + + return; +} + +/* + * Input interrupt handler and support routines + */ + +static void update_pen_state(void *arg, int slot, int x, int y, int pressure) +{ + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; + int sync = 0; + + if (pressure) + { + /*input_report_abs(pbmi_lcd->input_dev[slot], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_PRESSURE, pressure); + + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_PRESSURE, pressure);*/ + + if((slot == 0) || (slot == 2)) + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, pressure); + } + else + { + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_Y, y); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_X, x); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, pressure); + } + + if (!pbmi_lcd->pen_down[slot]) + { + /*input_report_key(pbmi_lcd->input_dev[slot], BTN_TOUCH, 1); + input_report_key(pbmi_lcd->input_dev[BMI_TS_M1234], BTN_TOUCH, 1);*/ + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 1); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 1); + } + + } + sync = 1; + } + else if (pbmi_lcd->pen_down[slot]) + { + /*input_report_key(pbmi_lcd->input_dev[slot], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[slot], ABS_PRESSURE, 0); + + input_report_key(pbmi_lcd->input_dev[BMI_TS_M1234], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M1234], ABS_PRESSURE, 0); */ + + if((slot == 0) || (slot == 2)) + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M13], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M13], ABS_PRESSURE, 0); + } + else + { + input_report_key(pbmi_lcd->input_dev[BMI_TS_M24], BTN_TOUCH, 0); + input_report_abs(pbmi_lcd->input_dev[BMI_TS_M24], ABS_PRESSURE, 0); + } + sync = 1; + } + + if (sync) + { + /*input_sync(pbmi_lcd->input_dev[slot]); + input_sync(pbmi_lcd->input_dev[BMI_TS_M1234]);*/ + if((slot == 0) || (slot == 2)) + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M13]); + } + else + { + input_sync(pbmi_lcd->input_dev[BMI_TS_M24]); + } + } + pbmi_lcd->pen_down[slot] = pressure ? 1 : 0; + +} + + +void bmilcd_input_work(void *arg, int slot); + +void bmilcd_input_work0(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 0); +} + +void bmilcd_input_work1(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 1); +} + +void bmilcd_input_work2(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 2); +} + +void bmilcd_input_work3(struct work_struct * work) { + bmilcd_input_work(&pbmi_lcd, 3); +} + +DECLARE_DELAYED_WORK(bmilcd_work0, bmilcd_input_work0); +DECLARE_DELAYED_WORK(bmilcd_work1, bmilcd_input_work1); +DECLARE_DELAYED_WORK(bmilcd_work2, bmilcd_input_work2); +DECLARE_DELAYED_WORK(bmilcd_work3, bmilcd_input_work3); + +// work handler +void bmilcd_input_work(void *arg, int slot) { + struct pbmi_lcd *pbmi_lcd = (struct pbmi_lcd *)arg; + struct i2c_adapter *adap = &pbmi_lcd->bdev[slot]->adap; + unsigned char acc_data[1]; + static int pitch = 0; + static int roll = 0; + static int gx = 0; + static int gy = 0; + + unsigned char iox_data[1]; + + unsigned char buf[4]; + int x = 0; + int y = 0; + int z1 = 0; + int z2 = 0; + int pressure = 0; + int debounce; + int penirq; + +#if defined DEBUG + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d)\n", slot); +#endif + + if(pbmi_lcd->bdev[slot] == 0) { + printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work called with no bdev active (slot %d)\n", slot); + return; + } + + if(pbmi_lcd->bdev[slot]->epraw.revision_msb >= 0x12) { + + // orientation + // read ROLL + printk(KERN_INFO "ACC Work: ROLLH\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_ROLLH, acc_data, slot)) + return; + roll = (0x0000 | *acc_data) << 8; + printk(KERN_INFO "ACC Work: ROLLL\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_ROLLL, acc_data, slot)) + return; + roll = roll | *acc_data; + // read PITCH + printk(KERN_INFO "ACC Work: PITCHH\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_PITCHH, acc_data, slot)) + return; + pitch = (0x0000 | *acc_data) << 8; + printk(KERN_INFO "ACC Work: PITCHL\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_PITCHL, acc_data, slot)) + return; + pitch = pitch | *acc_data; + + + + printk(KERN_INFO "ACC Work: GAZH\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAZH, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[0] = *acc_data; + printk(KERN_INFO "ACC Work: GAZL\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAZL, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[1] = *acc_data; + printk(KERN_INFO "ACC Work: GAYH\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAYH, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[2] = *acc_data; + gy = *acc_data << 8; + printk(KERN_INFO "ACC Work: GAYL\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAYL, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[3] = *acc_data; + gy = gy | *acc_data; + printk(KERN_INFO "ACC Work: GAXH\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAXH, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[4] = *acc_data; + gx = *acc_data << 8; + printk(KERN_INFO "ACC Work: GAXL\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_GAXL, acc_data, slot)) + return; + pbmi_lcd->acc[slot].sample[5] = *acc_data; + gx = gx | *acc_data; + + //wake up any read's + pbmi_lcd->acc[slot].flag = 1; + wake_up_interruptible(&pbmi_lcd->acc[slot].wq); + + if(ReadByteLock_IOX(pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return; + *iox_data = (*iox_data & 0xF8) | 0x4; + if(WriteByteLock_IOX(pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return; + printk(KERN_INFO "ACC Work: IOX\n"); + + // read STATUS + printk(KERN_INFO "ACC Work: STATUS\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_STATUS, acc_data, slot)) + return; + + if((*acc_data & 0x1) == 0) { + + // write PAGESEL + *acc_data = 0x0; + if(WriteByteLock_ACC(pbmi_lcd, ACC_PAGESEL, *acc_data, slot)) + return; + + // read INTRQ + printk(KERN_INFO "ACC Work: INTRQ\n"); + if(ReadByteLock_ACC(pbmi_lcd, ACC_INTRQ, acc_data, slot)) + return; + } + + // write PAGESEL + *acc_data = 0x1; + if(WriteByteLock_ACC(pbmi_lcd, ACC_PAGESEL, *acc_data, slot)) + return; + + + + // report orientation + // printk(KERN_INFO "bmi_lcd.c: bmi_lcd_input work (slot %d) pitch=0x%x, roll=0x%x, ABS_MISC=0x%x\n", + // slot, pitch, roll, pitch << 16 | roll); //pjg - debug + + input_report_abs(pbmi_lcd->input_dev[slot], ABS_MISC, (pitch << 16) | roll); + input_sync(pbmi_lcd->input_dev[slot]); + } + + // read touch screen - X, Y, TOUCH, PRESSURE + + + penirq = bmi_slot_status_irq_state(slot); + /*printk(KERN_INFO "bmi_lcd.c: IRQ Status %d (slot %d) %d\n", penirq, slot,msecs_to_jiffies(10));*/ + + if (pbmi_lcd->activated[slot] && penirq) + { + + for(debounce = 0; debounce < DEBOUNCE; debounce++) + { + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AY | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + y = (((buf[0] << 5) | buf[1] >> 3)) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AX | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + x = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ1 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z1 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_AZ2 | SPI_MODE_12 | SPI_MODE_DFR | SPI_ADC; + spi_lcd_read_reg(pbmi_lcd, buf, 1, slot); + z2 = (((buf[0] << 5) | buf[1]) >> 3) & 0xFFF; + mdelay(1); + } + + if(x && y && z1 && z2) + pressure = (X_PLATE * x / 4096) * ((z2 / z1) - 1); + + x = 4096 - x; + y = 4096 - y; + + if (pressure < 70) + { + if (pbmi_lcd->scount) + update_pen_state(arg, slot, x, y, pressure); + else + { + pbmi_lcd->scount[slot]++; + /*update_pen_state(arg, slot, 0, 0, pressure);*/ + } + } + /* else + { + update_pen_state(arg, slot, 0, 0, pressure); + }*/ + + switch(slot) + { + case BMI_TS_M1: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + break; + case BMI_TS_M2: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + break; + case BMI_TS_M3: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + break; + case BMI_TS_M4: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + break; + } + /* printk(KERN_INFO "bmi_lcd.c: work scheduled on (slot %d)\n", slot); */ + /*buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot);*/ + } + + else + { + /*printk(KERN_INFO "bmi_lcd.c: Pen up on (slot %d)\n", slot);*/ + memset(buf, 0, 4); + buf[3] = SPI_START | SPI_PD; + spi_lcd_write_reg(pbmi_lcd, buf, 1, slot); + update_pen_state(arg,slot, 0, 0, 0); + enable_irq(pbmi_lcd->interrupt[slot]); + } + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + disable_irq(irq); + /*printk(KERN_INFO "bmi_lcd.c: Interupt on (slot %d)\n", irq);*/ + switch(irq) + { + case M1_IRQ: + schedule_delayed_work(&bmilcd_work0, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M1] = 0; + break; + case M2_IRQ: + schedule_delayed_work(&bmilcd_work1, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M2] = 0; + break; + case M3_IRQ: + schedule_delayed_work(&bmilcd_work2, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M3] = 0; + break; + case M4_IRQ: + schedule_delayed_work(&bmilcd_work3, WORK_DELAY); + pbmi_lcd.scount[BMI_TS_M4] = 0; + break; + } + return IRQ_HANDLED; +} + +/* + * control device operations + */ + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *filp) +{ + if(pbmi_lcd.open_flag) { + return - EBUSY; + } + pbmi_lcd.open_flag = 1; + filp->private_data = &pbmi_lcd; + return 0; +} + +// release +int cntl_release(struct inode *inode, struct file *filp) +{ + pbmi_lcd.open_flag = 0; + return 0; +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data[1]; + int slot = (__user arg) & 0xF; + int bl = ((__user arg) & 0x70) >> 4; + + // error if no lcd active. + if(pbmi_lcd.active == -1) + return -ENODEV; + + if(cmd != BMI_LCD_GETSTAT) { + + // error if slot invalid + if((slot < CPLD_M1) || (slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[slot]->adap; + } + + // ioctl's + switch (cmd) { + case BMI_LCD_RLEDOFF: + bmi_set_module_gpio_data(slot, 3, 1);// Red LED=OFF + break; + case BMI_LCD_RLEDON: + bmi_set_module_gpio_data(slot, 3, 0);// Red LED=ON + break; + case BMI_LCD_GLEDOFF: + bmi_set_module_gpio_data(slot, 2, 1);// Green LED=OFF + break; + case BMI_LCD_GLEDON: + bmi_set_module_gpio_data(slot, 2, 0);// Green LED=ON + break; + case BMI_LCD_VSYNC_DIS: // enable VSYNC buffer tristate output + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data |= 0x08; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_VSYNC_EN: // disable VSYNC buffer tristate output + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data &= ~0x08; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_EN: // enable LCD component + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data &= ~0x10; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_DIS: // disable LCD component only + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data |= 0x10; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_SER_EN: // enable Serializer component + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data &= ~0x20; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_SER_DIS: // disable Serializer component only + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data |= 0x20; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_SETRST: // overall module reset + bmi_set_module_gpio_data (slot, 1, 0); // RST=0 + break; + case BMI_LCD_CLRRST: // overall module enable + bmi_set_module_gpio_data (slot, 1, 1); // RST=1 + break; + case BMI_LCD_SET_BL: // set backlight brightness + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8) | bl; + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + break; + case BMI_LCD_GETSTAT: + { + int *slot = ((int __user *) arg); + int read_data; + + *slot &= 0xF; + + // error if slot invalid + if((*slot < CPLD_M1) || (*slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[*slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[*slot]->adap; + + if(ReadByteLock_IOX(&pbmi_lcd, IOX_INPUT_REG, iox_data, *slot)) + return -ENODEV; + + read_data = *iox_data | (bmi_read_gpio_data_reg(*slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + case BMI_LCD_ACTIVATE: //pjg fix/test + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + return -ENODEV; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + return -ENODEV; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + return -ENODEV; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + return -ENODEV; + } + break; + } + // activate + if((!pbmi_lcd.activated[slot]) && (pbmi_lcd.bdev[slot] != 0)) { + bmi_lcd_probe(pbmi_lcd.bdev[slot]); + } + break; + case BMI_LCD_DEACTIVATE: + if(pbmi_lcd.activated[slot]) { + disable_irq_nosync(pbmi_lcd.interrupt[slot]); + pbmi_lcd.activated[slot] = 0; + if(ReadByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, iox_data, slot)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8); + if(WriteByteLock_IOX(&pbmi_lcd, IOX_OUTPUT_REG, *iox_data, slot)) + return -ENODEV; + bmi_slot_power_off(slot); + } + break; + case BMI_LCD_SUSPEND: + printk(KERN_ERR "BMI_LCD_SUSPEND NOT IMPLEMENTED\n"); //pjg + break; + case BMI_LCD_RESUME: + printk(KERN_ERR "BMI_LCD_RESUME NOT IMPLEMENTED\n"); //pjg + break; + default: + return -ENOTTY; + } + return 0; +} + + // control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + + // BMI LCD fops +void bmi_lcd_config(struct bmi_lcd *lcd, int disp) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.config)) { + lcd->lcd_ops.config(disp); + } +} + +void bmi_lcd_reset(struct bmi_lcd *lcd, int slot) +{ + if(pbmi_lcd.active == -1) { + return; + } + + if((lcd) && (lcd->lcd_ops.reset)) { + lcd->lcd_ops.reset(slot); + } +} + + +static struct miscdevice cntl_dev = { + MISC_DYNAMIC_MINOR, + "bmi_lcd_control", + &cntl_fops +}; + +/* + * Module functions + */ + +char const input_name0[MAX_STRG] = "bmi_lcd_ts0"; +char const input_name1[MAX_STRG] = "bmi_lcd_ts1"; +char const input_name2[MAX_STRG] = "bmi_lcd_ts2"; +char const input_name3[MAX_STRG] = "bmi_lcd_ts3"; +char const input_name4[MAX_STRG] = "bmi_lcd_ts4"; +char const input_name5[MAX_STRG] = "bmi_lcd_ts5"; +char const input_name6[MAX_STRG] = "bmi_lcd_ts6"; + +static __init int bmi_lcd_init(void) +{ + int ts; + int rc = 0; + + // No lcd is active. + pbmi_lcd.active = -1; + pbmi_lcd.activated[0] = 0; + pbmi_lcd.activated[1] = 0; + pbmi_lcd.activated[2] = 0; + pbmi_lcd.activated[3] = 0; + + // set up control character device - bmi_lcd_control + rc = misc_register(&cntl_dev); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't allocate bmi_lcd_control device\n"); + return rc; + } + + // Allocate and Register input device. - bmi_lcd_ts[BMI_TS_M1:BMI_TS_M1234] + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) { + pbmi_lcd.input_dev[ts] = input_allocate_device(); + if(!pbmi_lcd.input_dev[ts]) { + printk(KERN_ERR "bmi_lcd_init: Can't allocate input_dev[ts]\n"); + return -ENOMEM; + } + + // set up input device + switch(ts) { + case BMI_TS_M1: + pbmi_lcd.input_dev[BMI_TS_M1]->name = input_name0; + pbmi_lcd.input_dev[BMI_TS_M1]->phys = input_name0; + break; + case BMI_TS_M2: + pbmi_lcd.input_dev[BMI_TS_M2]->name = input_name1; + pbmi_lcd.input_dev[BMI_TS_M2]->phys = input_name1; + break; + case BMI_TS_M3: + pbmi_lcd.input_dev[BMI_TS_M3]->name = input_name2; + pbmi_lcd.input_dev[BMI_TS_M3]->phys = input_name2; + break; + case BMI_TS_M4: + pbmi_lcd.input_dev[BMI_TS_M4]->name = input_name3; + pbmi_lcd.input_dev[BMI_TS_M4]->phys = input_name3; + break; + case BMI_TS_M13: + pbmi_lcd.input_dev[BMI_TS_M13]->name = input_name4; + pbmi_lcd.input_dev[BMI_TS_M13]->phys = input_name4; + break; + case BMI_TS_M24: + pbmi_lcd.input_dev[BMI_TS_M24]->name = input_name5; + pbmi_lcd.input_dev[BMI_TS_M24]->phys = input_name5; + break; + case BMI_TS_M1234: + pbmi_lcd.input_dev[BMI_TS_M1234]->name = input_name6; + pbmi_lcd.input_dev[BMI_TS_M1234]->phys = input_name6; + break; + } + pbmi_lcd.input_dev[ts]->id.bustype = BUS_BMI; + pbmi_lcd.input_dev[ts]->private = &pbmi_lcd; + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); + pbmi_lcd.input_dev[ts]->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); + pbmi_lcd.input_dev[ts]->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_X)] |= BIT_MASK(ABS_X); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_Y)] |= BIT_MASK(ABS_Y); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_PRESSURE)] |= BIT_MASK(ABS_PRESSURE); + pbmi_lcd.input_dev[ts]->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_X, BMI_LCD_MIN_XC, BMI_LCD_MAX_XC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_Y, BMI_LCD_MIN_YC, BMI_LCD_MAX_YC, 0, 0); + input_set_abs_params(pbmi_lcd.input_dev[ts], ABS_PRESSURE, 0, 1024, 0, 0); + + // register input device + if(input_register_device(pbmi_lcd.input_dev[ts])) { + int tts; + printk(KERN_ERR "bmi_lcd_init() - input_register_device failed.\n"); + + for(tts = BMI_TS_M1; tts < ts; tts++) + input_unregister_device(pbmi_lcd.input_dev[tts]); + + misc_deregister(&cntl_dev); + + return -ENODEV; + } + } + + pbmi_lcd.lcd_cnt = 0; + + // hardware specfic set-up + s320x240_bmi_lcd.interface = s320x240_lcd_interface, + s320x240_bmi_lcd_ops.config = (void(*)) &s320x240_config; + s320x240_bmi_lcd_ops.reset = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.suspend = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.resume = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_on = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.disp_off = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.activate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd_ops.deactivate = NULL; //pjg - placeholder for multiple LCD hardware types + s320x240_bmi_lcd.lcd_ops = s320x240_bmi_lcd_ops; + pbmi_lcd.blcd[0] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[1] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[2] = &s320x240_bmi_lcd; + pbmi_lcd.blcd[3] = &s320x240_bmi_lcd; + + sema_init(&pbmi_lcd.sem[0], 1); + sema_init(&pbmi_lcd.sem[1], 1); + sema_init(&pbmi_lcd.sem[2], 1); + sema_init(&pbmi_lcd.sem[3], 1); + + sema_init(&pbmi_lcd.i2c_sem[0], 1); + sema_init(&pbmi_lcd.i2c_sem[1], 1); + sema_init(&pbmi_lcd.i2c_sem[2], 1); + sema_init(&pbmi_lcd.i2c_sem[3], 1); + + acc_init(); + + /*s320x240_config(0); + s320x240_config(1);*/ + + // register with BMI + rc = bmi_register_driver(&bmi_lcd_driver); + if(rc) { + printk(KERN_ERR "bmi_lcd.c: Can't register bmi_lcd_driver\n"); + + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + misc_deregister(&cntl_dev); + + return rc; + } + + printk("bmi_lcd.c: BMI_LCD Driver v%s \n", BMILCD_VERSION); + + return 0; +} + +static void __exit bmi_lcd_clean(void) +{ + int ts; + + // delete timers + del_timer(&pbmi_lcd.timer[0]); + del_timer(&pbmi_lcd.timer[1]); + del_timer(&pbmi_lcd.timer[2]); + del_timer(&pbmi_lcd.timer[3]); + + // remove input devices + for(ts = BMI_TS_M1; ts < BMI_TS_NUM; ts++) + input_unregister_device(pbmi_lcd.input_dev[ts]); + + // remove control device + misc_deregister(&cntl_dev); + + // remove bmi driver + bmi_unregister_driver(&bmi_lcd_driver); + + acc_clean(); + + return; +} + +module_init(bmi_lcd_init); +module_exit(bmi_lcd_clean); + + +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BMI lcd device driver"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_control"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_ts"); +MODULE_SUPPORTED_DEVICE("bmi_lcd_acc"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/drivers/bmi/pims/lcd/bmi_s320x240.c @@ -0,0 +1,632 @@ +#include +#include +#include //pjg +#include +#include +#include +#include "bug_lcd.h" + +// BMI device ID table +static struct bmi_device_id bmi_vs6624_tbl[] = +{ + { //pjg .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .match_flags = BMI_ANY, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_CAMERA_VS6624, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_vs6624_tbl); + +int bmi_vs6624_probe(struct bmi_device *bdev); +void bmi_vs6624_remove(struct bmi_device *bdev); +int bmi_vs6624_suspend(struct bmi_device *bdev); +int bmi_vs6624_resume(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_vs6624_driver = +{ + .name = "bmi_vs6624", + .id_table = bmi_vs6624_tbl, + .probe = bmi_vs6624_probe, + .remove = bmi_vs6624_remove, + }; + + +struct bmi_vs6624 { + struct bmi_device *bdev; + struct bmi_cam bcam; + unsigned int shutter; // shutter button save state + unsigned int zoomin; // zoomin button save state + unsigned int zoomout; // zoom out button save state + unsigned int flash; // state of camera FLASH + int irq; + struct input_dev *idev; + struct work_struct work; + +}; + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + + +// read byte from I2C IO expander + +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + //Rework: add conditional debug messages here + ret = -1; + } + return ret; +} + + +// write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + //Rework: add conditional debug messages here + + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +/* + * Input interrupt handler and support routines + */ + + +// work handler +void bmi_vs6624_buttons_work(void *arg) +{ + struct bmi_vs6624 *bmicam = (struct bmi_vs6624*)arg; + struct i2c_adapter *adap = &bmicam->bdev->adap; + + unsigned char iox_data; + unsigned int test_value; + int sync_flag = 0; + + + // read IOX data input + ReadByte_IOX (adap, IOX_INPUT_REG, &iox_data); + + // zoom in button + test_value = !((iox_data & 0x2) >> 1); + if (test_value != bmicam->zoomin) { + printk (KERN_ERR "bmi_vs6624buttons_work() - report ZOOMIN\n"); + bmicam->zoomin = test_value; + input_report_key(bmicam->idev, BN_ZOOMIN, test_value); + sync_flag = 1; + } + + + // zoom out button + test_value = !((iox_data & 0x4) >> 2); + if (test_value != bmicam->zoomout) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - report ZOOMOUT\n"); + bmicam->zoomout = test_value; + input_report_key(bmicam->idev, BN_ZOOMOUT, test_value); + sync_flag = 1; + } + + // flash button + test_value = (iox_data & 0x8) >> 3; + if (test_value != bmicam->flash) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - report FLASH\n"); + bmicam->flash = test_value; + input_report_key(bmicam->idev, BN_FLASH, test_value); + sync_flag = 1; + } + + + if ( sync_flag ) { + printk (KERN_ERR "bmi_vs6624_buttons_work() - input_sync()ing..\n"); + input_sync(bmicam->idev); + } + + enable_irq(bmicam->irq); + +} + + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + struct bmi_vs6624 *bmicam = dummy; + unsigned int test_value; + + int slot; + + + disable_irq_nosync(irq); + + slot = bmicam->bdev->info->slot; + + + // shutter button on GPIO + + test_value = !(bmi_read_gpio_data_reg (slot) & 0x1); + + if (!test_value == bmicam->shutter) { + bmicam->shutter = test_value; + printk (KERN_ERR "module_irq_handler() - report SHUTTER\n"); + input_report_key(bmicam->idev, BN_SHUTTER, test_value); + input_sync(bmicam->idev); + } + + + + // other buttons on I2C IOX + schedule_work (&bmicam->work); + return IRQ_HANDLED; +} + +/* + * control functions + */ + + +// configure IOX IO and states +void configure_IOX(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap = &cam->bdev->adap; + + printk (KERN_ERR "configure_IOX() - enter\n"); + printk (KERN_ERR "configure_IOX() - cam = %p\n", cam); + printk (KERN_ERR "configure_IOX() - cam->bdev = %p\n", cam->bdev); + printk (KERN_ERR "configure_IOX() - cam->bdev->adap = %p\n", &cam->bdev->adap); + + + WriteByte_IOX (adap, IOX_OUTPUT_REG, 0x40); // CE=0, F_CHG=1,SYNC=0, TORCH=0 + WriteByte_IOX (adap, IOX_CONTROL, 0x0F); // IOX[7:4]=OUT, IOX[3:0]=IN +} + +// configure GPIO IO and states +void configure_GPIO(struct bmi_vs6624 *cam) +{ + // set states before turning on outputs + + int slot = cam->bdev->info->slot; + + bmi_set_module_gpio_data (slot, 3, 1); // Red LED=OFF + bmi_set_module_gpio_data (slot, 2, 1); // Green LED=OFF + bmi_set_module_gpio_data (slot, 1, 0); // SER_RST=0 + + // configure direction + bmi_set_module_gpio_data (slot, 3, BMI_GPIO_OUT); + bmi_set_module_gpio_data (slot, 2, BMI_GPIO_OUT); + bmi_set_module_gpio_data (slot, 1, BMI_GPIO_OUT); + bmi_set_module_gpio_data (slot, 0, BMI_GPIO_IN); // SHUTTER +} + +// deconfigure IOX and GPIO +void deconfigure_module(struct bmi_vs6624 *cam) +{ + int slot = cam->bdev->info->slot; + struct i2c_adapter *adap = &cam->bdev->adap; + + WriteByte_IOX (adap, IOX_CONTROL, 0xFF); + bmi_set_module_gpio_data (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_data (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_data (slot, 1, BMI_GPIO_IN); +} + + +// configure serializer on plug-in module +void configure_serializer(struct bmi_vs6624 *cam) +{ + int slot = cam->bdev->info->slot; + + bmi_set_module_gpio_data(slot, 1, 1); // SER_RST=1 +} + +void deconfigure_serializer(struct bmi_vs6624 *cam) +{ + int slot = cam->bdev->info->slot; + bmi_set_module_gpio_data(slot, 1, 0); // SER_RST=0 +} + +void enable_camera(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap = &cam->bdev->adap; + unsigned char iox_data; + + printk (KERN_ERR "enable_camera() enter\n"); + + // The first i2c read seems to mess everything up. + + ReadByte_IOX(adap, IOX_OUTPUT_REG, &iox_data); + printk (KERN_ERR "enable_camera() iox_data = %02X\n", iox_data); + + WriteByte_IOX(adap, IOX_OUTPUT_REG, iox_data | 0x80); + printk (KERN_ERR "enable_camera() exit\n"); +} + +// disable camera on plug-in module +void disable_camera(struct bmi_vs6624 *cam) +{ + struct i2c_adapter *adap = &cam->bdev->adap; + unsigned char iox_data; + + printk (KERN_ERR "disable_camera() enter\n"); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, iox_data & 0x70); + + printk (KERN_ERR "disable_camera() exit\n"); +} + +// generate sync +void generate_camera_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "generate_camera_sync() - enter\n"); + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + + printk(KERN_INFO "generate_camera_sync() - read = %02X\n", iox_data[0]); + printk(KERN_INFO "generate_camera_sync() - write = %02X\n", *iox_data | 0x20); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + + printk(KERN_INFO "generate_camera_sync() - write = %02X\n", *iox_data & 0xD0); + + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + udelay(20); // 60 MHz * 1024 = ~17 us sync time + + printk(KERN_INFO "generate_camera_sync() - exit\n"); +} + +void set_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "set_sync() - enter\n"); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data | 0x20);// SYNC = 1 + + printk(KERN_INFO "set_sync() - exit\n"); +} + +void clear_sync(struct i2c_adapter *adap) +{ + unsigned char iox_data[0]; + + printk(KERN_INFO "clear_sync() - enter\n"); + + ReadByte_IOX (adap, IOX_OUTPUT_REG, iox_data); + WriteByte_IOX (adap, IOX_OUTPUT_REG, *iox_data & 0xD0);// SYNC = 0 + + printk(KERN_INFO "clear_sync() - exit\n"); +} + + +// check serializer lock +int check_camera_lock(void) +{ + return bmi_sensor_lock_status(); +} + +void bmi_vs6624_set_color(struct bmi_cam *cam, int bright, int saturation, int red, int green, int blue) +{ + + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + adap = &bmi_vs6624->bdev->adap; + + vs6624_set_color (adap, bright, saturation, red, green, blue); + return; + +} + +void bmi_vs6624_get_color(struct bmi_cam *cam, int *bright, int *saturation, int *red, int *green, int *blue) +{ + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + adap = &bmi_vs6624->bdev->adap; + + vs6624_get_color (adap, bright, saturation, red, green, blue); + return; +} + + + + +void bmi_vs6624_set_ae_mode (struct bmi_cam *cam, int ae_mode) +{ + printk (KERN_ERR "bmi_vs6624_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +void bmi_vs6624_get_ae_mode (struct bmi_cam *cam, int *ae_mode) +{ + printk (KERN_ERR "bmi_vs6624_set_ae_mode() - NOT IMPLEMENTED.\n"); +} + + +sensor_interface * bmi_vs6624_config (struct bmi_cam *cam, int *frame_rate, int high_quality) +{ + //REWORK: Add code here + return 0; + +} + + +sensor_interface * bmi_vs6624_reset (struct bmi_cam *cam) +{ + //REWORK: Add code here + //REWORK: What is a valid soft reset sequence ? + return 0; +} + +int bmi_vs6624_activate (struct bmi_cam *cam, struct input_dev *idev) +{ + //REWORK: Add code here + int rc = 0; + int i; + struct i2c_adapter *adap; + struct bmi_vs6624 *bmi_vs6624; + + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + //bmi_vs6624 struct fields + bmi_vs6624->shutter = 0; + bmi_vs6624->zoomin = 0; + bmi_vs6624->zoomout = 0; + bmi_vs6624->flash = 0; + + // install button irq_handler + if (request_irq(bmi_vs6624->irq, &module_irq_handler, 0, "bmi_cam_button", bmi_vs6624)) { + printk( KERN_ERR + "bmi_vs6624_activate() Can't allocate irq %d\n", + bmi_vs6624->irq + ); + + rc = -EBUSY; + goto exit; + } + + //Activate serial link + bmi_sensor_active(0); // rising edge clock + bmi_sensor_active(1); // rising edge clock + configure_serializer (bmi_vs6624); + + adap = &bmi_vs6624->bdev->adap; + set_sync (adap); + + for (i = 0; i < 10; i++) { + + msleep(10); + + if(check_camera_lock()) { + printk(KERN_INFO "vs6624.c: camera serializer locked, i = %d\n", i); + break; + } + else { + printk(KERN_ERR "vs6624.c: camera serializer did not lock,i = %d\n", i); + } + + } + clear_sync (adap); + + +exit: + return rc; +} +int bmi_vs6624_deactivate (struct bmi_cam *cam) +{ + //REWORK: Add code here + struct bmi_vs6624 *bmi_vs6624; + bmi_vs6624 = container_of(cam, struct bmi_vs6624, bcam); + + + //De-activate serial link + deconfigure_serializer (bmi_vs6624); + + + //uninstall button irq_handler + + free_irq(bmi_vs6624->irq, bmi_vs6624); + + + return 0; +} + +int bmi_vs6624_probe(struct bmi_device *bdev) +{ + + //REWORK: Add code here + + int slot = bdev->info->slot; + + + // allocate a driver-specific structure + + struct bmi_vs6624 *bmi_vs6624 = kzalloc(sizeof(struct bmi_vs6624), GFP_KERNEL); + if (!bmi_vs6624) { + return -1; + } + + // attach bmi_device structure (so we can find it later). + + bmi_device_set_drvdata(bdev, bmi_vs6624); + + + + // initialize bmi_vs6624 struct + + bmi_vs6624->bdev = bdev; + + // sensor interface struct fields + + bmi_vs6624->bcam.interface.clk_mode = 0; // gated + bmi_vs6624->bcam.interface.ext_vsync = 1; // external vsync + bmi_vs6624->bcam.interface.Vsync_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.Hsync_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.pixclk_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.data_pol = 0; // non-inverted + bmi_vs6624->bcam.interface.data_width = 1; // 8-bits + bmi_vs6624->bcam.interface.width = 1280-1; // 1280 - SXGA + bmi_vs6624->bcam.interface.height = 1024-1; // 1024 - SXGA + bmi_vs6624->bcam.interface.pixel_fmt = IPU_PIX_FMT_UYVY; // YUV422 + bmi_vs6624->bcam.interface.mclk = 12000000; // frequency/src + + //bmi_camera_sensor struct fields + + bmi_vs6624->bcam.sensor.set_color = bmi_vs6624_set_color; + bmi_vs6624->bcam.sensor.get_color = bmi_vs6624_get_color; + bmi_vs6624->bcam.sensor.set_ae_mode = bmi_vs6624_set_ae_mode; + bmi_vs6624->bcam.sensor.get_ae_mode = bmi_vs6624_get_ae_mode; + bmi_vs6624->bcam.sensor.config = bmi_vs6624_config; + bmi_vs6624->bcam.sensor.reset = bmi_vs6624_reset; + + //bmi_cam struct fields + + bmi_vs6624->bcam.activate = bmi_vs6624_activate ; + bmi_vs6624->bcam.deactivate = bmi_vs6624_deactivate; + + //bmi_vs6624 struct fields + bmi_vs6624->shutter = 0; + bmi_vs6624->zoomin = 0; + bmi_vs6624->zoomout = 0; + bmi_vs6624->flash = 0; + + //initialize struct work_struct + PREPARE_WORK (&bmi_vs6624->work, bmi_vs6624_buttons_work, bmi_vs6624); + + + //Do one-time hw initialization (e.g. patch) + + // configure IOX + configure_IOX (bmi_vs6624); + + // configure GPIO + configure_GPIO (bmi_vs6624); + + // chip enable camera + enable_camera (bmi_vs6624); + + vs6624_patch (&bmi_vs6624->bdev->adap); + + //register with bug_camera + + //REWORK: check return code + register_bug_camera (&bmi_vs6624->bcam, slot); + + return 0; +} + +void bmi_vs6624_remove(struct bmi_device *bdev) +{ + //REWORK: Add code here + + + //get our pointer + struct bmi_vs6624 *bmi_vs6624 = (struct bmi_vs6624*)(bmi_device_get_drvdata (bdev)); + int slot = bdev->info->slot; + + + + unregister_bug_camera ( &bmi_vs6624->bcam, slot); + + //REWORK: Avoid I2c access if camera module is not present. + + disable_camera (bmi_vs6624); + deconfigure_module (bmi_vs6624); + + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + + //free driver-specific structure + kfree (bmi_vs6624); + return; +} + + +static __init int bmi_vs6624_init(void) +{ + +// REWORK: Add code here. + +// Register with BMI bus. + return bmi_register_driver (&bmi_vs6624_driver); + +} + +static void __exit bmi_vs6624_cleanup(void) +{ +// REWORK: Add code here. + bmi_unregister_driver (&bmi_vs6624_driver); + return; +} + + +module_init(bmi_vs6624_init); +module_exit(bmi_vs6624_cleanup); + + --- /dev/null +++ git/drivers/bmi/pims/lcd/lcd_ctl.c @@ -0,0 +1,421 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include "lcd_ctl.h" + +static int lcd_ctl_open (struct inode *, struct file *); +static int lcd_ctl_release (struct inode *, struct file *); +static int lcd_ctl_ioctl (struct inode *, struct file *, unsigned int, unsigned long); +static int ReadByte_IOX(struct i2c_adapter *, unsigned char, unsigned char *) + + +struct file_operations lcd_ctl_fops = { + .owner = THIS_MODULE, + .ioctl = lcd_ctl_ioctl, + .open = lcd_ctl_open, + .release = lcd_ctl_release, +}; + + + // read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + /* Read Byte with Pointer */ + + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; /* write */ + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; /* read */ + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + // write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + /* Write Byte with Pointer */ + + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; /* write */ + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; /* write */ + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + + +static int lcd_ctl_major; + +int lcd_ctl_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI LCD Control Driver"); + + if (retval) { + return -1; + } + lcd_ctl_major = MAJOR(dev_id); + return 0; +} + +void lcd_ctl_clean (void) +{ + dev_t dev_id; + + dev_id = MKDEV(lcd_ctl_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int lcd_ctl_probe (struct lcd_ctl *lcd_ctl, int slot) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + cdev = &lcd_ctl->cdev; + cdev_init (cdev, &lcd_ctl_fops); + + dev_id = MKDEV (lcd_ctl_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + bmi_class = bmi_get_bmi_class (); + + lcd_ctl->class_dev = device_create (bmi_class, NULL, MKDEV(lcd_ctl_major, slot), lcd_ctl, "bmi_lcd_ctl_m%i", slot+1); + + if (IS_ERR(lcd_ctl->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_lcd_ctl_m%i; errno = %ld\n", + slot+1, PTR_ERR(lcd_ctl->class_dev)); + lcd_ctl->class_dev = NULL; + } + lcd_ctl->slot = slot; + + return ret; +} + +void lcd_ctl_remove (struct lcd_ctl *lcd_ctl, int slot) +{ + struct class *bmi_class; + + bmi_class = bmi_get_bmi_class (); + device_destroy (bmi_class, MKDEV(lcd_ctl_major, slot)); + + lcd_ctl->class_dev = 0; + + cdev_del (&lcd_ctl->cdev); + return; +} + + +static int lcd_ctl_open (struct inode *inode, struct file *file) +{ + struct lcd_ctl *lcd_ctl; + + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + + + // Save ctl pointer for later. + + file->private_data = lcd_ctl; + return 0; +} + +static int lcd_ctl_release (struct inode *inode, struct file *file) +{ + struct lcd_ctl *lcd_ctl; + + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + return 0; +} + + +/*// ioctl +int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data[1]; + int slot = (__user arg) & 0xF; + int bl = ((__user arg) & 0x70) >> 4; + + // error if no lcd active. + if(pbmi_lcd.active == -1) + return -ENODEV; + + if(cmd != BMI_LCD_GETSTAT) { + + // error if slot invalid + if((slot < CPLD_M1) || (slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[slot]->adap; + } + + // ioctl's + switch (cmd) { + case BMI_LCD_RLEDOFF: + bmi_set_module_gpio_data(slot, 3, 1);// Red LED=OFF + break; + case BMI_LCD_RLEDON: + bmi_set_module_gpio_data(slot, 3, 0);// Red LED=ON + break; + case BMI_LCD_GLEDOFF: + bmi_set_module_gpio_data(slot, 2, 1);// Green LED=OFF + break; + case BMI_LCD_GLEDON: + bmi_set_module_gpio_data(slot, 2, 0);// Green LED=ON + break; + case BMI_LCD_VSYNC_DIS: // enable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_VSYNC_EN: // disable VSYNC buffer tristate output + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x08; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_EN: // enable LCD component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_DIS: // disable LCD component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x10; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_EN: // enable Serializer component + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data &= ~0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SER_DIS: // disable Serializer component only + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data |= 0x20; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_SETRST: // overall module reset + bmi_set_module_gpio_data (slot, 1, 0); // RST=0 + break; + case BMI_LCD_CLRRST: // overall module enable + bmi_set_module_gpio_data (slot, 1, 1); // RST=1 + break; + case BMI_LCD_SET_BL: // set backlight brightness + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xFC) | bl; + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + break; + case BMI_LCD_GETSTAT: + { + int *slot = ((int __user *) arg); + int read_data; + + *slot &= 0xF; + + // error if slot invalid + if((*slot < CPLD_M1) || (*slot > CPLD_M4)) + return -ENODEV; + + // error if no lcd in chosen slot + if(pbmi_lcd.bdev[*slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_lcd.bdev[*slot]->adap; + + if(ReadByte_IOX(adap, IOX_INPUT_REG, iox_data)) + return -ENODEV; + + read_data = *iox_data | (bmi_read_gpio_data_reg(*slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + case BMI_LCD_ACTIVATE: //pjg fix/test + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_lcd.activated[2] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + return -ENODEV; + } + break; + case 1: + if(pbmi_lcd.activated[3] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + return -ENODEV; + } + break; + case 2: + if(pbmi_lcd.activated[0] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + return -ENODEV; + } + break; + case 3: + if(pbmi_lcd.activated[1] == 1) { + printk(KERN_INFO "bmi_lcd.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + return -ENODEV; + } + break; + } + // activate + if((!pbmi_lcd.activated[slot]) && (pbmi_lcd.bdev[slot] != 0)) { + bmi_lcd_probe(pbmi_lcd.bdev[slot]); + } + break; + case BMI_LCD_DEACTIVATE: + if(pbmi_lcd.activated[slot]) { + disable_irq_nosync(pbmi_lcd.interrupt[slot]); + pbmi_lcd.activated[slot] = 0; + if(ReadByte_IOX(adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + *iox_data = (*iox_data & 0xF8); + if(WriteByte_IOX(adap, IOX_OUTPUT_REG, *iox_data)) + return -ENODEV; + bmi_slot_power_off(slot); + } + break; + case BMI_LCD_SUSPEND: + printk(KERN_ERR "BMI_LCD_SUSPEND NOT IMPLEMENTED\n"); //pjg + break; + case BMI_LCD_RESUME: + printk(KERN_ERR "BMI_LCD_RESUME NOT IMPLEMENTED\n"); //pjg + break; + default: + return -ENOTTY; + } + return 0; +} +*/ + +static int lcd_ctl_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct lcd_ctl *lcd_ctl; + int slot; + + lcd_ctl = container_of(inode->i_cdev, struct lcd_ctl, cdev); + slot = lcd_ctl->slot; + if (slot < 0) { + return -ENODEV; + } + + + switch (cmd) { + + case BMI_LCD_RLEDOFF: + bmi_slot_gpio_write_bit (slot, 3, 1); //Red LED Off + break; + + case BMI_LCD_RLEDON: + bmi_slot_gpio_write_bit (slot, 3, 0); //Red LED On + break; + + case BMI_LCD_GLEDOFF: + bmi_slot_gpio_write_bit (slot, 2, 1); //Green LED Off + break; + + case BMI_LCD_GLEDON: + bmi_slot_gpio_write_bit (slot, 2, 0); //Green LED On + break; + + default: + printk (KERN_ERR "lcd_ctl_ioctl() - error exit\n"); + return -ENOTTY; + } + + return 0; +} + --- /dev/null +++ git/drivers/bmi/pims/lcd/lcd_ctl.h @@ -0,0 +1,87 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI LCD Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef LCD_CTL_H +#define LCD_CTL_H + +#include + +#include +#include + +#include + + // IOCTL commands for BMI LCD driver +#define BMI_LCD_RLEDOFF _IOW(BMI_LCD_IOCTL, 0x1, __u32) // turn off Red LED +#define BMI_LCD_RLEDON _IOW(BMI_LCD_IOCTL, 0x2, __u32) // turn on Red LED +#define BMI_LCD_GLEDOFF _IOW(BMI_LCD_IOCTL, 0x3, __u32) // turn off Green LED +#define BMI_LCD_GLEDON _IOW(BMI_LCD_IOCTL, 0x4, __u32) // turn on Green LED +#define BMI_LCD_VSYNC_DIS _IOW(BMI_LCD_IOCTL, 0x5, __u32) // Enable VSYNC output buffer +#define BMI_LCD_VSYNC_EN _IOW(BMI_LCD_IOCTL, 0x6, __u32) // Disable VSYNC output buffer +#define BMI_LCD_EN _IOW(BMI_LCD_IOCTL, 0x7, __u32) // Enable LCD component +#define BMI_LCD_DIS _IOW(BMI_LCD_IOCTL, 0x8, __u32) // Disable LCD component +#define BMI_LCD_SER_EN _IOW(BMI_LCD_IOCTL, 0x9, __u32) // Enable Seriallizer component +#define BMI_LCD_SER_DIS _IOW(BMI_LCD_IOCTL, 0xa, __u32) // Disable Seriallizer component +#define BMI_LCD_SETRST _IOW(BMI_LCD_IOCTL, 0xb, __u32) // Disable entire module +#define BMI_LCD_CLRRST _IOW(BMI_LCD_IOCTL, 0xc, __u32) // Enable entire module +#define BMI_LCD_SET_BL _IOW(BMI_LCD_IOCTL, 0xd, __u32) // Set IOX backlight bits [2:0] +#define BMI_LCD_GETSTAT _IOR(BMI_LCD_IOCTL, 0xe, __u32) // Get IOX state +#define BMI_LCD_ACTIVATE _IOW(BMI_LCD_IOCTL, 0xf, __u32) // Activate SER, TS, ACCEL +#define BMI_LCD_DEACTIVATE _IOW(BMI_LCD_IOCTL, 0x10, __u32) // Deactivate SER, TS, ACCEL +#define BMI_LCD_SUSPEND _IOW(BMI_LCD_IOCTL, 0x11, __u32) // Power down module +#define BMI_LCD_RESUME _IOW(BMI_LCD_IOCTL, 0x12, __u32) // Power up module + +/* + * I2C set up + */ + + // I2C Slave Address +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define BMI_ACC_I2C_ADDRESS 0x17 // 7-bit address + + // I2C IOX register addresses +#define IOX_INPUT_REG 0x0 // IOX input data register +#define IOX_OUTPUT_REG 0x1 // IOX output data register +#define IOX_POLARITY_REG 0x2 // IOX polarity data register +#define IOX_CONTROL 0x3 // IOX direction control register +#define IOX_B1 (0) // bit 0 - backlight control +#define IOX_A1_A2 (1) // bit 1 - backlight control +#define IOX_ACC_RST_N (2) // bit 2 - acceleromter reset +#define IOX_VSYNC_EN_N (3) // bit 3 - VSYNC output buffer enable +#define IOX_LCD_RST_N (4) // bit 4 - LCD reset +#define IOX_SERDES_PD_N (5) // bit 5 - SERDES power down +#define IOX_X_INT (6) // bit 6 - accelerometer interrupt +#define IOX_Y_INT (7) // bit 7 - accelerometer interrupt + +struct lcd_ctl +{ + int slot; + struct cdev cdev; + struct device *class_dev; +}; + +extern int lcd_ctl_init (void); +extern void lcd_ctl_clean(void); +extern int lcd_ctl_probe (struct lcd_ctl *lcd_ctl, int slot); +extern void lcd_ctl_remove(struct lcd_ctl *lcd_ctl, int slot); + + +#endif + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/Kconfig @@ -0,0 +1,6 @@ +config BMI_MDACC + tristate "BMI Motion Detector/Accelerometer" + depends on BMI_PIMS + default n + ---help--- + This is the BMI Motion Detector/Acceleromter driver. --- /dev/null +++ git/drivers/bmi/pims/mdacc/Makefile @@ -0,0 +1,9 @@ +# +# BMI PIM: Motion Detector Accelerometer +# + +bmi_mdacc-objs := mdacc.o avr.o md.o acc.o ctl.o mon.o cque.o +obj-$(CONFIG_BMI_MDACC) += bmi_mdacc.o + + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/acc.c @@ -0,0 +1,381 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include "acc.h" +#include "mdacc.h" +#include "mon.h" +#include +#include +#include + +static int acc_open (struct inode *, struct file *); +static int acc_release (struct inode *, struct file *); +static int acc_ioctl (struct inode *, struct file *, unsigned int, unsigned long); +static ssize_t acc_read (struct file *, char __user *, size_t, loff_t *); +static unsigned int acc_poll (struct file *, struct poll_table_struct *); + +struct file_operations acc_fops = { + .owner = THIS_MODULE, + .ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, + .read = acc_read, + .poll = acc_poll, +}; + +static int acc_major; + +int acc_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI MDACC Accelerometer Driver"); + + if (retval) { + return -1; + } + acc_major = MAJOR(dev_id); + return 0; +} + +void acc_clean(void) +{ + dev_t dev_id; + + dev_id = MKDEV(acc_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int acc_probe (struct acc *acc, int slot, struct mon *mon) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + // initialize cdev + + cdev = &acc->cdev; + cdev_init (cdev, &acc_fops); + + dev_id = MKDEV (acc_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + + bmi_class = bmi_get_class (); + + acc->class_dev = device_create (bmi_class, NULL, MKDEV(acc_major, slot), acc, "bmi_mdacc_acc_m%i", slot+1); + + if (IS_ERR(acc->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_mdacc_acc_m%i; errno = %ld\n", + slot+1, PTR_ERR(acc->class_dev)); + acc->class_dev = NULL; + } + + acc->open_flag = 0; + acc->mon = mon; + + + // initialize mdacc_accel_config + + acc->cfg.read_queue_size = 1; + acc->cfg.read_queue_threshold = 1; + acc->cfg.delay_mode = 0; + acc->cfg.delay = 4000; + acc->cfg.delay_resolution = 1; + acc->cfg.sensitivity = 0; + acc->cfg.run = 0; + + + // initialize cque + + acc->cque = cque_create (acc->cfg.read_queue_size, acc->cfg.read_queue_threshold); + + // initialize read_wait_queue + + init_waitqueue_head (&acc->read_wait_queue); + return ret; +} + +void acc_remove (struct acc *acc, int slot) +{ + struct class *bmi_class; + + cque_destroy (acc->cque); + + bmi_class = bmi_get_class (); + device_destroy (bmi_class, MKDEV(acc_major, slot)); + + acc->class_dev = 0; + + cdev_del (&acc->cdev); + + + return; +} + + +static int acc_open (struct inode *inode, struct file *file) +{ + struct acc *acc; + + acc = container_of(inode->i_cdev, struct acc, cdev); + + //Enforce single open behavior + if (acc->open_flag) { + return -EBUSY; + } + acc->open_flag = 1; + + // Save acc_drv pointer for later. + file->private_data = acc; + return 0; +} + +static int acc_release (struct inode *inode, struct file *file) +{ + struct acc *acc; + + acc = container_of(inode->i_cdev, struct acc, cdev); + acc->open_flag = 0; + + //Enforce stop-on-close behavior. + if (acc->cfg.run) { + acc->cfg.run = 0; + mon_stop_accel (acc->mon); + } + return 0; + +} + +static int check_config (struct mdacc_accel_config *config) +{ + int err = 0; + + if (!config) { + err = 1; + goto exit; + } + + if (config->read_queue_size < 1) { + err = 1; + } + if (config->read_queue_threshold > config->read_queue_size) { + err = 1; + } + if (config->delay_mode) { + switch (config->delay_resolution) + { + case 0: + err = 1; + break; + case 1: // 1 => 1 usec + if (config->delay < 5000) { + err = 1; + } + break; + case 2: + if (config->delay < 625) { // 2 => 8 usec + err = 1; + } + break; + case 3: + if (config->delay < 79) { // 3 => 64 usec + err = 1; + } + break; + case 4: + if (config->delay < 20) { // 4 => 256 usec + err = 1; + } + break; + case 5: + if (config->delay < 5) { // 5 => 1024 usec + err = 1; + } + break; + default: + err = 1; + break; + } + } + + if (config->sensitivity > 3) { + err = 1; + } + +exit: + return err; +} + +static int acc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct acc *acc; + acc = container_of(inode->i_cdev, struct acc, cdev); + + switch (cmd) { + + case BMI_MDACC_ACCELEROMETER_SET_CONFIG: + { + struct mdacc_accel_config new_cfg; + + err = copy_from_user ( (void*)&new_cfg, (void*)arg, sizeof (struct mdacc_accel_config) ); + if (err) { + return -EFAULT; + } + + err = check_config (&new_cfg); + if (err) { + return -EINVAL; + } + + if (acc->cfg.run) { + mon_stop_accel (acc->mon); + } + + // take the mon semaphore + down_interruptible (&acc->mon->sem); + + memcpy ( &acc->cfg, &new_cfg, sizeof (struct mdacc_accel_config) ); + cque_destroy (acc->cque); + acc->cque = cque_create (acc->cfg.read_queue_size, acc->cfg.read_queue_threshold); + + // release the mon semaphore + up (&acc->mon->sem); + + mon_set_config_accel( acc->mon, &acc->cfg); + + } + break; + + case BMI_MDACC_ACCELEROMETER_GET_CONFIG: + + mon_get_config_accel( acc->mon, &acc->cfg); + + + err = copy_to_user ( (void*)arg, &acc->cfg, sizeof (struct mdacc_accel_config) ); + if (err) { + return -EFAULT; + } + break; + + case BMI_MDACC_ACCELEROMETER_RUN: + + acc->cfg.run = 1; + err = mon_start_accel (acc->mon); + if (err){ + return -ENODEV; + } + break; + + + case BMI_MDACC_ACCELEROMETER_STOP: + + acc->cfg.run = 0; + err = mon_stop_accel (acc->mon); + if (err){ + return -ENODEV; + } + break; + + default: + printk (KERN_ERR "acc_ioctl() - error exit\n"); + return -ENOTTY; + } + + return 0; +} + +static ssize_t acc_read (struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ +// if fd is non-blocking and acc is not enabled, return EAGAIN +// if fd is non-blocking and acc is enabled, and cque is not ready, return EAGAIN +// if fd is non-blocking and acc is enabled, and cque is ready, read cque, copy to user, + +// if fd is blocking and acc is not enabled, , return EAGAIN +// + +// if fs is blocking and acc is enabled, and cque is not ready, sleep until ready. +// when ready, read cque, copy to user, + + + + struct acc *acc = file->private_data; + unsigned char temp[6]; + int bytes_read; + + if (count < 6) { + return -EINVAL; + } + + if (!acc->cfg.run) { + return -EAGAIN; + } + + while (!cque_is_ready_for_read(acc->cque)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible (acc->read_wait_queue, (cque_is_ready_for_read(acc->cque)))) + return -ERESTARTSYS; + } + + //loop through 1 sample at a time. + + bytes_read = 0; + + while (count >= 6) { + + if (cque_read (acc->cque, &temp)) + break; + if (copy_to_user (buf, &temp, 6)) + break; + bytes_read += 6; + buf += 6; + count -= 6; + } + return bytes_read; +} + + +static unsigned int acc_poll (struct file *file, struct poll_table_struct *table) +{ + unsigned int mask = 0; + struct acc *acc = file->private_data; + + poll_wait(file, &acc->read_wait_queue, table); + + if (cque_is_ready_for_read( acc->cque) ) { + mask |= POLLIN | POLLRDNORM; /* readable */ + } + if (mdacc_check_bdev_acc (acc) ) { + mask |= POLLHUP; /* hang-up */ + } + if (!acc->cfg.run) { + mask |= POLLHUP; /* hang-up */ + } + return mask; +} + --- /dev/null +++ git/drivers/bmi/pims/mdacc/acc.h @@ -0,0 +1,54 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef MDACC_ACC_H +#define MDACC_ACC_H + +#include +#include +#include +#include +#include +#include +#include +#include "cque.h" + +struct mon; + +struct acc +{ + struct cdev cdev; + struct device *class_dev; + int open_flag; + struct mon *mon; + struct mdacc_accel_config cfg; + struct cque *cque; + wait_queue_head_t read_wait_queue; +}; + + +extern int acc_init (void); +extern void acc_clean(void); +extern int acc_probe (struct acc *acc, int slot, struct mon *mon); +extern void acc_remove(struct acc *acc, int slot); + + + + +#endif --- /dev/null +++ git/drivers/bmi/pims/mdacc/avr.c @@ -0,0 +1,511 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include +#include + + +struct avr_regs +{ + unsigned char timer_res; // SPI Register 0 + unsigned char timer_msb; // SPI Register 1 + unsigned char timer_lsb; // SPI Register 2 + unsigned char mode; // SPI Register 3 + + unsigned char status; // SPI Register 4 + + unsigned char adc0h; // SPI Register 5 + unsigned char adc0l; // SPI Register 6 + unsigned char adc1h; // SPI Register 7 + unsigned char adc1l; // SPI Register 8 + unsigned char adc2h; // SPI Register 9 + unsigned char adc2l; // SPI Register 10 +}; + + + +/* + +Cmd Bit Definitions + +Cmd bit 7 - R0/W1 +Cmd bit 6 - Cnt 2 +Cmd bit 5 - Cnt 1 +Cmd bit 4 - Cnt 0 +Cmd bit 3 - Address 3 +Cmd bit 2 - Address 2 +Cmd bit 1 - Address 1 +Cmd bit 0 - Address 0 +*/ + + + +#define AVR_CMD_READ_ADC (0x65) +#define AVR_CMD_READ_STATUS (0x14) +#define AVR_CMD_READ_STATUS_AND_ADC (0x74) +#define AVR_CMD_READ_MODE (0x13) +#define AVR_CMD_READ_TIMER (0x30) +#define AVR_CMD_READ_TIMER_AND_MODE (0x40) +#define AVR_CMD_WRITE_MODE (0x93) +#define AVR_CMD_WRITE_TIMER (0xB0) +#define AVR_CMD_WRITE_TIMER_AND_MODE (0xC0) + +int avr_read_adc (struct spi_device *spi, struct avr_regs *regs); +int avr_read_status (struct spi_device *spi, struct avr_regs *regs); +int avr_read_status_and_adc (struct spi_device *spi, struct avr_regs *regs); +int avr_read_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_read_timer (struct spi_device *spi, struct avr_regs *regs); +int avr_read_timer_and_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_write_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_write_timer (struct spi_device *spi, struct avr_regs *regs); +int avr_write_timer_and_mode (struct spi_device *spi, struct avr_regs *regs); + + +int avr_read_adc (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + + struct spi_transfer x[7]; + struct spi_message message; + + cmd = AVR_CMD_READ_ADC; + sync = 0; + + dont_care = 0; + + spi_message_init(&message); + memset(x, 0, sizeof x); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + x[0].bits_per_word = 8; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->adc0h; + x[1].delay_usecs = 400; + x[1].bits_per_word = 8; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = &dont_care; + x[2].rx_buf = ®s->adc0l; + x[2].delay_usecs = 400; + x[2].bits_per_word = 8; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = &dont_care; + x[3].rx_buf = ®s->adc1h; + x[3].delay_usecs = 400; + x[3].bits_per_word = 8; + spi_message_add_tail(&x[3], &message); + + x[4].len = 1; + x[4].tx_buf = &dont_care; + x[4].rx_buf = ®s->adc1l; + x[4].delay_usecs = 400; + x[4].bits_per_word = 8; + spi_message_add_tail(&x[4], &message); + + x[5].len = 1; + x[5].tx_buf = &dont_care; + x[5].rx_buf = ®s->adc2h; + x[5].delay_usecs = 400; + x[5].bits_per_word = 8; + spi_message_add_tail(&x[5], &message); + + x[6].len = 1; + x[6].tx_buf = &dont_care; + x[6].rx_buf = ®s->adc2l; + x[6].delay_usecs = 400; + x[6].bits_per_word = 8; + spi_message_add_tail(&x[6], &message); + + err = spi_sync(spi, &message); + + return err; +} + + +int avr_read_status (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + struct spi_transfer x[2]; + struct spi_message message; + + cmd = AVR_CMD_READ_STATUS; + sync = 0; + + dont_care = 0;; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->status; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_read_status_and_adc (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + struct spi_transfer x[8]; + struct spi_message message; + + cmd = AVR_CMD_READ_STATUS_AND_ADC; + sync = 0; + dont_care = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->status; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = &dont_care; + x[2].rx_buf = ®s->adc0h; + x[2].delay_usecs = 400; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = &dont_care; + x[3].rx_buf = ®s->adc0l; + x[3].delay_usecs = 400; + spi_message_add_tail(&x[3], &message); + + x[4].len = 1; + x[4].tx_buf = &dont_care; + x[4].rx_buf = ®s->adc1h; + x[4].delay_usecs = 400; + spi_message_add_tail(&x[4], &message); + + x[5].len = 1; + x[5].tx_buf = &dont_care; + x[5].rx_buf = ®s->adc1l; + x[5].delay_usecs = 400; + spi_message_add_tail(&x[5], &message); + + x[6].len = 1; + x[6].tx_buf = &dont_care; + x[6].rx_buf = ®s->adc2h; + x[6].delay_usecs = 400; + spi_message_add_tail(&x[6], &message); + + x[7].len = 1; + x[7].tx_buf = &dont_care; + x[7].rx_buf = ®s->adc2l; + x[7].delay_usecs = 400; + spi_message_add_tail(&x[7], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_read_mode (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + + struct spi_transfer x[2]; + struct spi_message message; + + + cmd = AVR_CMD_READ_MODE; + sync = 0; + + dont_care = 0; + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->mode; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_read_timer (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + struct spi_transfer x[4]; + struct spi_message message; + + + cmd = AVR_CMD_READ_TIMER; + sync = 0; + dont_care = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->timer_res; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = &dont_care; + x[2].rx_buf = ®s->timer_msb; + x[2].delay_usecs = 400; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = &dont_care; + x[3].rx_buf = ®s->timer_lsb; + x[3].delay_usecs = 400; + spi_message_add_tail(&x[3], &message); + + err = spi_sync(spi, &message); + + return err; +} +int avr_read_timer_and_mode (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + unsigned char dont_care; + struct spi_transfer x[5]; + struct spi_message message; + + + cmd = AVR_CMD_READ_TIMER_AND_MODE; + sync = 0; + dont_care = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = &dont_care; + x[1].rx_buf = ®s->timer_res; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = &dont_care; + x[2].rx_buf = ®s->timer_msb; + x[2].delay_usecs = 400; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = &dont_care; + x[3].rx_buf = ®s->timer_lsb; + x[3].delay_usecs = 400; + spi_message_add_tail(&x[3], &message); + + x[4].len = 1; + x[4].tx_buf = &dont_care; + x[4].rx_buf = ®s->mode; + x[4].delay_usecs = 400; + spi_message_add_tail(&x[4], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_write_mode (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + struct spi_transfer x[2]; + struct spi_message message; + + cmd = AVR_CMD_WRITE_MODE; + sync = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = ®s->mode; + x[1].rx_buf = 0; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_write_timer (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + struct spi_transfer x[4]; + struct spi_message message; + + cmd = AVR_CMD_WRITE_TIMER; + sync = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = ®s->timer_res; + x[1].rx_buf = 0; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = ®s->timer_msb; + x[2].rx_buf = 0; + x[2].delay_usecs = 400; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = ®s->timer_lsb; + x[3].rx_buf = 0; + x[3].delay_usecs = 400; + spi_message_add_tail(&x[3], &message); + + err = spi_sync(spi, &message); + + return err; +} + +int avr_write_timer_and_mode (struct spi_device *spi, struct avr_regs *regs) +{ + int err; + unsigned char cmd; + unsigned char sync; + struct spi_transfer x[5]; + struct spi_message message; + + + cmd = AVR_CMD_WRITE_TIMER_AND_MODE; + sync = 0; + + memset(x, 0, sizeof x); + spi_message_init(&message); + + x[0].len = 1; + x[0].tx_buf = &cmd; + x[0].rx_buf = &sync; + x[0].delay_usecs = 400; + spi_message_add_tail(&x[0], &message); + + x[1].len = 1; + x[1].tx_buf = ®s->timer_res; + x[1].rx_buf = 0; + x[1].delay_usecs = 400; + spi_message_add_tail(&x[1], &message); + + x[2].len = 1; + x[2].tx_buf = ®s->timer_msb; + x[2].rx_buf = 0; + x[2].delay_usecs = 400; + spi_message_add_tail(&x[2], &message); + + x[3].len = 1; + x[3].tx_buf = ®s->timer_lsb; + x[3].rx_buf = 0; + x[3].delay_usecs = 400; + spi_message_add_tail(&x[3], &message); + + x[4].len = 1; + x[4].tx_buf = ®s->mode; + x[4].rx_buf = 0; + x[4].delay_usecs = 400; + spi_message_add_tail(&x[4], &message); + + err = spi_sync(spi, &message); + + return err; +} + --- /dev/null +++ git/drivers/bmi/pims/mdacc/avr.h @@ -0,0 +1,54 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ +#ifndef MDACC_AVR_H +#define MDACC_AVR_H + +#include +#include + +struct avr_regs +{ + unsigned char timer_res; // SPI Register 0 + unsigned char timer_msb; // SPI Register 1 + unsigned char timer_lsb; // SPI Register 2 + unsigned char mode; // SPI Register 3 + + unsigned char status; // SPI Register 4 + + unsigned char adc0h; // SPI Register 5 + unsigned char adc0l; // SPI Register 6 + unsigned char adc1h; // SPI Register 7 + unsigned char adc1l; // SPI Register 8 + unsigned char adc2h; // SPI Register 9 + unsigned char adc2l; // SPI Register 10 +}; + +int avr_read_adc (struct spi_device *spi, struct avr_regs *regs); +int avr_read_status (struct spi_device *spi, struct avr_regs *regs); +int avr_read_status_and_adc (struct spi_device *spi, struct avr_regs *regs); +int avr_read_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_read_timer (struct spi_device *spi, struct avr_regs *regs); +int avr_read_timer_and_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_write_mode (struct spi_device *spi, struct avr_regs *regs); +int avr_write_timer (struct spi_device *spi, struct avr_regs *regs); +int avr_write_timer_and_mode (struct spi_device *spi, struct avr_regs *regs); + +#endif + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/cque.c @@ -0,0 +1,150 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include "cque.h" +#include +#include + +struct cque *cque_create (int num_entries, int threshold) +{ + + int entry_size; + struct cque *cque; + void *buf_base; + int buf_size; + + entry_size = 6; + cque = 0; + buf_size = entry_size * num_entries; + if (!buf_size) { + return 0; + } + + if (( threshold > num_entries) || + ( threshold < 1)) { + return 0; + } + + cque = kmalloc (sizeof(cque), GFP_KERNEL); + if (!cque) { + return 0; + } + + buf_base = kmalloc (buf_size, GFP_KERNEL); + if (!buf_base) { + kfree (cque); + return 0; + } + + cque->entry_size = entry_size; + cque->num_entries = num_entries; + cque->entry_cnt = 0; + cque->threshold = threshold; + cque->start = buf_base; + cque->end = buf_base + buf_size - entry_size; + cque->top = buf_base; + cque->bot = buf_base; + + return cque; +} + +void cque_destroy (struct cque *cque) +{ + + if (cque) { + kfree (cque->start); + kfree (cque); + } + return; + +} + +int cque_write (struct cque *cque, void *data) +{ + int size; + int err; + + size = 6; + err = 1; + if (cque) { + //insert + + memcpy (cque->top, data, size); + cque->top += size; + if (cque->top > cque->end) { + cque->top = cque->start; + } + + + if (cque->entry_cnt < cque->num_entries) { + cque->entry_cnt++; + } + else { + cque->bot += size; + if (cque->bot > cque->end) { + cque->bot = cque->start; + } + } + err = 0; + } + return err; + +} + +int cque_read (struct cque *cque, void *data ) +{ + int size; + int err; + + size = 6; + err = 1; + if (cque) { + + if (cque->entry_cnt) { + + //remove + + //memcpy (data, cque->bot, size) with swab ; + + *((short*)(data)) = ntohs (*((short*)(cque->bot))); + *((short*)(data+2)) = ntohs (*((short*)(cque->bot+2))); + *((short*)(data+4)) = ntohs (*((short*)(cque->bot+4))); + cque->bot += size; + if (cque->bot > cque->end) { + cque->bot = cque->start; + } + cque->entry_cnt--; + err = 0; + } + } + return err; +} + +int cque_is_ready_for_read (struct cque *cque) +{ + int ready; + + ready = 0; + if ((cque) && (cque->entry_cnt >= cque->threshold)) { + ready = 1; + } + return ready; +} + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/cque.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ +#ifndef MDACC_CQUE_H +#define MDACC_CQUE_H + +struct cque +{ + int entry_size; + int num_entries; + int entry_cnt; + int threshold; + + void *start; + void *end; + void *top; + void *bot; +}; + +struct cque *cque_create (int num_entries, int threshold); +void cque_destroy (struct cque *cque); + +int cque_write (struct cque *cque, void *data); +int cque_read (struct cque *cque, void *data); +int cque_is_ready_for_read (struct cque *cque); + +#endif --- /dev/null +++ git/drivers/bmi/pims/mdacc/ctl.c @@ -0,0 +1,176 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include "ctl.h" +#include "mdacc.h" +#include +#include +#include +#include + + +#include + +static int ctl_open (struct inode *, struct file *); +static int ctl_release (struct inode *, struct file *); +static int ctl_ioctl (struct inode *, struct file *, unsigned int, unsigned long); + +struct file_operations ctl_fops = { + .owner = THIS_MODULE, + .ioctl = ctl_ioctl, + .open = ctl_open, + .release = ctl_release, +}; + +static int ctl_major; + +int ctl_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI MDACC Control Driver"); + + if (retval) { + return -1; + } + ctl_major = MAJOR(dev_id); + return 0; +} + +void ctl_clean (void) +{ + dev_t dev_id; + + dev_id = MKDEV(ctl_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int ctl_probe (struct ctl *ctl, int slot) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + cdev = &ctl->cdev; + cdev_init (cdev, &ctl_fops); + + dev_id = MKDEV (ctl_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + bmi_class = bmi_get_class (); + + ctl->class_dev = device_create (bmi_class, NULL, MKDEV(ctl_major, slot), ctl, "bmi_mdacc_ctl_m%i", slot+1); + + if (IS_ERR(ctl->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_mdacc_ctl_m%i; errno = %ld\n", + slot+1, PTR_ERR(ctl->class_dev)); + ctl->class_dev = NULL; + } + + return ret; +} + +void ctl_remove (struct ctl *ctl, int slot) +{ + struct class *bmi_class; + + bmi_class = bmi_get_class (); + device_destroy (bmi_class, MKDEV(ctl_major, slot)); + + ctl->class_dev = 0; + + cdev_del (&ctl->cdev); + return; +} + + +static int ctl_open (struct inode *inode, struct file *file) +{ + struct ctl *ctl; + + ctl = container_of(inode->i_cdev, struct ctl, cdev); + + + // Save ctl pointer for later. + + file->private_data = ctl; + return 0; +} + +static int ctl_release (struct inode *inode, struct file *file) +{ + struct ctl *ctl; + + ctl = container_of(inode->i_cdev, struct ctl, cdev); + return 0; +} + +static int ctl_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ctl *ctl; + int slot; + unsigned char tmp = 0; + + ctl = container_of(inode->i_cdev, struct ctl, cdev); + slot = mdacc_get_slot_ctl (ctl); + if (slot < 0) { + return -ENODEV; + } + + switch (cmd) { + + case BMI_MDACC_CTL_RED_LED_OFF: + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp | RED_LED); + //bmi_slot_gpio_write_bit (slot, 3, 1); //Red LED Off + break; + + case BMI_MDACC_CTL_RED_LED_ON: + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp & ~RED_LED); + //bmi_slot_gpio_write_bit (slot, 3, 0); //Red LED On + break; + + case BMI_MDACC_CTL_GREEN_LED_OFF: + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp | GREEN_LED); + //bmi_slot_gpio_write_bit (slot, 2, 1); //Green LED Off + break; + + case BMI_MDACC_CTL_GREEN_LED_ON: + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp & ~GREEN_LED); + //bmi_slot_gpio_write_bit (slot, 2, 0); //Green LED On + break; + + default: + printk (KERN_ERR "ctl_ioctl() - error exit\n"); + return -ENOTTY; + } + + return 0; +} + --- /dev/null +++ git/drivers/bmi/pims/mdacc/ctl.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef MDACC_CTL_H +#define MDACC_CTL_H + +#include + +#include +#include + + +struct ctl +{ + struct cdev cdev; + struct device *class_dev; +}; + +extern int ctl_init (void); +extern void ctl_clean(void); +extern int ctl_probe (struct ctl *ctl, int slot); +extern void ctl_remove(struct ctl *ctl, int slot); + + +#endif + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/md.c @@ -0,0 +1,333 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include "md.h" +#include "mdacc.h" +#include "mon.h" +#include +#include +#include + +static int md_open (struct inode *, struct file *); +static int md_release (struct inode *, struct file *); +static int md_ioctl (struct inode *, struct file *, unsigned int, unsigned long); +static ssize_t md_read (struct file *, char __user *, size_t, loff_t *); +static unsigned int md_poll (struct file *, struct poll_table_struct *); + +struct file_operations md_fops = { + .owner = THIS_MODULE, + .ioctl = md_ioctl, + .open = md_open, + .release = md_release, + .read = md_read, + .poll = md_poll, +}; + +static int md_major; + +#define BMI_MOTION_DETECT_MASK (BMI_MOTION_DETECT_STATUS | \ + BMI_MOTION_DETECT_LATCHED_STATUS | \ + BMI_MOTION_DETECT_DELTA | \ + BMI_MOTION_DETECT_ENABLED) + + + +void md_update (struct md *md, char data) +{ + + unsigned char old_delta; + unsigned char new_delta; + unsigned char merge_bits; + unsigned char new_bits; + + + // Delta and Latched Status Bit Handling. + + // MOTION_STATUS Bit 3 + // LATCHED_STATUS Bit 2 + // DELTA Bit 1 + // ENABLED Bit 0 + + + // Handle bits individually + + old_delta = md->status & 0x02; + new_delta = data & 0x02; + + if (!new_delta) { + //preserve old latch bit, update delta bit + merge_bits = (md->status & 0x06) | (data & 0x02); + } + else { + //update latch bit and delta bit + merge_bits = (md->status & 0x06) | (data & 0x06); + } + new_bits = data & 0x09; + md->status = merge_bits | new_bits; + + if (!old_delta && new_delta) { + md->ready = 1; + //wake up anyone sleeping on our read wait queue + wake_up_interruptible (&md->read_wait_queue); + + } + return; +} + + +void md_clear_status (struct md *md) +{ + + md->status &= ~(BMI_MOTION_DETECT_LATCHED_STATUS | + BMI_MOTION_DETECT_DELTA); + + md->ready = 0; + return; + +} + + +int md_init (void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI MDACC Motion Detector Driver"); + + if (retval) { + return -1; + } + md_major = MAJOR(dev_id); + return 0; +} + +void md_clean (void) +{ + dev_t dev_id; + + dev_id = MKDEV(md_major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +int md_probe (struct md *md, int slot, struct mon *mon) +{ + struct cdev *cdev; + dev_t dev_id; + int ret; + struct class *bmi_class; + + md->removed = 0; + + cdev = &md->cdev; + cdev_init (cdev, &md_fops); + + dev_id = MKDEV (md_major, slot); + ret = cdev_add (cdev, dev_id, 1); + + //Create class device + bmi_class = bmi_get_class (); + + md->class_dev = device_create (bmi_class, NULL, MKDEV(md_major, slot), md, "bmi_mdacc_mot_m%i", slot+1); + + if (IS_ERR(md->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_mdacc_mot_m%i; errno = %ld\n", + slot+1, PTR_ERR(md->class_dev)); + md->class_dev = NULL; + } + + md->open_flag = 0; + md->enabled = 0; + md->status = 0; + init_waitqueue_head (&md->read_wait_queue); + md->mon = mon; + return ret; +} + +void md_remove (struct md *md, int slot ) +{ + struct class *bmi_class; + + md->removed = 1; + md->ready = -1; + wake_up_interruptible (&md->read_wait_queue); + + cdev_del (&md->cdev); + bmi_class = bmi_get_class (); + device_destroy (bmi_class, MKDEV(md_major, slot)); + md->class_dev = 0; + return; +} + + +static int md_open (struct inode *inode, struct file *file) +{ + struct md *md; + + md = container_of(inode->i_cdev, struct md, cdev); + + //Enforce single open behavior + if (md->open_flag) { + return -EBUSY; + } + md->open_flag = 1; + + // Save md_drv pointer for later. + file->private_data = md; + return 0; +} + +static int md_release (struct inode *inode, struct file *file) +{ + struct md *md; + + md = container_of(inode->i_cdev, struct md, cdev); + md->open_flag = 0; + + //Enforce stop-on-close behavior. + if (md->enabled) { + mon_stop_motion (md->mon); + } + return 0; +} + +static int md_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct md *md; + md = container_of(inode->i_cdev, struct md, cdev); + + switch (cmd) { + + case BMI_MDACC_MOTION_DETECTOR_GET_STATUS: + + err = mon_status_request (md->mon); + if (err){ + return -ENODEV; + } + + //copy to user + err = copy_to_user((void*)(arg), &md->status, sizeof(md->status)); + md_clear_status (md); + if (err) { + return -EFAULT; + } + break; + + + case BMI_MDACC_MOTION_DETECTOR_RUN: + + err = mon_start_motion (md->mon); + + if (err){ + return -ENODEV; + } + mon_status_request (md->mon); + md->enabled = 1; + break; + + + case BMI_MDACC_MOTION_DETECTOR_STOP: + + err = mon_stop_motion (md->mon); + if (err){ + return -ENODEV; + } + + break; + + default: + printk (KERN_ERR "md_ioctl() - error exit\n"); + return -ENOTTY; + } + return 0; +} + +static ssize_t md_read (struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + +// +// if fd is non-blocking and md is not enabled, return EWOULDBLOCK +// if fd is non-blocking and md is enabled, and md is not ready, return EWOULDBLOCK +// if fd is non-blocking and md is enabled, and md is ready, copy md->status to user, md_clear_status(), + +// if fd is blocking and md is not enabled, avr_read_status, +// md_update, copy md->status to user, md_clear_status() + +// if fs is blocking and md is enabled, and md is not ready, sleep until ready. +// when ready, copy md->status to user, md_clear_status() + + + int err; + struct md *md = file->private_data; + + + if (!md->enabled) { + return -EAGAIN; + } + + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + + while (!md->ready) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible (md->read_wait_queue, (md->ready))) + return -ERESTARTSYS; + + if(md->removed) { + return -1; + } + } + + err = copy_to_user (buf, &md->status, 1); + md_clear_status (md); + if (err) { + return -EFAULT; + } + return 1; +} + +static unsigned int md_poll (struct file *file, struct poll_table_struct *table) +{ + unsigned int mask = 0; + struct md *md = file->private_data; + + poll_wait(file, &md->read_wait_queue, table); + + if (md->ready) { + mask |= POLLIN | POLLRDNORM; /* readable */ + } + + if (mdacc_check_bdev_md (md) ) { + mask |= POLLHUP; /* hang-up */ + } + + if (!md->enabled) { + mask |= POLLHUP; /* hang-up */ + } + return mask; +} + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/md.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef MDACC_MD_H +#define MDACC_MD_H + +#include +#include + +#include +#include + +#include +#include + +#include + +struct mon; + +struct md +{ + struct cdev cdev; + struct device *class_dev; + int open_flag; + int enabled; + unsigned char status; + int ready; + wait_queue_head_t read_wait_queue; + struct mon *mon; + u8 removed; +}; + + +extern int md_init (void); +extern void md_clean(void); +extern int md_probe (struct md *md, int slot, struct mon *mon); +extern void md_remove(struct md *md, int slot); + + +void md_update ( struct md *md, char data); +void md_clear_status (struct md *md ); + + +#endif --- /dev/null +++ git/drivers/bmi/pims/mdacc/mdacc.c @@ -0,0 +1,333 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + * This kernel module contains the device drivers for the Bug MDACC Plug-In + * Module. Refer to include/linux/bmi/bmi_mdacc.h for user-level device driver + * programming information. + *------------------------------------------------------------------------------ + */ + +#include +#include + +#include +#include + +#include +#include + +#include + +#define BMI_MDACC_VERSION "1.0" // driver version + + +#include "md.h" +#include "acc.h" +#include "ctl.h" +#include "mon.h" +#include "avr.h" +#include "cque.h" +#include "mdacc.h" + + +// private device structure +struct pim +{ + struct bmi_device *bdev; + struct ctl ctl; + struct md md; + struct acc acc; + struct mon mon; + char *name; +}; + +static struct pim bug_mdacc_pim[4]; + +// BMI device ID table +static struct bmi_device_id bmi_mdacc_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_MOT_ACCEL, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_mdacc_tbl); + +int bmi_mdacc_probe(struct bmi_device *bdev); +void bmi_mdacc_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_mdacc_driver = +{ + .name = "bmi_mdacc", + .id_table = bmi_mdacc_tbl, + .probe = bmi_mdacc_probe, + .remove = bmi_mdacc_remove, +}; + + +// Support functions + +int mdacc_get_slot_mon (struct mon *mon) +{ + struct pim *pim; + int slot; + + pim = container_of(mon, struct pim, mon); + + if (pim->bdev == 0) { + slot = -1; + } + else { + slot = pim->bdev->slot->slotnum; + } + return slot; +} + +int mdacc_get_slot_ctl (struct ctl *ctl) +{ + struct pim *pim; + int slot; + + pim = container_of(ctl, struct pim, ctl); + + if (pim->bdev == 0) { + slot = -1; + } + else { + slot = pim->bdev->slot->slotnum; + } + return slot; +} + + +struct spi_device* mdacc_get_spi_mon (struct mon *mon) +{ + struct pim *pim; + struct spi_device *spi = 0; + + /* + pim = container_of(mon, struct pim, mon); + + if (pim->bdev) { + spi = pim->bdev->slot->slotnum; + } + */ + return spi; +} + +struct bmi_device* mdacc_get_bdev_mon (struct mon *mon) +{ + struct pim *pim; + + pim = container_of(mon, struct pim, mon); + return pim->bdev; +} + +int mdacc_check_bdev_md (struct md *md) +{ + int err; + struct pim *pim; + + err = 0; + pim = container_of(md, struct pim, md); + if (!pim->bdev) { + err = 1; + } + return err; +} + +int mdacc_check_bdev_acc (struct acc *acc) +{ + int err; + struct pim *pim; + + err = 0; + pim = container_of(acc, struct pim, acc); + if (!pim->bdev) { + err = 1;; + } + return err; +} + +// BMI Functions + +int bmi_mdacc_probe(struct bmi_device *bdev) +{ + int slot; + struct pim *pim; + int irq; + unsigned char tmp = 0; + + // Module GPIO use: + // 0 1 + // GPIO 3 Red LED On Off + // GPIO 2 Green LED On Off + // GPIO 1 AVR Reset Reset Normal Operation + // GPIO 0 Accel. Sleep Mode Sleep Normal Operation + + slot = bdev->slot->slotnum; + pim = &bug_mdacc_pim[slot]; + + bmi_device_set_drvdata(bdev, pim); + pim->bdev = bdev; + + + // Setup GPIOs for this slot + + bmi_slot_gpio_configure(slot, RED_LED | GREEN_LED | GPIO_1 | GPIO_0); //Red LED: On + bmi_slot_gpio_set(slot, GPIO_0); + + bmi_slot_spi_enable(slot); + + //AVR Reset Active time + mdelay(1); + + + //Take AVR out of reset + bmi_slot_gpio_set(slot, GPIO_1 | GPIO_0); + + //AVR Reset Recovery time + + mdelay (100); + + + switch (slot) { + case 0: + pim->name = "mdacc_m1"; + break; + case 1: + pim->name = "mdacc_m2"; + break; + case 2: + pim->name = "mdacc_m3"; + break; + case 3: + pim->name = "mdacc_m4"; + break; + } + + irq = bdev->slot->status_irq; + + if (mon_probe (&pim->mon, pim->name, irq, &pim->md, &pim->acc) ) { + printk (KERN_ERR "bmi_mdacc_probe() - mon_probe() failed.\n"); + goto exit1; + } + if (md_probe (&pim->md, slot, &pim->mon) ) { + printk (KERN_ERR "bmi_mdacc_probe() - md_probe() failed.\n"); + goto exit2; + } + if (acc_probe (&pim->acc, slot, &pim->mon) ) { + printk (KERN_ERR "bmi_mdacc_probe() - acc_probe() failed.\n"); + goto exit3; + } + if (ctl_probe (&pim->ctl, slot) ) { + printk (KERN_ERR "bmi_mdacc_probe() - ctl_probe() failed.\n"); + goto exit4; + } + + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp | RED_LED); //Red + Green LEDs Off + return 0; + +exit4: + acc_remove (&pim->acc, slot); +exit3: + md_remove (&pim->md, slot); +exit2: + mon_remove (&pim->mon); + +exit1: + bmi_slot_spi_disable(slot); + bmi_device_set_drvdata (bdev, 0); + pim->bdev = 0; + // bmi_slot_gpio_write_bit (slot, 2, 1); //Green LED Off + tmp = bmi_slot_gpio_get(slot); + bmi_slot_gpio_set (slot, tmp | GREEN_LED); + return -1; +} + +void bmi_mdacc_remove(struct bmi_device *bdev) +{ + int slot; + struct pim *pim; + + slot = bdev->slot->slotnum; + pim = &bug_mdacc_pim[slot]; + + ctl_remove (&pim->ctl, slot); + acc_remove (&pim->acc, slot); + md_remove (&pim->md, slot); + mon_remove (&pim->mon); + + bmi_slot_spi_disable(slot); + + bmi_device_set_drvdata (bdev, 0); +#if 0 + pim->bdev = 0; +#endif + + return; +} + + + +static __init int bmi_mdacc_init(void) +{ + int rc = 0; + + acc_init(); + md_init(); + ctl_init(); + +// Register with BMI bus. + rc = bmi_register_driver(&bmi_mdacc_driver); + if(rc) { + printk(KERN_ERR "bmi_mdacc_init() - bmi_register_driver failed\n"); + return rc; + } + + printk("BMI MDACC Driver v%s \n", BMI_MDACC_VERSION); + return 0; +} + +static void __exit bmi_mdacc_clean(void) +{ +// Unregister with BMI bus. + bmi_unregister_driver(&bmi_mdacc_driver); + + ctl_clean(); + md_clean(); + acc_clean(); + return; +} + +module_init(bmi_mdacc_init); +module_exit(bmi_mdacc_clean); + +MODULE_AUTHOR("EnCADIS Design, Inc."); +MODULE_DESCRIPTION("BMI Motion Detector/Accelerometer Driver"); +MODULE_LICENSE("GPL"); + + + + + + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/mdacc.h @@ -0,0 +1,43 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef MDACC_H +#define MDACC_H + +struct acc; +struct ctl; +struct md; +struct mon; +struct spi_device; +struct bmi_device; + +extern int mdacc_get_slot_mon (struct mon *mon); +extern int mdacc_get_slot_ctl (struct ctl *ctl); + +extern struct bmi_device* mdacc_get_bdev_mon (struct mon *mon); +extern struct spi_device* mdacc_get_spi_mon (struct mon *mon); + +int mdacc_check_bdev_md (struct md *md); +int mdacc_check_bdev_acc (struct acc *acc); + +struct mon *mdacc_get_mon_md(struct md *md); +struct mon *mdacc_get_mon_acc(struct acc *acc); + +#endif + --- /dev/null +++ git/drivers/bmi/pims/mdacc/mon.c @@ -0,0 +1,474 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#include "mon.h" +#include "mdacc.h" +#include + +#include + +#define work_to_mon(w) container_of(w, struct mon, work_item) + +enum +{ + MON_STATE_IDLE, + MON_STATE_MOTION_ONLY, + MON_STATE_ACC_ONLY, + MON_STATE_MOTION_AND_ACC +}; + +enum +{ + MON_ACTION_START_ACC, + MON_ACTION_START_MOTION, + MON_ACTION_STOP_ACC, + MON_ACTION_STOP_MOTION +}; + + + + +/* + Spi Mode Register + -------------------------------- + Timer Enable Bit 4 + ADC Poll Enable Bit 3 + Motion Detect Enable Bit 2 + GSEL1:0 Bits 1:0 + + + SPI Status Register + ------------------------------------ + Not used Bit 7 + Timer Enabled Bit 6 + Adc Complete Bit 5 + Adc Enabled Bit 4 + Motion Detect Status Bit 3 + Motion Detect Latched Status Bit 2 + Motion Detect Delta Bit 1 + Motion Detect Enabled Bit 0 + + + +*/ + +static void mon_change_state (struct mon* mon, int action) +{ + switch (mon->state) { + + case MON_STATE_IDLE: + + switch (action) { + + case MON_ACTION_START_MOTION: + mon->state = MON_STATE_MOTION_ONLY; + break; + + case MON_ACTION_START_ACC: + mon->state = MON_STATE_ACC_ONLY; + break; + } + break; + + case MON_STATE_MOTION_ONLY: + + switch (action) { + + case MON_ACTION_STOP_MOTION: + mon->state = MON_STATE_IDLE; + break; + + case MON_ACTION_START_ACC: + mon->state = MON_STATE_MOTION_AND_ACC; + break; + } + break; + + case MON_STATE_ACC_ONLY: + + switch (action) { + + case MON_ACTION_STOP_ACC: + mon->state = MON_STATE_IDLE; + break; + + case MON_ACTION_START_MOTION: + mon->state = MON_STATE_MOTION_AND_ACC; + break; + } + break; + + case MON_STATE_MOTION_AND_ACC: + + switch (action) { + + case MON_ACTION_STOP_ACC: + mon->state = MON_STATE_MOTION_ONLY; + break; + + case MON_ACTION_STOP_MOTION: + mon->state = MON_STATE_ACC_ONLY; + break; + } + break; + } + return; +} + +int mon_start_motion (struct mon *mon) +{ + int err = 0; + struct spi_device *spi; + + spi = mon->spi; + if (spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + mon->regs.mode |= 0x04; + avr_write_mode (spi, &mon->regs); + mon_change_state (mon, MON_ACTION_START_MOTION); + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_start_motion() - FAIL - spi is 0.\n"); + err = 1; + } + return err ; +} + +int mon_stop_motion (struct mon *mon) +{ + int err = 0; + struct spi_device *spi; + + spi = mon->spi; + if (spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + mon->regs.mode &= ~(0x04); + avr_write_mode (spi, &mon->regs); + mon_change_state (mon, MON_ACTION_STOP_MOTION); + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_stop_motion() - FAIL - spi is 0.\n"); + err = 1; + } + return err ; +} + + +int mon_set_config_accel (struct mon *mon, struct mdacc_accel_config *cfg) +{ + int err = 0; + unsigned char tmp; + struct spi_device *spi; + + spi = mon->spi; + if (cfg && spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + if (cfg->delay_mode) { + + mon->regs.timer_res = cfg->delay_resolution; + mon->regs.timer_msb = cfg->delay >> 8; + mon->regs.timer_lsb = cfg->delay; + mon->regs.mode |= 0x10; + } + else { + mon->regs.mode &= ~0x10; + } + + if (cfg->run) { + mon->regs.mode |= 0x08; + mon_change_state (mon, MON_ACTION_START_ACC); + } + else { + mon->regs.mode &= ~0x08; + mon_change_state (mon, MON_ACTION_STOP_ACC); + + } + + tmp = mon->regs.mode & 0xFC; + tmp |= cfg->sensitivity & 0x03; + mon->regs.mode = tmp; + + if (cfg->delay_mode) { + avr_write_timer_and_mode (spi, &mon->regs); + } + else { + avr_write_mode (spi, &mon->regs); + } + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_set_config_accel() - FAIL - null pointer.\n"); + err = 1; + } + return err ; +} + +int mon_get_config_accel (struct mon *mon, struct mdacc_accel_config *cfg) +{ + int err = 0; + struct spi_device *spi; + + spi = mon->spi; + if (cfg && spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + + avr_read_timer_and_mode (spi, &mon->regs); + + + if (mon->regs.mode & 0x10) { + cfg->delay_mode = 1; + cfg->delay_resolution = mon->regs.timer_res; + cfg->delay = mon->regs.timer_msb << 8 | mon->regs.timer_lsb; + } + else { + cfg->delay_mode = 0; + cfg->delay_resolution = 1; + cfg->delay = 5000; + } + + if (mon->regs.mode & 0x08) { + cfg->run = 1; + } + else { + cfg->run = 0; + } + cfg->sensitivity = mon->regs.mode & 0x03; + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_get_config_accel() - FAIL - null pointer.\n"); + err = 1; + } + return err ; +} + +int mon_start_accel (struct mon *mon) +{ + int err = 0; + struct spi_device *spi; + + spi = mon->spi; + if (spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + mon->regs.mode |= 0x08; + avr_write_mode (spi, &mon->regs); + mon_change_state (mon, MON_ACTION_START_ACC); + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_start_accel() - FAIL - spi is 0.\n"); + err = 1; + } + return err ; +} + + +int mon_stop_accel (struct mon *mon) +{ + int err = 0; + + struct spi_device *spi; + + spi = mon->spi; + if (spi) { + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + + mon->regs.mode &= ~0x08; + avr_write_mode (spi, &mon->regs); + mon_change_state (mon, MON_ACTION_STOP_ACC); + up (&mon->sem); + } + else { + printk (KERN_ERR "mon_stop_accel() - FAIL - spi is 0.\n"); + err = 1; + } + return err ; +} + + +int mon_status_request (struct mon* mon) +{ + struct spi_device *spi; + int err = 0; + + + spi = mon->spi; + + if (!spi) { + printk (KERN_ERR "mon_status_request() - FAIL - spi is 0.\n"); + err = 1; + } + else { + + if (down_interruptible (&mon->sem)) + return -ERESTARTSYS; + //Read the avr status register + + switch (mon->state) { + + + case MON_STATE_IDLE: + + avr_read_status (spi, &mon->regs); + + //update md status + md_update (mon->md, mon->regs.status); + break; + + case MON_STATE_MOTION_ONLY: + + avr_read_status (spi, &mon->regs); + + //update md status + md_update (mon->md, mon->regs.status); + break; + + + case MON_STATE_ACC_ONLY: + + avr_read_status_and_adc (spi, &mon->regs); + + cque_write (mon->acc->cque, &mon->regs.adc0h); + if (cque_is_ready_for_read (mon->acc->cque) ) { + wake_up_interruptible (&mon->acc->read_wait_queue); + } + break; + + case MON_STATE_MOTION_AND_ACC: + + avr_read_status (spi, &mon->regs); + + //update md status + md_update (mon->md, mon->regs.status); + + // adc complete status ? + if (mon->regs.status & 0x20) { + avr_read_adc (spi, &mon->regs); + cque_write (mon->acc->cque, &mon->regs.adc0h); + if (cque_is_ready_for_read (mon->acc->cque) ) { + wake_up_interruptible (&mon->acc->read_wait_queue); + } + } + + break; + + default: + printk (KERN_ERR "mon_work_handler() - invalid state.\n"); + + } + up (&mon->sem); + } + return err; +} + +// work handler +static void mon_work_handler (struct work_struct * work) +{ + + struct mon *mon = work_to_mon(work); + + if ( !mon_status_request(mon) ) { + enable_irq (mon->irq); + } + return; +} + +// interrupt handler +static irqreturn_t mon_irq_handler(int irq, void *dummy) +{ + struct mon *mon = dummy; + + disable_irq_nosync(irq); + schedule_work (&mon->work_item); + return IRQ_HANDLED; +} + +int mon_probe (struct mon *mon, const char *name, int irq, struct md *md, struct acc *acc) +{ + int err; + struct bmi_device *bdev; + unsigned long speed; + + unsigned char mode; + unsigned char bits_per_word; + + err = 1; + if (mon) { + bdev = mdacc_get_bdev_mon (mon); + speed = 250000; +// speed = 125000; + mode = 1; + bits_per_word = 8; + + strcpy(mon->mon_spi_info.modalias, "bug_mdacc_spi"); + mon->mon_spi_info.max_speed_hz = speed; + mon->mon_spi_info.bus_num = bdev->slot->spi_bus_num; + mon->mon_spi_info.chip_select = bdev->slot->spi_cs; + mon->mon_spi_info.mode = mode; + mon->spi = spi_new_device(spi_busnum_to_master(mon->mon_spi_info.bus_num), &mon->mon_spi_info) ; + if (!mon->spi) { + printk (KERN_ERR "mon_probe() - bmi_device_spi_setup() failed.\n"); + goto exit; + } + + mon->irq = irq; + mon->md = md; + mon->acc = acc; + init_MUTEX (&mon->sem); + mon->state = MON_STATE_IDLE; + memset (&mon->regs, 0, sizeof (struct avr_regs) ); + + mon->workqueue = create_singlethread_workqueue (name); + if (!mon->workqueue) { + printk (KERN_ERR "mon_probe() - create_singlethread_workqueue() failed.\n"); + goto exit; + } + INIT_WORK(&mon->work_item, mon_work_handler); + + if (request_irq(irq, &mon_irq_handler, 0, name, mon)) { + printk (KERN_ERR "mon_probe() - request_irq (irq = %d) failed.\n", irq); + destroy_workqueue( mon->workqueue ); + goto exit; + } + err = 0; + } +exit: + return err; +} + + +void mon_remove (struct mon *mon) +{ + if (mon) { + free_irq(mon->irq, mon); + destroy_workqueue( mon->workqueue ); + spi_unregister_device(mon->spi); + } + return; +} + + --- /dev/null +++ git/drivers/bmi/pims/mdacc/mon.h @@ -0,0 +1,61 @@ +/* + * Copyright 2008 EnCADIS Designs, Inc. All Rights Reserved. + * Copyright 2008 Bug-Labs, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*----------------------------------------------------------------------------- + * + * Part of BMI Motion Detector Accelerometer (MDACC) Kernel Module + * + *----------------------------------------------------------------------------- + */ + +#ifndef MDACC_MON_H +#define MDACC_MON_H + +#include "avr.h" +#include "md.h" +#include "acc.h" + +#include + +struct mon +{ + int irq; + struct md *md; + struct acc *acc; + struct spi_device *spi; + struct spi_board_info mon_spi_info; + struct semaphore sem; + int state; + struct avr_regs regs; + struct workqueue_struct *workqueue; + struct work_struct work_item; +}; + +struct md; +struct acc; + +int mon_probe (struct mon *mon, const char *name, int irq, struct md *md, struct acc *acc); +void mon_remove (struct mon *mon); + +int mon_start_motion (struct mon *mon); +int mon_stop_motion (struct mon *mon); + +int mon_set_config_accel (struct mon *mon, struct mdacc_accel_config *cfg); +int mon_get_config_accel (struct mon *mon, struct mdacc_accel_config *cfg); +int mon_start_accel (struct mon *mon); +int mon_stop_accel (struct mon *mon); + +int mon_status_request (struct mon *mon); + + +#endif + --- /dev/null +++ git/drivers/bmi/pims/projector/Makefile @@ -0,0 +1,7 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_VIDEO_BMI_PROJECTOR) += bmi_projector_core.o +bmi_projector_core-objs := bmi_projector.o ch7024.o + --- /dev/null +++ git/drivers/bmi/pims/projector/bmi_projector.c @@ -0,0 +1,674 @@ +/* + * bmi_projector.c + * + * BMI PROJECTOR device driver + * + * Derived from: bmi_lcd.c + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ch7024.h" + +#define DEBUG_PROJECTOR +#undef DEBUG_PROJECTOR + +#define BMIPROJECTOR_VERSION "1.0" // driver version +#define BMI_SLOT_NUM (4) // number of BMI slots +#define MAX_STRG (40) // Max string buffer size +#define VSYNC_DISABLE 0x0 +#define VSYNC_ENABLE 0x1 +#define PROJ_DEF_MODE 0x09 // Projector default mode of operation + + // projector +struct projector_interface { + char projector_type[MAX_STRG]; // text description of PROJECTOR type + u8 suspended; // power management state + u8 rotation; // screen rotation + u8 disp; // display number (DISP0 or DISP1) + u8 addr_mode; // display addressing mode + u8 vsync_mode; // VSYNC signal enable (VSYNC_ENABLE | VSYNC_DISABLE) + u8 bus_if_type; // bus type (XY | FullWoBE | FullWithBE) + ipu_adc_sig_cfg_t adc_sig; // IPU ADC set-up parameters + ipu_di_signal_cfg_t di_sig; // IPU DI set-up parameters +}; + +static struct projector_interface projector_interface = { + .projector_type = "MXCFB_PROJECTOR", + .suspended = 0, + .rotation = IPU_ROTATE_NONE, + .disp = DISP0, + .vsync_mode = VSYNC_DISABLE, + .bus_if_type = XY, + .adc_sig = { 0, 0, 0, 0, 0, 0, 0, 0, IPU_ADC_BURST_WCS, IPU_ADC_IFC_MODE_SYS80_TYPE2, + 16, 0, 0, IPU_ADC_SER_NO_RW }, + .di_sig = { 0,0,0,0,0,0,0,0 }, //pjg - reserved for multiple Projector driver +}; + +extern void projector_config(int disp); +extern int projector_disp_on(int disp); +extern int projector_disp_regset(int disp, unsigned short reg, unsigned short val); + +struct bmi_projector; +static int disp_mode_reg = 0x09; // Bit 3 of GPIO control register should always be maintained high +//static int proj_mode = 0x1; // Bit 3 of GPIO control register should always be maintained high + +struct bmi_projector_ops { + void *(*config) (int disp); // Projector configuration/initialization + void *(*reset) (int slot); // Projector reset + int *(*suspend) (struct bmi_projector *bprojector); // power management + int *(*resume) (struct bmi_projector *bprojector); // power management + int *(*disp_on) (int disp); // display on + int *(*disp_off) (int disp); // display off + int (*activate) (struct bmi_projector *projector, int slot); // enable Projector + int (*deactivate) (struct bmi_projector *projector, int slot); // disable Projector +}; + +struct bmi_projector_ops bmi_projector_ops; + +struct bmi_projector { + struct projector_interface interface; // pointer to this struct is returned by config() + struct bmi_projector_ops projector_ops; // function pointers +}; + +static struct bmi_projector bmi_projector; + +int register_bmi_projector(struct bmi_projector *bprojector, int slot); +int unregister_bmi_projector(struct bmi_projector *bprojector, int slot); + + // private device structure +struct pbmi_projector +{ + int open_flag; // force single open + unsigned int projector_cnt; // number of Projector's present + unsigned int active; // indication of Projector's presence + unsigned int activated[BMI_SLOT_NUM]; // indication of Projector's presence + int proj_mode[BMI_SLOT_NUM]; // Indicate the state of projector on the slot + //int disp_mode_reg[BMI_SLOT_NUM]; + + struct bmi_projector *bprojector[BMI_SLOT_NUM]; // BMI Projector structure - placeholder for multiple display types + struct bmi_device *bdev[BMI_SLOT_NUM]; // BMI device per slot + unsigned int interrupt[BMI_SLOT_NUM]; // input device interrupt handlers + char int_name[MAX_STRG]; // interrupt name +}; + +static struct pbmi_projector pbmi_projector; // Projector device sructure + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_projector_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_PROJECTOR, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_projector_tbl); + +int bmi_projector_probe(struct bmi_device *bdev); +void bmi_projector_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_projector_driver = +{ + .name = "bmi_projector", + .id_table = bmi_projector_tbl, + .probe = bmi_projector_probe, + .remove = bmi_projector_remove, +}; + +void bmi_projector_config(struct bmi_projector *projector, int disp); + + // probe +int bmi_projector_probe(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + struct i2c_adapter *adap; + struct bmi_projector *projector; + /*int first_time = 1;*/ + + printk(KERN_INFO "bmi_projector.c: probe slot %d\n", slot); + + // check for opposite side already active + switch(slot) { // opposite side + case 0: + if(pbmi_projector.activated[2] == 1) { + printk(KERN_INFO "bmi_projector.c: probe slot %d not allowed (slot 2 already active)\n", slot); + bmi_slot_power_off(0); + pbmi_projector.bdev[0] = bdev; + return 0; + } + break; + case 1: + if(pbmi_projector.activated[3] == 1) { + printk(KERN_INFO "bmi_projector.c: probe slot %d not allowed (slot 3 already active)\n", slot); + bmi_slot_power_off(1); + pbmi_projector.bdev[1] = bdev; + return 0; + } + break; + case 2: + if(pbmi_projector.activated[0] == 1) { + printk(KERN_INFO "bmi_projector.c: probe slot %d not allowed (slot 0 already active)\n", slot); + bmi_slot_power_off(2); + pbmi_projector.bdev[2] = bdev; + return 0; + } + break; + case 3: + if(pbmi_projector.activated[1] == 1) { + printk(KERN_INFO "bmi_projector.c: probe slot %d not allowed (slot 1 already active)\n", slot); + bmi_slot_power_off(3); + pbmi_projector.bdev[3] = bdev; + return 0; + } + break; + } + + adap = &bdev->adap; + +// bmi_slot_power_on(slot); + + mdelay(500); + + if (!ch7024_detect (adap)) + { + /* setup for NTSC */ + ch7024_setup (adap, PROJOUT_FMT_NTSC); +#ifdef DEBUG_PROJECTOR + printk ("\nFound encoder on slot %d \n", slot); +#endif + ch7024_enable (adap); + } + else + { + printk ("\nError! Failed to detect encoder chip\n"); + return 0; + } + + // reset serial link (master) + if((slot == 0) || (slot == 2)) { + bmi_lcd_inactive(0); // We are using Same CPLD pins for projector + } else { + bmi_lcd_inactive(1); + } + + // FPGA PROG_0 - Active low signal + bmi_set_module_gpio_data(slot, 0, 0); + bmi_set_module_gpio_dir(slot, 0, BMI_GPIO_OUT); + + /* Reset the FPGA */ + bmi_set_module_gpio_data(slot, 1, 1); + bmi_set_module_gpio_dir(slot, 0, BMI_GPIO_OUT); + mdelay(100); + bmi_set_module_gpio_data(slot, 1, 0); + + // unreset serial link (master) + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_lcd_active(0, 0x0, LCD_MODE_I80); + } else { + mdelay(2); + bmi_lcd_active(1, 0x0, LCD_MODE_I80); + } + + + // set up bdev/pbmi_projector pointers + bmi_device_set_drvdata(bdev, &pbmi_projector); + pbmi_projector.bdev[slot] = bdev; + + // complete pbmi_projector set-up + pbmi_projector.projector_cnt++; + pbmi_projector.active = 1; + pbmi_projector.activated[slot] = 1; + pbmi_projector.proj_mode[slot] = 1; + + mdelay(100); + + projector = pbmi_projector.bprojector[slot]; + if((slot == 0) || (slot == 2)) { + mdelay(2); + bmi_projector_config(projector, 0); + mdelay(2); + } else { + mdelay(2); + bmi_projector_config(projector, 1); + mdelay(2); + } + + // Turn on Projctor + disp_mode_reg = PROJ_DEF_MODE; + projector_disp_regset((slot & 0x1), 0x1, disp_mode_reg); + // check GPIO status + printk(KERN_INFO "bmi_projector.c: slot %d gpio = %x\n", slot, bmi_read_gpio_data_reg(slot)); + printk(KERN_INFO "bmi_projector.c: Projector count = %d\n", pbmi_projector.projector_cnt); + + return 0; +} + + // remove +void bmi_projector_remove(struct bmi_device *bdev) +{ + int slot = bdev->info->slot; + + if(pbmi_projector.activated[slot] == 0) + return; + + bmi_set_module_gpio_dir (slot, 3, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 2, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 1, BMI_GPIO_IN); + bmi_set_module_gpio_dir (slot, 0, BMI_GPIO_IN); + + //de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, NULL); + + // deactivate + pbmi_projector.activated[slot] = 0; + pbmi_projector.bdev[slot] = 0; + pbmi_projector.projector_cnt--; + + if((pbmi_projector.activated[0] == 0) && (pbmi_projector.activated[2] == 0)) { + bmi_lcd_inactive(0); // disable serializer + } + + if((pbmi_projector.activated[1] == 0) && (pbmi_projector.activated[3] == 0)) { + bmi_lcd_inactive(1); // disable serializer + } + + if((pbmi_projector.activated[0] == 0) && (pbmi_projector.activated[1] == 0) && + (pbmi_projector.activated[2] == 0) && (pbmi_projector.activated[3] == 0)) { + pbmi_projector.active = -1; + } + + // enable Projector on opposite side + switch(slot) { + case 0: + if(pbmi_projector.bdev[2] != 0) + bmi_projector_probe(pbmi_projector.bdev[2]); + break; + case 1: + if(pbmi_projector.bdev[3] != 0) + bmi_projector_probe(pbmi_projector.bdev[3]); + break; + case 2: + if(pbmi_projector.bdev[0] != 0) + bmi_projector_probe(pbmi_projector.bdev[0]); + break; + case 3: + if(pbmi_projector.bdev[1] != 0) + bmi_projector_probe(pbmi_projector.bdev[1]); + break; + } + + printk(KERN_INFO "bmi_projector.c: projector count = %d\n", pbmi_projector.projector_cnt); + + return; +} + +/* + * control device operations + */ + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *filp) +{ + if(pbmi_projector.open_flag) { + return - EBUSY; + } + pbmi_projector.open_flag = 1; + filp->private_data = &pbmi_projector; + return 0; +} + +// release +int cntl_release(struct inode *inode, struct file *filp) +{ + pbmi_projector.open_flag = 0; + return 0; +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + int slot = (__user arg) & 0xF; + int disp = 0; + int ret = 0; + + // error if no projector active. + if(pbmi_projector.active == -1) + return -ENODEV; + + // error if slot invalid + if((slot < CPLD_M1) || (slot > CPLD_M4)) + return -ENODEV; + + disp = slot & 0x1; // disp=0 for Slot 0 and 2, disp=1 for 1 and 3 +#ifdef DEBUG_PROJECTOR + printk (KERN_INFO "Slot No is:%d\n", slot); + printk (KERN_INFO "Disp No is:%d\n", disp); +#endif + + // error if no projector in chosen slot + if(pbmi_projector.bdev[slot] == 0) + return -ENODEV; + + // i2c adapter + adap = &pbmi_projector.bdev[slot]->adap; + + if( (cmd != BMI_PROJECTOR_ON) && (pbmi_projector.proj_mode[slot]/*proj_mode*/ == 0) ) + { + printk(KERN_ERR "Project is in OFF state !!!\n"); + return -EINVAL; + } + + // ioctl's + switch (cmd) { + case BMI_PROJECTOR_ON: + { + printk(KERN_INFO "BMI_PROJECTOR turning on\n"); + disp_mode_reg &= ~(0x3); + disp_mode_reg |= 0x1; + ch7024_enable(adap); + projector_disp_regset(disp, 0x1, disp_mode_reg); + mdelay(100); + projector_disp_regset(disp, 0x0, 0x1); + //proj_mode = 0x1; + pbmi_projector.proj_mode[slot] = 0x1; + } + break; + + case BMI_PROJECTOR_MODE: + { + int mode = ((__user arg) & 0xF0) >> 4; + printk(KERN_INFO "BMI_PROJECTOR setting mode to 0x%x \n",mode); + + disp_mode_reg &= ~(0x3); + switch(mode) + { + case PROJECTOR_ECONOMY_MODE: + disp_mode_reg |= 0x2; //Economy mode + break; + case PROJECTOR_BRIGHT_MODE: + disp_mode_reg |= 0x1; + break; + default: + printk(KERN_ERR "Invalid Mode\n"); + return -EINVAL; + } + + projector_disp_regset(disp, 0x1, disp_mode_reg); + } + break; + + case BMI_PROJECTOR_OFF: + { + disp_mode_reg &= ~(0x3); + disp_mode_reg |= 0x3; + ch7024_disable(adap); +#ifdef DEBUG_PROJECTOR + printk (KERN_INFO "Mode reg value is:0x%X\n", disp_mode_reg); +#endif + projector_disp_regset(disp, 0x1, disp_mode_reg); + //proj_mode = 0x0; + pbmi_projector.proj_mode[slot] = 0x0; + } + break; + + case BMI_PROJECTOR_BATTERY: + { + printk(KERN_INFO "BMI_PROJECTOR Staring Battery Charger for BUG\n"); + ch7024_disable(adap); + projector_disp_regset(disp, 0x1, 0xF); + //proj_mode = 0x0; + pbmi_projector.proj_mode[slot] = 0x0; + } + + break; + case BMI_PROJECTOR_HUE: + { + int val = ((__user arg) >> 8) & 0xff; + printk(KERN_INFO "BMI_PROJECTOR setting Hue to 0x%x\n",val); + ret = ch7024_set_hue(adap, val); + } + break; + case BMI_PROJECTOR_SATURATION: + { + int val = ((__user arg) >> 8) & 0xff; + printk(KERN_INFO "BMI_PROJECTOR setting Saturation to 0x%x\n",val); + ret = ch7024_set_sat(adap, val); + } + break; + case BMI_PROJECTOR_CONTRAST: + { + int val = ((__user arg) >> 8) & 0xff; + printk(KERN_INFO "BMI_PROJECTOR setting Contrast to 0x%x\n",val); + ret = ch7024_set_cont(adap, val); + } + break; + case BMI_PROJECTOR_BRIGHTNESS: + { + int val = ((__user arg) >> 8) & 0xff; + printk(KERN_INFO "BMI_PROJECTOR setting Brightness to 0x%x\n",val); + ret = ch7024_set_bright(adap, val); + } + break; + case BMI_PROJECTOR_SHARPNESS: + { + int val = ((__user arg) >> 8) & 0xff; + printk(KERN_INFO "BMI_PROJECTOR setting Sharpness to 0x%x\n",val); + ret = ch7024_set_sharp(adap, val); + } + break; + default: + return -ENOTTY; + } + return ret; +} + + // control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + + // BMI Projector fops +void bmi_projector_config(struct bmi_projector *projector, int disp) +{ + if(pbmi_projector.active == -1) { + return; + } + + if((projector) && (projector->projector_ops.config)) { + projector->projector_ops.config(disp); + } +} + +void bmi_projector_reset(struct bmi_projector *projector, int slot) +{ + if(pbmi_projector.active == -1) { + return; + } + + if((projector) && (projector->projector_ops.reset)) { + projector->projector_ops.reset(slot); + } +} + +int register_bmi_projector(struct bmi_projector *projector, int slot) //pjg - placeholder for multiple Projector types +{ + if(!projector) { + return -1; + } + if((slot < 0) || (slot > 3)) { + return -1; + } + if(pbmi_projector.bprojector[slot]) { + return -1; + } + else { + pbmi_projector.bprojector[slot] = projector; + } + + if(projector->projector_ops.activate) { + projector->projector_ops.activate(projector, slot); + } + + return 0; +} + +int unregister_bmi_projector(struct bmi_projector *projector, int slot) //pjg - placeholder for multiple projector types +{ + if (!projector) { + return -1; + } + if ((slot < 0) || (slot > 3)) { + return -1; + } + if (pbmi_projector.bprojector[slot] != projector) { + return -1; + } + else { + pbmi_projector.bprojector [slot] = 0; + projector->projector_ops.deactivate(projector, slot); + } + return 0; +} + +static struct miscdevice cntl_dev = { + MISC_DYNAMIC_MINOR, + "bmi_projector", + &cntl_fops +}; + +static __init int bmi_projector_init(void) +{ + int rc = 0; + + // No projector is active. + pbmi_projector.active = -1; + pbmi_projector.activated[0] = 0; + pbmi_projector.activated[1] = 0; + pbmi_projector.activated[2] = 0; + pbmi_projector.activated[3] = 0; + pbmi_projector.proj_mode[0] = 0; + pbmi_projector.proj_mode[1] = 0; + pbmi_projector.proj_mode[2] = 0; + pbmi_projector.proj_mode[3] = 0; + + // set up control character device - bmi_projector_control + rc = misc_register(&cntl_dev); + if(rc) { + printk(KERN_ERR "bmi_projector.c: Can't allocate bmi_projector_control device\n"); + return rc; + } + + pbmi_projector.projector_cnt = 0; + + // hardware specfic set-up + bmi_projector.interface = projector_interface, + bmi_projector_ops.config = (void(*)) &projector_config; + bmi_projector_ops.reset = NULL; //pjg - placeholder for multiple projector hardware types + bmi_projector_ops.suspend = NULL; //pjg - placeholder for multiple projector hardware types + bmi_projector_ops.resume = NULL; //pjg - placeholder for multiple projector hardware types + bmi_projector_ops.disp_on = NULL; //pjg - placeholder for multiple projector hardware types + bmi_projector_ops.disp_off = NULL; //pjg - placeholder for multiple projector hardware types + bmi_projector_ops.activate = NULL; //pjg - placeholder for multiple Projector hardware types + bmi_projector_ops.deactivate = NULL; //pjg - placeholder for multiple Projector hardware types + bmi_projector.projector_ops = bmi_projector_ops; + pbmi_projector.bprojector[0] = &bmi_projector; + pbmi_projector.bprojector[1] = &bmi_projector; + pbmi_projector.bprojector[2] = &bmi_projector; + pbmi_projector.bprojector[3] = &bmi_projector; + + // register with BMI + rc = bmi_register_driver(&bmi_projector_driver); + if(rc) { + printk(KERN_ERR "bmi_projector.c: Can't register bmi_projector_driver\n"); + + misc_deregister(&cntl_dev); + + return rc; + } + + printk("bmi_projector.c: BMI_Projector Driver v%s \n", BMIPROJECTOR_VERSION); + + return 0; +} + +static void __exit bmi_projector_clean(void) +{ + + // remove control device + misc_deregister(&cntl_dev); + + // remove bmi driver + bmi_unregister_driver(&bmi_projector_driver); + + return; +} + +module_init(bmi_projector_init); +module_exit(bmi_projector_clean); + +// Exported symbols +EXPORT_SYMBOL(register_bmi_projector); +EXPORT_SYMBOL(unregister_bmi_projector); + + +MODULE_AUTHOR("Suresh Rao"); +MODULE_DESCRIPTION("BMI projector device driver"); +MODULE_SUPPORTED_DEVICE("bmi_projector_control"); +MODULE_LICENSE("GPL"); + --- /dev/null +++ git/drivers/bmi/pims/projector/ch7024.c @@ -0,0 +1,476 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + + /*! + * @file ch7024.c + * @brief Driver for CH7024 TV encoder + * + * @ingroup Framebuffer + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include + +#include "ch7024.h" + +#define DEBUG_CH7024 + +static int ch7024_found = 0; +static struct i2c_adapter *ch7024_adap = NULL; + +static int i2c_ch7024_client_xfer( char *reg, int reg_len, char *buf, int num, + int tran_flag) +{ + struct i2c_msg msg[2]; + int ret; + if ((ch7024_adap == NULL)) + { + printk (KERN_ERR "ch7024_adap is NULL\n"); + return -1; + } + msg[0].addr = CH7024_I2C_ADDR; + msg[0].len = reg_len; + msg[0].buf = reg; + msg[0].flags = tran_flag; + msg[0].flags &= ~I2C_M_RD; + + msg[1].addr = CH7024_I2C_ADDR; + msg[1].len = num; + msg[1].buf = buf; + + msg[1].flags = tran_flag; + if (tran_flag & I2C_M_RD) { + msg[1].flags |= I2C_M_RD; + } else { + msg[1].flags &= ~I2C_M_RD; + } + + ret = i2c_transfer(ch7024_adap, msg, 2); + return ret; +} + +static int bug_i2c_ch7024_polling_read(char *reg, int reg_len, char *buf, int num) +{ + return i2c_ch7024_client_xfer(reg, reg_len, buf, num,I2C_M_RD); +} + +static int bug_i2c_ch7024_polling_write(char *reg, int reg_len, char *buf, + int num) +{ + return i2c_ch7024_client_xfer(reg, reg_len, buf, num, 0); + +} + +static int ch7024_read_reg(u32 reg, u32 * word, u32 len) +{ + int i; + u8 *wp = (u8 *) word; + + *word = 0; + + for (i = 0; i < len; i++) { + int ret = bug_i2c_ch7024_polling_read((char *)®, 1, wp, 1); + if (ret < 0) + return ret; + wp++; + reg++; + } + return 0; +} + +static int ch7024_write_reg(u32 reg, u32 word, u32 len) +{ + return bug_i2c_ch7024_polling_write((char *)®, 1, (u8 *) & word, len); +} + +/** + * PAL B/D/G/H/K/I clock and timting structures + */ +static struct ch7024_clock ch7024_clk_pal = { + .A = 0x0, + .P = 0x36b00, + .N = 0x41eb00, + .T = 0x3f, + .PLLN1 = 0x0, + .PLLN2 = 0x1b, + .PLLN3 = 0x12, +}; + +static struct ch7024_input_timing ch7024_timing_pal = { + .HTI = 950, + .VTI = 560, + .HAI = 640, + .VAI = 480, + .HW = 60, + .HO = 250, + .VW = 40, + .VO = 40, + .VOS = CH7024_VOS_PAL_BDGHKI, +}; + +/** + * NTSC_M clock and timting structures + * TODO: change values to work well. + */ +static struct ch7024_clock ch7024_clk_ntsc = { + .A = 0x0, + .P = 0x2ac90, + .N = 0x36fc90, + .T = 0x3f, + .PLLN1 = 0x0, + .PLLN2 = 0x1b, + .PLLN3 = 0x12, +}; + +static struct ch7024_input_timing ch7024_timing_ntsc = { + .HTI = 801, + .VTI = 554, + .HAI = 640, + .VAI = 480, + .HW = 60, + .HO = 101, + .VW = 20, + .VO = 54, + .VOS = CH7024_VOS_NTSC_M, +}; + +/** + * ch7024_setup + * initial the CH7024 chipset by setting register + * @param: + * vos: output video format + * @return: + * 0 successful + * otherwise failed + */ +int ch7024_setup(struct i2c_adapter *adap,int vos) +{ + struct ch7024_input_timing *ch_timing; + struct ch7024_clock *ch_clk; +#ifdef DEBUG_CH7024 + int i, val; +#endif + ch7024_adap = adap; +#if 0 + if (!ch7024_found) { + printk(KERN_ERR "CH7024: no such device to setup!\n"); + return -ENODEV; + } +#endif + /* select output video format */ + if (vos == PROJOUT_FMT_PAL) { + ch_timing = &ch7024_timing_pal; + ch_clk = &ch7024_clk_pal; + pr_debug("CH7024: change to PAL video\n"); + } else if (vos == PROJOUT_FMT_NTSC) { + ch_timing = &ch7024_timing_ntsc; + ch_clk = &ch7024_clk_ntsc; + pr_debug("CH7024: change to NTSC video\n"); + } else if (vos == PROJOUT_FMT_QVGA) { + ch_timing = &ch7024_timing_ntsc; + ch_clk = &ch7024_clk_ntsc; + pr_debug("CH7024: change to NTSC video\n"); + } + else { + + pr_debug("CH7024: no such video format.\n"); + return -EINVAL; + } + printk("Resetting Chrontel Card\n"); + ch7024_write_reg(CH7024_POWER, 0x0C, 1); /* power on, disable DAC */ + ch7024_write_reg(CH7024_RESET, 0x00, 1); /* Reset */ + ch7024_write_reg(CH7024_RESET, 0x03, 1); /* Reset */ + mdelay(10); + + ch7024_write_reg(CH7024_POWER, 0x0C, 1); /* power on, disable DAC */ + ch7024_write_reg(CH7024_XTAL, CH7024_XTAL_13MHZ, 1); /* 13MHz cystal */ + ch7024_write_reg(CH7024_SYNC, 0x0D, 1); /* Master mode, and TTL */ + ch7024_write_reg(CH7024_IDF1, 0x00, 1); + ch7024_write_reg(CH7024_TVFILTER1, 0x00, 1); /* set XCH=0 */ + + /* set input clock and divider */ + /* set PLL */ + ch7024_write_reg(CH7024_PLL1, ch_clk->PLLN1, 1); + ch7024_write_reg(CH7024_PLL2, ch_clk->PLLN2, 1); + ch7024_write_reg(CH7024_PLL3, ch_clk->PLLN3, 1); + + /* set A register */ + ch7024_write_reg(CH7024_PCLK_A1, 0x00, 1); + ch7024_write_reg(CH7024_PCLK_A2, 0x00, 1); + ch7024_write_reg(CH7024_PCLK_A3, 0x00, 1); + ch7024_write_reg(CH7024_PCLK_A4, 0x00, 1); + /* set P register */ + ch7024_write_reg(CH7024_CLK_P1, (ch_clk->P >> 16) & 0xFF, 1); + ch7024_write_reg(CH7024_CLK_P2, (ch_clk->P >> 8) & 0xFF, 1); + ch7024_write_reg(CH7024_CLK_P3, ch_clk->P & 0xFF, 1); + /* set N register */ + ch7024_write_reg(CH7024_CLK_N1, (ch_clk->N >> 16) & 0xFF, 1); + ch7024_write_reg(CH7024_CLK_N2, (ch_clk->N >> 8) & 0xFF, 1); + ch7024_write_reg(CH7024_CLK_N3, ch_clk->N & 0xFF, 1); + /* set T register */ + ch7024_write_reg(CH7024_CLK_T, ch_clk->T & 0xFF, 1); + + /* set sub-carrier frequency generation method */ + ch7024_write_reg(CH7024_ACIV, 0x10, 1); /* ACIV = 1, automatical SCF */ + /* TV out pattern and DAC switch */ + ch7024_write_reg(CH7024_OUT_FMT, (0x10 | ch_timing->VOS) & 0xFF, 1); + +if (vos != PROJOUT_FMT_QVGA) +{ + + /* input settings */ + ch7024_write_reg(CH7024_IDF2, 0x033, 1); + /* HAI/HTI VAI */ + ch7024_write_reg(CH7024_IN_TIMING1, ((ch_timing->HTI >> 5) & 0x38) | + ((ch_timing->HAI >> 8) & 0x07), 1); + ch7024_write_reg(CH7024_IN_TIMING2, ch_timing->HAI & 0xFF, 1); + ch7024_write_reg(CH7024_IN_TIMING8, ch_timing->VAI & 0xFF, 1); + /* HTI VTI */ + ch7024_write_reg(CH7024_IN_TIMING3, ch_timing->HTI & 0xFF, 1); + ch7024_write_reg(CH7024_IN_TIMING9, ch_timing->VTI & 0xFF, 1); + /* HW/HO(h) VW */ + ch7024_write_reg(CH7024_IN_TIMING4, ((ch_timing->HW >> 5) & 0x18) | + ((ch_timing->HO >> 8) & 0x7), 1); + ch7024_write_reg(CH7024_IN_TIMING6, ch_timing->HW & 0xFF, 1); + ch7024_write_reg(CH7024_IN_TIMING11, ch_timing->VW & 0x3F, 1); + /* HO(l) VO/VAI/VTI */ + ch7024_write_reg(CH7024_IN_TIMING5, ch_timing->HO & 0xFF, 1); + ch7024_write_reg(CH7024_IN_TIMING7, ((ch_timing->VO >> 4) & 0x30) | + ((ch_timing->VTI >> 6) & 0x0C) | + ((ch_timing->VAI >> 8) & 0x03), 1); + ch7024_write_reg(CH7024_IN_TIMING10, ch_timing->VO & 0xFF, 1); + +} + /* adjust the brightness */ + ch7024_write_reg(CH7024_TVBRI, 0x90, 1); + + ch7024_write_reg(CH7024_OUT_TIMING1, 0x4, 1); + ch7024_write_reg(CH7024_OUT_TIMING2, 0xe0, 1); + + if (vos == PROJOUT_FMT_PAL) { + ch7024_write_reg(CH7024_V_POS1, 0x03, 1); + ch7024_write_reg(CH7024_V_POS2, 0x7d, 1); + } else { + ch7024_write_reg(CH7024_V_POS1, 0x02, 1); + ch7024_write_reg(CH7024_V_POS2, 0x7b, 1); + } + + /* Set up the sub carrier frequency */ + if (vos == PROJOUT_FMT_PAL) { + } + else { + /* We have crystal of 13MHz */ + ch7024_write_reg(CH7024_SC_FREQ4, 0x7E, 1); + ch7024_write_reg(CH7024_SC_FREQ3, 0xEA, 1); + ch7024_write_reg(CH7024_SC_FREQ2, 0x33, 1); + ch7024_write_reg(CH7024_SC_FREQ1, 0x02, 1); + + } + +#ifdef DEBUG_CH7024 + for (i = 0; i < CH7024_SC_FREQ4; i++) { + + ch7024_read_reg(i, &val, 1); + pr_debug("CH7024, reg[0x%x] = %x\n", i, val); + } +#endif + return 0; +} + +/** + * ch7024_enable + * Enable the ch7024 Power to begin TV encoder + */ +void ch7024_enable(struct i2c_adapter *adap) +{ + ch7024_adap = adap; + if (ch7024_found) { + ch7024_write_reg(CH7024_POWER, 0x00, 1); + printk("CH7024 power on.\n"); + } +} + +/** + * ch7024_disable + * Disable the ch7024 Power to stop TV encoder + */ +void ch7024_disable(struct i2c_adapter *adap) +{ + ch7024_adap = adap; + if (ch7024_found) { + ch7024_write_reg(CH7024_POWER, 0x0D, 1); + printk("CH7024 power off.\n"); + } +} + +int ch7024_dump (struct i2c_adapter *adap) + { + int i; + u32 data; + ch7024_adap = adap; + for (i =0; i <= CH7024_SC_FREQ4; i++) + { + ch7024_read_reg(i, &data, 1); + printk ("Offset :0%X Value :0%X\n", i, data); + } + ch7024_read_reg(0x62, &data, 1); + printk ("Offset :0x62 Value :0%X\n", data); + ch7024_read_reg(0x63, &data, 1); + printk ("Offset :0x63 Value :0%X\n", data); + ch7024_read_reg(0x7E, &data, 1); + printk ("Offset :0x7E Value :0%X\n", data); + return 0; + } +EXPORT_SYMBOL(ch7024_dump); + +int encoder_read_reg (struct i2c_adapter *adap, u32 offset, u32 *data) + { + int ret; + ch7024_adap = adap; + ret = ch7024_read_reg(offset, data, 1); + if (ret < 0) + { + printk ("Encoder read register failed at offset 0x%X\n", offset); + return ret; + } + return 0; + + } +EXPORT_SYMBOL(encoder_read_reg); + +int encoder_write_reg (struct i2c_adapter *adap, u32 offset, u32 data) + { + int ret; + ch7024_adap = adap; + ret = ch7024_write_reg(offset, data, 1); + if (ret < 0) + { + printk ("Encoder write2 register failed at offset 0x%X\n", offset); + return ret; + } + return 0; + + } +EXPORT_SYMBOL(encoder_write_reg); + +int ch7024_detect (struct i2c_adapter *adap) +{ + int ret; + u32 id; + ch7024_adap = adap; + /*TODO client detection */ + ret = ch7024_read_reg(CH7024_DEVID, &id, 1); + if (ret < 0 || id != CH7024_DEVICE_ID) { + printk(KERN_ERR + "ch7024: TV encoder not present: %d, read ret %d\n", id, + ret); + return -1; + } + printk(KERN_ERR "ch7024: TV encoder present: %x, read ret %x\n", id, + ret); + ch7024_found = 1; + return 0; + +} +EXPORT_SYMBOL(ch7024_detect); + +int ch7024_set_bright (struct i2c_adapter *adap,u32 val) +{ + ch7024_adap = adap; + if (val & ~0xFF) { + printk ("Brighness value is out of range[0-255] %d\n", val); + return -1; + } else { + ch7024_write_reg(CH7024_TVBRI, val, 1); + } + return 0; +} +EXPORT_SYMBOL(ch7024_set_bright); + +int ch7024_set_cont (struct i2c_adapter *adap,u32 val) +{ + ch7024_adap = adap; + if (val & ~0x7F) { + printk ("Contrast value is out of range[0-127] %d\n", val); + return -1; + } else { + ch7024_write_reg(CH7024_TVCTA, val, 1); + } + return 0; +} +EXPORT_SYMBOL(ch7024_set_cont); + +int ch7024_set_hue (struct i2c_adapter *adap,u32 val) +{ + ch7024_adap = adap; + if (val & ~0x7F) { + printk ("Hue value is out of range[0-127] %d\n", val); + return -1; + } else { + ch7024_write_reg(CH7024_TVHUE, val, 1); + } + return 0; +} +EXPORT_SYMBOL(ch7024_set_hue); + +int ch7024_set_sharp (struct i2c_adapter *adap,u32 val) +{ + ch7024_adap = adap; + if (val & ~0x07) { + printk ("Sharpness value is out of range[0-8] %d\n", val); + return -1; + } else { + ch7024_write_reg(CH7024_TVSHARP, val, 1); + } + return 0; +} +EXPORT_SYMBOL(ch7024_set_sharp); + +int ch7024_set_sat (struct i2c_adapter *adap,u32 val) +{ + ch7024_adap = adap; + if (val & ~0x7F) { + printk ("Saturation value is out of range[0-127] %d\n", val); + return -1; + } else { + ch7024_write_reg(CH7024_TVSAT, val, 1); + } + return 0; +} +EXPORT_SYMBOL(ch7024_set_sat); + +void ch7024_set_attr (struct i2c_adapter *adap,struct ch7024_attr *attributes) +{ + if (!attributes) + return; + ch7024_set_bright (adap, attributes->brghtness & 0xFF); + ch7024_set_cont (adap, attributes->contrast & 0xFF); + ch7024_set_hue (adap, attributes->hue & 0xFF); + ch7024_set_sharp (adap, attributes->sharpness & 0xFF); + ch7024_set_sat (adap,attributes->saturation & 0xFF); + return; +} + +EXPORT_SYMBOL(ch7024_set_attr); + + +EXPORT_SYMBOL(ch7024_setup); +EXPORT_SYMBOL(ch7024_enable); +EXPORT_SYMBOL(ch7024_disable); + + + --- /dev/null +++ git/drivers/bmi/pims/projector/ch7024.h @@ -0,0 +1,166 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ch7024.h + * @brief Driver for CH7024 TV encoder + * + * @ingroup Framebuffer + */ +#ifndef _CH7024_H_ +#define _CH7024_H_ + +#ifdef __KERNEL__ + + +/* I2C bus id and device address of CH7024 chip */ + +#define CH7024_I2C_ADDR 0x76 /* 7bits I2C address */ + +/*! + * CH7024 registers + */ +#define CH7024_DEVID 0x00 +#define CH7024_REVID 0x01 +#define CH7024_PG 0x02 + +#define CH7024_RESET 0x03 +#define CH7024_POWER 0x04 +#define CH7024_TVHUE 0x05 +#define CH7024_TVSAT 0x06 +#define CH7024_TVCTA 0x07 +#define CH7024_TVBRI 0x08 +#define CH7024_TVSHARP 0x09 +#define CH7024_OUT_FMT 0x0A +#define CH7024_XTAL 0x0B +#define CH7024_IDF1 0x0C +#define CH7024_IDF2 0x0D +#define CH7024_SYNC 0x0E +#define CH7024_TVFILTER1 0x0F +#define CH7024_TVFILTER2 0x10 +#define CH7024_IN_TIMING1 0x11 +#define CH7024_IN_TIMING2 0x12 +#define CH7024_IN_TIMING3 0x13 +#define CH7024_IN_TIMING4 0x14 +#define CH7024_IN_TIMING5 0x15 +#define CH7024_IN_TIMING6 0x16 +#define CH7024_IN_TIMING7 0x17 +#define CH7024_IN_TIMING8 0x18 +#define CH7024_IN_TIMING9 0x19 +#define CH7024_IN_TIMING10 0x1A +#define CH7024_IN_TIMING11 0x1B +#define CH7024_ACIV 0x1C +#define CH7024_OUT_TIMING1 0x1E +#define CH7024_OUT_TIMING2 0x1F +#define CH7024_V_POS1 0x20 +#define CH7024_V_POS2 0x21 +#define CH7024_H_POS1 0x22 +#define CH7024_H_POS2 0x23 +#define CH7024_PCLK_A1 0x24 +#define CH7024_PCLK_A2 0x25 +#define CH7024_PCLK_A3 0x26 +#define CH7024_PCLK_A4 0x27 +#define CH7024_CLK_P1 0x28 +#define CH7024_CLK_P2 0x29 +#define CH7024_CLK_P3 0x2A +#define CH7024_CLK_N1 0x2B +#define CH7024_CLK_N2 0x2C +#define CH7024_CLK_N3 0x2D +#define CH7024_CLK_T 0x2E +#define CH7024_PLL1 0x2F +#define CH7024_PLL2 0x30 +#define CH7024_PLL3 0x31 +#define CH7024_SC_FREQ1 0x34 +#define CH7024_SC_FREQ2 0x35 +#define CH7024_SC_FREQ3 0x36 +#define CH7024_SC_FREQ4 0x37 +#define CH7024_DATA_IO 0x63 + +/*! + * CH7024 register values + */ +/* video output formats */ +#define CH7024_VOS_NTSC_M 0x0 +#define CH7024_VOS_NTSC_J 0x1 +#define CH7024_VOS_NTSC_443 0x2 +#define CH7024_VOS_PAL_BDGHKI 0x3 +#define CH7024_VOS_PAL_M 0x4 +#define CH7024_VOS_PAL_N 0x5 +#define CH7024_VOS_PAL_NC 0x6 +#define CH7024_VOS_PAL_60 0x7 +/* crystal predefined */ +#define CH7024_XTAL_13MHZ 0x4 +#define CH7024_XTAL_26MHZ 0xB +#define CH7024_XTAL_27MHZ 0xC + +/* chip ID */ +#define CH7024_DEVICE_ID 0x45 + +/* clock source define */ +#define CLK_HIGH 0 +#define CLK_LOW 1 + +/* CH7024 presets structs */ +struct ch7024_clock { + u32 A; + u32 P; + u32 N; + u32 T; + u8 PLLN1; + u8 PLLN2; + u8 PLLN3; +}; + +struct ch7024_input_timing { + u32 HTI; + u32 VTI; + u32 HAI; + u32 VAI; + u32 HW; + u32 HO; + u32 VW; + u32 VO; + u32 VOS; +}; + +struct ch7024_attr{ + u32 brghtness; + u32 sharpness; + u32 hue; + u32 contrast; + u32 saturation; +}; + +/* function declare, used by bmi projector module */ +int ch7024_setup (struct i2c_adapter *adap,int vos); +void ch7024_enable (struct i2c_adapter *adap); +void ch7024_disable (struct i2c_adapter *adap); +int ch7024_detect (struct i2c_adapter *adap); +void ch7024_set_attr (struct i2c_adapter *adap, struct ch7024_attr *attributes); +int ch7024_set_sat (struct i2c_adapter *adap, u32 val); +int ch7024_set_sharp (struct i2c_adapter *adap, u32 val); +int ch7024_set_hue (struct i2c_adapter *adap, u32 val); +int ch7024_set_cont (struct i2c_adapter *adap, u32 val); +int ch7024_set_bright (struct i2c_adapter *adap, u32 val); +int ch7024_dump (struct i2c_adapter *adap); +int encoder_read_reg (struct i2c_adapter *adap, u32 offset, u32 *data); +int encoder_write_reg (struct i2c_adapter *adap, u32 offset, u32 data); + +#endif /* __KERNEL__ */ + +/* output video format */ +#define PROJOUT_FMT_PAL 0x01 +#define PROJOUT_FMT_NTSC 0x02 +#define PROJOUT_FMT_QVGA 0x03 + +#endif /* _CH7024_H_ */ --- /dev/null +++ git/drivers/bmi/pims/sensor/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BMI_SENSOR) += bmi_sensor.o + --- /dev/null +++ git/drivers/bmi/pims/sensor/bmi_sensor.c @@ -0,0 +1,4321 @@ +/* + * bmi_sensor.c + * + * BMI sensor device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define BMISENSOR_VERSION "1.0" + +#define work_to_sensor(w) container_of(w, struct bmi_sensor, work_item) +#define dev_to_bmi_device(d) container_of(d, struct bmi_device, dev) + +/* + * Global variables + */ + +static ushort factory_test = 0; +static int eeprom_init = 0; +static ushort xdac_init = 0; +static ushort ydac_init = 0; +static ushort zdac_init = 0; +static ushort fcc_test = 0; + +// private device structure +struct bmi_sensor +{ + struct semaphore sem; // bmi_sensor mutex + struct bmi_device *bdev; // BMI device + struct cdev cdev; // control device + struct device *class_dev; // control class device + struct sensor_eeprom_raw eeprom; // eeprom contents + char int_name[20]; // interrupt name + struct workqueue_struct *workqueue; // interrupt work queue + struct work_struct work_item; // interrupt work structure + char work_name[20]; // workqueue name + wait_queue_head_t pl_wait_queue; // Proximity/Light interrupt wait queue + unsigned char pl_int_en; // Proximity/Light interrupts are enabled + unsigned char pl_int_fl; // Proximity/Light interrupt occurred + wait_queue_head_t temp_wait_queue; // Temperature interrupt wait queue + unsigned char temp_int_en; // Temperature interrupts are enabled + unsigned char temp_int_fl; // Temperature interrupt occurred + wait_queue_head_t mot_wait_queue; // Motion interrupt wait queue + unsigned char mot_int_en; // Motion interrupts are enabled + unsigned char mot_int_fl; // Motion interrupt occurred + unsigned int mot_state; // previous motion detector state + wait_queue_head_t acc_wait1_queue; // Accelerometer interrupt wait queue + unsigned char acc_int1_en; // Accelerometer interrupts are enabled + unsigned char acc_int1_fl; // Accelerometer interrupt occurred + wait_queue_head_t acc_wait2_queue; // Accelerometer interrupt wait queue + unsigned char acc_int2_en; // Accelerometer interrupts are enabled + unsigned char acc_int2_fl; // Accelerometer interrupt occurred + wait_queue_head_t usb_wait_queue; // USB interrupt wait queue + unsigned char usb_int_en; // USB interrupts are enabled + unsigned char usb_int_fl; // USB interrupt occurred + wait_queue_head_t dcomp_wait_queue; // Digital compass interrupt wait queue + unsigned char dcomp_int_en; // Digital compass interrupts are enabled + unsigned char dcomp_int_fl; // Digital compass interrupt occurred + unsigned int aprox_duration; // Analog Proximity LED burst duration (ms) + struct timer_list aprox_timer; // Analog Proximity LED burst timer + wait_queue_head_t aprox_wait_queue; // Analog Proximity timer wait queue + unsigned char aprox_int_en; // Analog Proximity timer are enabled + unsigned char aprox_int_fl; // Analog Proximity timer occurred + wait_queue_head_t dlight_wait_queue; // Digital Light interrupt wait queue + unsigned char dlight_int_en; // Digital Light interrupts are enabled + unsigned char dlight_int_fl; // Digital Light interrupt occurred + unsigned int comp_xsf; // Compass calibration + unsigned int comp_ysf; // Compass calibration + unsigned int comp_zsf; // Compass calibration + unsigned int comp_xoff; // Compass calibration + unsigned int comp_yoff; // Compass calibration + unsigned int comp_zoff; // Compass calibration +}; + +static struct bmi_sensor bmi_sensor[4]; // per slot device structure +static int major; // control device major + +/* + * BMI set up + */ + +// BMI device ID table +static struct bmi_device_id bmi_sensor_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_SENSOR, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_sensor_tbl); + +int bmi_sensor_probe(struct bmi_device *bdev); +void bmi_sensor_remove(struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_sensor_driver = +{ + .name = "bmi_sensor", + .id_table = bmi_sensor_tbl, + .probe = bmi_sensor_probe, + .remove = bmi_sensor_remove, +}; + +/* + * I2C set up + */ + +// IOX +// write byte to I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// ADC +// write byte to ADC +static int WriteByte_ADC(struct i2c_adapter *adap, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[1]; + int num_msgs; + + wmsg[0].addr = BMI_ADC_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &data; + + num_msgs = 1; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 1) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_ADC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read data from ADC +static int ReadByte_ADC(struct i2c_adapter *adap, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[1]; + int num_msgs; + + rmsg[0].addr = BMI_ADC_I2C_ADDRESS; + rmsg[0].flags = I2C_M_RD; // read + rmsg[0].len = 2; + rmsg[0].buf = data; + + num_msgs = 1; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 1) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_ADC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// Proximity/Light and Digital Light (same I2c address and format) +// write byte to I2C PL +static int WriteByte_PL(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_PL_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_PL_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_PL() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte from I2C PL +static int ReadByte_PL(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_PL_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_PL_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_PL() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// write byte to I2C PL SYNC +static int WriteByte_PL_SYNC(struct i2c_adapter *adap) +{ + int ret = 0; + struct i2c_msg wmsg[1]; + int num_msgs; + unsigned char offset = SENSOR_PL_EXT_SYNC; + + // Write Byte with Pointer + wmsg[0].addr = BMI_PL_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + num_msgs = 1; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 1) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_PL_SYNC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// write byte to I2C DL Interrupt Clear +static int WriteByte_DL_IC(struct i2c_adapter *adap) +{ + int ret = 0; + struct i2c_msg wmsg[1]; + int num_msgs; + unsigned char offset = SENSOR_DL_INT_CLR; + + // Write Byte with Pointer + wmsg[0].addr = BMI_DLIGHT_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + num_msgs = 1; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 1) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_DL_IC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// Temperature +// write byte to Temperature sensor +static int WriteByte_TEMP(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_TEMP_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_TEMP_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_TEMP() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte from Temperature sensor +static int ReadByte_TEMP(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_TEMP_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_TEMP_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_TEMP() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// Accelerometer +// write byte to I2C Accelerometer +static int WriteByte_ACC(struct i2c_adapter *adap, struct sensor_acc_rw *acc_rw) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_ACCEL_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &acc_rw->address; + + wmsg[1].addr = BMI_ACCEL_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = acc_rw->data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte(s) from Acceleromter +static int ReadByte_ACC(struct i2c_adapter *adap, struct sensor_acc_rw *acc_rw) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_ACCEL_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &acc_rw->address; + + rmsg[1].addr = BMI_ACCEL_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = acc_rw->count; + rmsg[1].buf = acc_rw->data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_ACC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// digital compass +// write byte to digital compass +static int WriteByte_DCOMP(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_DCOMP_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_DCOMP_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_DCOMP() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte from digital compass +static int ReadByte_DCOMP(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_DCOMP_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_DCOMP_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_DCOMP() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// EEPROM +// write byte to I2C EEPROM +static int WriteByte_EE(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_MEE_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_MEE_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "WriteByte_EE() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// read byte from I2C EEPROM +static int ReadByte_EE(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_MEE_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_MEE_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) { + ret = 0; + } + else { + printk(KERN_ERR "ReadByte_EE() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *file) +{ + struct bmi_sensor *sensor; + + sensor = container_of(inode->i_cdev, struct bmi_sensor, cdev); + file->private_data = sensor; + return 0; + +} + +// release +int cntl_release(struct inode *inode, struct file *file) +{ + return 0; +} + +// analog proximity timer function +void aptimer(unsigned long arg) +{ + struct bmi_sensor *sensor = (struct bmi_sensor *) arg; + int ret; + + del_timer (&sensor->aprox_timer); + + // wake sleepers + ret = down_interruptible(&sensor->sem); + sensor->aprox_int_en = 0; + sensor->aprox_int_fl = 1; + up(&sensor->sem); + wake_up_all(&sensor->aprox_wait_queue); +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + struct bmi_sensor *sensor = (struct bmi_sensor *)(file->private_data); + int slot; + int ret = 0; + + // error if sensor not present + if(sensor->bdev == 0) + return -ENODEV; + + slot = bmi_device_get_slot(sensor->bdev); + adap = bmi_device_get_i2c_adapter(sensor->bdev); + + // ioctl's + switch(cmd) { + + case BMI_SENSOR_RLEDOFF: + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_RED_LED, SENSOR_GPIO_LED_OFF); // Red LED=OFF + break; + + case BMI_SENSOR_RLEDON: + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_RED_LED, SENSOR_GPIO_LED_ON); // Red LED=ON + break; + + case BMI_SENSOR_GLEDOFF: + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_GREEN_LED, SENSOR_GPIO_LED_OFF); // Green LED=OFF + break; + + case BMI_SENSOR_GLEDON: + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_GREEN_LED, SENSOR_GPIO_LED_ON); // Green LED=ON + break; + + case BMI_SENSOR_GETSTAT: + { + int read_data; + + if(ReadByte_IOX(adap, IOX_INPUT0_REG, &iox_data)) + return -ENODEV; + read_data = iox_data; + + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + return -ENODEV; + read_data |= (iox_data << 8) | (bmi_read_gpio_data_reg(slot) << 16); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_ADCWR: + { + unsigned char adc_data; + + if(sensor->eeprom.adc_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + adc_data = (unsigned char) (arg & 0xFF); + if(WriteByte_ADC(adap, adc_data)) + return -ENODEV; + } + break; + + case BMI_SENSOR_ADCRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.adc_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_HUMRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.humidity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_HUMIDITY | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_ACOMPRST: + { + if(sensor->eeprom.acompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_IOX(adap, IOX_INPUT0_REG, &iox_data)) + return -ENODEV; + + iox_data &= ~(0x1 << SENSOR_IOX_COMP_RS_N); + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + + mdelay(5); + + iox_data |= (0x1 << SENSOR_IOX_COMP_RS_N); + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + } + break; + + case BMI_SENSOR_ACOMPXRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.acompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_X | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_ACOMPYRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.acompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Y | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_ACOMPZRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.acompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Z | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_PLWR: + { + struct sensor_pl_rw *pl = NULL; + unsigned char pl_data; + + if(sensor->eeprom.light_proximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((pl = kmalloc(sizeof(struct sensor_pl_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(pl, (struct sensor_pl_rw *) arg, sizeof(struct sensor_pl_rw))) { + kfree(pl); + return -EFAULT; + } + + pl_data = pl->cmd1; + if(WriteByte_PL(adap, SENSOR_PL_CMD1, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->cmd2; + if(WriteByte_PL(adap, SENSOR_PL_CMD2, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_lt_lsb; + if(WriteByte_PL(adap, SENSOR_PL_INT_LT_LSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_lt_msb; + if(WriteByte_PL(adap, SENSOR_PL_INT_LT_MSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_ht_lsb; + if(WriteByte_PL(adap, SENSOR_PL_INT_HT_LSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_ht_msb; + if(WriteByte_PL(adap, SENSOR_PL_INT_HT_MSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + kfree(pl); + } + break; + + case BMI_SENSOR_PLRD: + { + struct sensor_pl_rw *pl = NULL; + unsigned char pl_data; + + if(sensor->eeprom.light_proximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((pl = kmalloc(sizeof(struct sensor_pl_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + if(ReadByte_PL(adap, SENSOR_PL_CMD1, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->cmd1 = pl_data; + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->dm = pl_data; + + if(ReadByte_PL(adap, SENSOR_PL_DATA_LSB, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->dl = pl_data; + + if(copy_to_user((struct sensor_pl_rw *) arg, pl, sizeof(struct sensor_pl_rw))) { + kfree(pl); + return -EFAULT; + } + + kfree(pl); + } + break; + + case BMI_SENSOR_PL_SYNC: + { + if(sensor->eeprom.light_proximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_PL_SYNC(adap)) + return -ENODEV; + } + break; + + case BMI_SENSOR_PL_IWAIT: + { + struct sensor_pl_rw *pl = NULL; + unsigned char pl_data; + + if(sensor->eeprom.light_proximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((pl = kmalloc(sizeof(struct sensor_pl_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(pl, (struct sensor_pl_rw *) arg, sizeof(struct sensor_pl_rw))) { + kfree(pl); + return -EFAULT; + } + + pl_data = pl->cmd1; + if(WriteByte_PL(adap, SENSOR_PL_CMD1, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->cmd2; + if(WriteByte_PL(adap, SENSOR_PL_CMD2, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_lt_lsb; + if(WriteByte_PL(adap, SENSOR_PL_INT_LT_LSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_lt_msb; + if(WriteByte_PL(adap, SENSOR_PL_INT_LT_MSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_ht_lsb; + if(WriteByte_PL(adap, SENSOR_PL_INT_HT_LSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + pl_data = pl->int_ht_msb; + if(WriteByte_PL(adap, SENSOR_PL_INT_HT_MSB, pl_data)) { + kfree(pl); + return -ENODEV; + } + + ret = down_interruptible(&sensor->sem); + sensor->pl_int_en = 1; + sensor->pl_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->pl_wait_queue, (sensor->pl_int_fl == 1)); + if(ret) + return ret; + + if(ReadByte_PL(adap, SENSOR_PL_CMD1, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->cmd1 = pl_data; + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->dm = pl_data; + + if(ReadByte_PL(adap, SENSOR_PL_DATA_LSB, &pl_data)) { + kfree(pl); + return -ENODEV; + } + pl->dl = pl_data; + + if(copy_to_user((struct sensor_pl_rw *) arg, pl, sizeof(struct sensor_pl_rw))) { + kfree(pl); + return -EFAULT; + } + + kfree(pl); + } + break; + + case BMI_SENSOR_SNDARD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.sound_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_AVG | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_SNDPRD: + case BMI_SENSOR_SNDIRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.sound_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + // read peak + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_PEAK | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + // clear peak + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + return -ENODEV; + + iox_data &= ~(0x1 << SENSOR_IOX_S_PK_CLR_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + return -ENODEV; + + mdelay(1); + + iox_data |= (0x1 << SENSOR_IOX_S_PK_CLR_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + return -ENODEV; + + if(cmd == BMI_SENSOR_SNDPRD) { + // return data + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } else { + + // read peak + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_PEAK | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + // return data + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + + } + break; + + case BMI_SENSOR_TEMPWR: + { + struct sensor_temp_rw *temp = NULL; + unsigned char temp_addr; + unsigned char temp_data; + + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((temp = kmalloc(sizeof(struct sensor_temp_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(temp, (struct sensor_temp_rw *) arg, sizeof(struct sensor_temp_rw))) { + kfree(temp); + return -EFAULT; + } + + temp_addr = temp->address; + temp_data = temp->d1; + if(WriteByte_TEMP(adap, temp_addr, temp_data)) { + kfree(temp); + return -ENODEV; + } + + kfree(temp); + } + break; + + case BMI_SENSOR_TEMPRD: + { + struct sensor_temp_rw *temp = NULL; + unsigned char temp_addr; + unsigned char temp_data; + + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((temp = kmalloc(sizeof(struct sensor_temp_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(temp, (struct sensor_temp_rw *) arg, sizeof(struct sensor_temp_rw))) { + kfree(temp); + return -EFAULT; + } + + temp_addr = temp->address; + if(ReadByte_TEMP(adap, temp_addr, &temp_data)) { + kfree(temp); + return -ENODEV; + } + + temp->d1 = temp_data; + if(copy_to_user((struct sensor_temp_rw *) arg, temp, sizeof(struct sensor_temp_rw))) { + kfree(temp); + return -EFAULT; + } + + kfree(temp); + } + break; + + case BMI_SENSOR_TEMPRD_SL: + { + unsigned int read_data; + unsigned char temp_datam; + unsigned char temp_datal; + + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_MSB, &temp_datam)) { + return -ENODEV; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_LSB, &temp_datal)) { + return -ENODEV; + } + + read_data = (temp_datam << 8) | temp_datal; + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + + } + break; + + case BMI_SENSOR_TEMPRD_SR: + { + unsigned int read_data; + unsigned char temp_datam; + unsigned char temp_datal; + + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_MSB, &temp_datam)) { + return -ENODEV; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_LSB, &temp_datal)) { + return -ENODEV; + } + + read_data = (temp_datam << 8) | temp_datal; + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + + } + break; + + case BMI_SENSOR_TEMPRD_UR: + { + unsigned int read_data; + unsigned char temp_datam; + unsigned char temp_datal; + + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_MSB, &temp_datam)) { + return -ENODEV; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_LSB, &temp_datal)) { + return -ENODEV; + } + + read_data = (temp_datam << 8) | temp_datal; + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + + } + break; + + case BMI_SENSOR_TEMP_IWAIT: + { + if(sensor->eeprom.temperature_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + ret = down_interruptible(&sensor->sem); + sensor->temp_int_en = 1; + sensor->temp_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->temp_wait_queue, (sensor->temp_int_fl == 1)); + if(ret) + return ret; + } + break; + + case BMI_SENSOR_MOTRD: + { + unsigned int read_data; + + if(sensor->eeprom.motion_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + read_data = bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET); + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + + } + break; + + case BMI_SENSOR_MOT_IWAIT: + { + unsigned int read_data; + + if(sensor->eeprom.motion_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + ret = down_interruptible(&sensor->sem); + sensor->mot_int_en = 1; + sensor->mot_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->mot_wait_queue, (sensor->mot_int_fl == 1)); + if(ret) + return ret; + + read_data = bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET); + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + + } + break; + + case BMI_SENSOR_ACCWR: + { + struct sensor_acc_rw *acc = NULL; + + if((sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) + && (sensor->eeprom.acc302_present != SENSOR_DEVICE_PRESENT)) + return -ENODEV; + + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(acc, (struct sensor_acc_rw *) arg, sizeof(struct sensor_acc_rw))) { + kfree(acc); + return -EFAULT; + } + + if(WriteByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + kfree(acc); + } + break; + + case BMI_SENSOR_ACCRD: + { + struct sensor_acc_rw *acc = NULL; + + if((sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) + && (sensor->eeprom.acc302_present != SENSOR_DEVICE_PRESENT)) + return -ENODEV; + + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(acc, (struct sensor_acc_rw *) arg, sizeof(struct sensor_acc_rw))) { + kfree(acc); + return -EFAULT; + } + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + if(copy_to_user((struct sensor_acc_rw *) arg, acc, sizeof(struct sensor_acc_rw))) { + kfree(acc); + return -EFAULT; + } + + kfree(acc); + } + break; + + case BMI_SENSOR_ACCXRD: + { + struct sensor_acc_rw *acc = NULL; + unsigned int read_data; + + if(sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) { + + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_ACC_DX0; + acc->count = 2; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = (acc->data[1] << 8) | acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else if(sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT) { + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_A3_OUTX; + acc->count = 1; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else { + return -ENODEV; + } + + kfree(acc); + } + break; + + case BMI_SENSOR_ACCYRD: + { + struct sensor_acc_rw *acc = NULL; + unsigned int read_data; + + if(sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) { + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_ACC_DY0; + acc->count = 2; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = (acc->data[1] << 8) | acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else if(sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT) { + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_A3_OUTY; + acc->count = 1; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else { + return -ENODEV; + } + + kfree(acc); + } + break; + + case BMI_SENSOR_ACCZRD: + { + struct sensor_acc_rw *acc = NULL; + unsigned int read_data; + + if(sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) { + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_ACC_DZ0; + acc->count = 2; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = (acc->data[1] << 8) | acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else if(sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT) { + if ((acc = kmalloc(sizeof(struct sensor_acc_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + + acc->address = SENSOR_A3_OUTZ; + acc->count = 1; + + if(ReadByte_ACC(adap, acc)) { + kfree(acc); + return -ENODEV; + } + + read_data = acc->data[0]; + if(put_user(read_data, (int __user *) arg)) { + kfree(acc); + return -EFAULT; + } + } else { + return -ENODEV; + } + + kfree(acc); + } + break; + + case BMI_SENSOR_ACC_I1WAIT: + { + if((sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) + && (sensor->eeprom.acc302_present != SENSOR_DEVICE_PRESENT)) + return -ENODEV; + + ret = down_interruptible(&sensor->sem); + sensor->acc_int1_en = 1; + sensor->acc_int1_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->acc_wait1_queue, (sensor->acc_int1_fl == 1)); + if(ret) + return ret; + } + break; + + case BMI_SENSOR_ACC_I2WAIT: + { + if((sensor->eeprom.accel_present != SENSOR_DEVICE_PRESENT) + && (sensor->eeprom.acc302_present != SENSOR_DEVICE_PRESENT)) + return -ENODEV; + + ret = down_interruptible(&sensor->sem); + sensor->acc_int2_en = 1; + sensor->acc_int2_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->acc_wait2_queue, (sensor->acc_int2_fl == 1)); + if(ret) + return ret; + } + break; + + case BMI_SENSOR_EEWR: + { + struct sensor_rw *ee = NULL; + unsigned char ee_addr; + unsigned char ee_data; + + if ((ee = kmalloc(sizeof(struct sensor_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(ee, (struct sensor_rw *) arg, sizeof(struct sensor_rw))) { + kfree(ee); + return -EFAULT; + } + + ee_addr = ee->address; + ee_data = ee->data; + if(WriteByte_EE(adap, ee_addr, ee_data)) { + kfree(ee); + return -ENODEV; + } + + kfree(ee); + } + break; + + case BMI_SENSOR_EERD: + { + struct sensor_rw *ee = NULL; + unsigned char ee_addr; + unsigned char ee_data; + + if ((ee = kmalloc(sizeof(struct sensor_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(ee, (struct sensor_rw *) arg, sizeof(struct sensor_rw))) { + kfree(ee); + return -EFAULT; + } + + ee_addr = ee->address; + if(ReadByte_EE(adap, ee_addr, &ee_data)) { + kfree(ee); + return -ENODEV; + } + + ee->data = ee_data; + if(copy_to_user((struct sensor_rw *) arg, ee, sizeof(struct sensor_rw))) { + kfree(ee); + return -EFAULT; + } + + kfree(ee); + } + break; + + case BMI_SENSOR_MOT_IE: + { + if(sensor->eeprom.motion_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_IOX(adap, IOX_OUTPUT0_REG, &iox_data)) + return -ENODEV; + + if(arg == BMI_SENSOR_ON) + iox_data |= (0x1 << SENSOR_IOX_MOT_EN); + else + iox_data &= ~(0x1 << SENSOR_IOX_MOT_EN); + + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + } + break; + + case BMI_SENSOR_USB_IWAIT: + { + ret = down_interruptible(&sensor->sem); + sensor->usb_int_en = 1; + sensor->usb_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->usb_wait_queue, (sensor->usb_int_fl == 1)); + if(ret) + return ret; + } + break; + + case BMI_SENSOR_USB_PWR_EN: + { + if(ReadByte_IOX(adap, IOX_OUTPUT0_REG, &iox_data)) + return -ENODEV; + + if(arg == BMI_SENSOR_ON) + iox_data |= (0x1 << SENSOR_IOX_USB_EN); + else + iox_data &= ~(0x1 << SENSOR_IOX_USB_EN); + + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + } + break; + + case BMI_SENSOR_HUM_PWR_EN: + { + if(ReadByte_IOX(adap, IOX_OUTPUT0_REG, &iox_data)) + return -ENODEV; + + if(arg == BMI_SENSOR_ON) + iox_data |= (0x1 << SENSOR_IOX_HUM_EN); + else + iox_data &= ~(0x1 << SENSOR_IOX_HUM_EN); + + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + } + break; + + case BMI_SENSOR_DCOM_RST: + { + if(ReadByte_IOX(adap, IOX_OUTPUT0_REG, &iox_data)) + return -ENODEV; + + if(arg == BMI_SENSOR_ON) + iox_data |= (0x1 << SENSOR_IOX_COMP_RS_N); + else + iox_data &= ~(0x1 << SENSOR_IOX_COMP_RS_N); + + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, iox_data)) + return -ENODEV; + + } + break; + + case BMI_SENSOR_COM_GCAL: + { + struct sensor_comp_cal *cal = NULL; + unsigned char ee_datam; + unsigned char ee_datal; + + if ((cal = kmalloc(sizeof(struct sensor_comp_cal), GFP_KERNEL)) == NULL) + return -ENOMEM; + + if(ReadByte_EE(adap, 0x0, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0x1, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->xsf = (ee_datam << 8) | ee_datal; + + if(ReadByte_EE(adap, 0x2, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0x3, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->ysf = (ee_datam << 8) | ee_datal; + + if(ReadByte_EE(adap, 0x4, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0x5, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->zsf = (ee_datam << 8) | ee_datal; + + if(ReadByte_EE(adap, 0x6, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0x7, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->xoff = (ee_datam << 8) | ee_datal; + + if(ReadByte_EE(adap, 0x8, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0x9, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->yoff = (ee_datam << 8) | ee_datal; + + if(ReadByte_EE(adap, 0xA, &ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(ReadByte_EE(adap, 0xB, &ee_datal)) { + kfree(cal); + return -ENODEV; + } + + cal->zoff = (ee_datam << 8) | ee_datal; + + if(copy_to_user((struct sensor_comp_cal *) arg, cal, sizeof(struct sensor_comp_cal))) { + kfree(cal); + return -EFAULT; + } + + kfree(cal); + } + break; + + case BMI_SENSOR_COM_SCAL: + { + struct sensor_comp_cal *cal = NULL; + unsigned char ee_datam; + unsigned char ee_datal; + + if ((cal = kmalloc(sizeof(struct sensor_comp_cal), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(cal, (struct sensor_comp_cal *) arg, sizeof(struct sensor_comp_cal))) { + kfree(cal); + return -EFAULT; + } + + sensor->comp_xsf = cal->xsf; + sensor->comp_ysf = cal->ysf; + sensor->comp_zsf = cal->zsf; + sensor->comp_xoff = cal->xoff; + sensor->comp_xoff = cal->xoff; + sensor->comp_zoff = cal->zoff; + + ee_datam = (unsigned char) ((cal->xsf >> 8) & 0xff); + ee_datal = (unsigned char) (cal->xsf & 0xff); + + if(WriteByte_EE(adap, 0x0, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0x1, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + ee_datam = (unsigned char) ((cal->ysf >> 8) & 0xff); + ee_datal = (unsigned char) (cal->ysf & 0xff); + + if(WriteByte_EE(adap, 0x2, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0x3, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + ee_datam = (unsigned char) ((cal->zsf >> 8) & 0xff); + ee_datal = (unsigned char) (cal->zsf & 0xff); + + if(WriteByte_EE(adap, 0x4, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0x5, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + ee_datam = (unsigned char) ((cal->xoff >> 8) & 0xff); + ee_datal = (unsigned char) (cal->xoff & 0xff); + + if(WriteByte_EE(adap, 0x6, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0x7, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + ee_datam = (unsigned char) ((cal->yoff >> 8) & 0xff); + ee_datal = (unsigned char) (cal->yoff & 0xff); + + if(WriteByte_EE(adap, 0x8, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0x9, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + ee_datam = (unsigned char) ((cal->zoff >> 8) & 0xff); + ee_datal = (unsigned char) (cal->zoff & 0xff); + + if(WriteByte_EE(adap, 0xA, ee_datam)) { + kfree(cal); + return -ENODEV; + } + + if(WriteByte_EE(adap, 0xB, ee_datal)) { + kfree(cal); + return -ENODEV; + } + + kfree(cal); + } + break; + + case BMI_SENSOR_DCWR: + { + struct sensor_rw *dc = NULL; + unsigned char dc_addr; + unsigned char dc_data; + + if(sensor->eeprom.dcompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((dc = kmalloc(sizeof(struct sensor_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dc, (struct sensor_rw *) arg, sizeof(struct sensor_rw))) { + kfree(dc); + return -EFAULT; + } + + dc_addr = dc->address; + dc_data = dc->data; + if(WriteByte_DCOMP(adap, dc_addr, dc_data)) { + kfree(dc); + return -ENODEV; + } + + kfree(dc); + } + break; + + case BMI_SENSOR_DCRD: + { + struct sensor_rw *dc = NULL; + unsigned char dc_addr; + unsigned char dc_data; + + if(sensor->eeprom.dcompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((dc = kmalloc(sizeof(struct sensor_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dc, (struct sensor_rw *) arg, sizeof(struct sensor_rw))) { + kfree(dc); + return -EFAULT; + } + + dc_addr = dc->address; + if(ReadByte_DCOMP(adap, dc_addr, &dc_data)) { + kfree(dc); + return -ENODEV; + } + + dc->data = dc_data; + if(copy_to_user((struct sensor_rw *) arg, dc, sizeof(struct sensor_rw))) { + kfree(dc); + return -EFAULT; + } + + kfree(dc); + } + break; + + case BMI_SENSOR_DC_GDAC: + { + struct sensor_comp_dac *dac = NULL; + + if(sensor->eeprom.dcompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((dac = kmalloc(sizeof(struct sensor_comp_dac), GFP_KERNEL)) == NULL) + return -ENOMEM; + + dac->xdac = sensor->eeprom.xdac; + dac->ydac = sensor->eeprom.ydac; + dac->zdac = sensor->eeprom.zdac; + + if(copy_to_user((struct sensor_comp_dac *) arg, dac, sizeof(struct sensor_comp_dac))) { + kfree(dac); + return -EFAULT; + } + + kfree(dac); + } + break; + + case BMI_SENSOR_DC_SDAC: + { + struct sensor_comp_dac *dac = NULL; + + if(sensor->eeprom.dcompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((dac = kmalloc(sizeof(struct sensor_comp_dac), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dac, (struct sensor_comp_dac *) arg, sizeof(struct sensor_comp_dac))) { + kfree(dac); + return -EFAULT; + } + + sensor->eeprom.xdac = dac->xdac; + sensor->eeprom.ydac = dac->ydac; + sensor->eeprom.zdac = dac->zdac; + + if(WriteByte_EE(adap, SENSOR_EE_XDAC, dac->xdac & 0xFF)) { + kfree(dac); + return -ENODEV; + } + mdelay(5); + + if(WriteByte_EE(adap, SENSOR_EE_YDAC, dac->ydac & 0xFF)) { + kfree(dac); + return -ENODEV; + } + mdelay(5); + + if(WriteByte_EE(adap, SENSOR_EE_ZDAC, dac->zdac & 0xFF)) { + kfree(dac); + return -ENODEV; + } + mdelay(5); + + kfree(dac); + } + break; + + case BMI_SENSOR_DC_IWAIT: + { + if(sensor->eeprom.dcompass_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + ret = down_interruptible(&sensor->sem); + sensor->dcomp_int_en = 1; + sensor->dcomp_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->dcomp_wait_queue, (sensor->dcomp_int_fl == 1)); + if(ret) + return ret; + } + break; + + case BMI_SENSOR_APROX_DUR: + { + if(sensor->eeprom.aproximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(arg < 2) + sensor->aprox_duration = HZ/50; + else if(arg > 100) + sensor->aprox_duration = HZ/10; + else + sensor->aprox_duration = (HZ/100) * arg; + } + break; + + case BMI_SENSOR_APROXRD: + { + unsigned char aprox_data[2]; + int read_data; + + if(sensor->eeprom.aproximity_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + // start burst to LED + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + return -ENODEV; + iox_data |= (0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + return -ENODEV; + + // set up timer + sensor->aprox_timer.expires = jiffies + sensor->aprox_duration; + add_timer (&sensor->aprox_timer); + + // wait for timer + ret = down_interruptible(&sensor->sem); + sensor->aprox_int_en = 1; + sensor->aprox_int_fl = 0; + up(&sensor->sem); + ret = wait_event_interruptible(sensor->aprox_wait_queue, (sensor->aprox_int_fl == 1)); + if(ret) + return ret; + + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + return -ENODEV; + + // digital output + read_data = (iox_data & (0x1 << SENSOR_IOX_PROX_OUT)) << 14; + + // read ADC - analog output + if(WriteByte_ADC(adap, SENSOR_ADC_APROXIMITY | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, aprox_data)) + return -ENODEV; + read_data |= (aprox_data[0] << 8) | aprox_data[1]; + + // stop burst to LED + iox_data &= ~(0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + return -ENODEV; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_ALIGHTRD: + { + unsigned char adc_data[2]; + int read_data; + + if(sensor->eeprom.alight_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_ADC(adap, SENSOR_ADC_LIGHT | SENSOR_ADC_PD_OFF)) + return -ENODEV; + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) + return -ENODEV; + read_data = ((adc_data[0] & SENSOR_ADC_DATA_MSB) << 8) | adc_data[1]; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_DLIGHTWR: + { + struct sensor_dl_rw *dl = NULL; + unsigned char dl_data; + + if(sensor->eeprom.dlight_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if ((dl = kmalloc(sizeof(struct sensor_dl_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dl, (struct sensor_dl_rw *) arg, sizeof(struct sensor_dl_rw))) { + kfree(dl); + return -EFAULT; + } + + dl_data = dl->cmd; + if(WriteByte_PL(adap, SENSOR_DL_CMD, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->control; + if(WriteByte_PL(adap, SENSOR_DL_CONT, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->int_thi; + if(WriteByte_PL(adap, SENSOR_DL_INT_THI, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->int_tlo; + if(WriteByte_PL(adap, SENSOR_DL_INT_TLO, dl_data)) { + kfree(dl); + return -ENODEV; + } + + kfree(dl); + } + break; + + case BMI_SENSOR_DLIGHTRD: + { + unsigned char dl_data; + unsigned int read_data; + + if(sensor->eeprom.dlight_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + // read sensor data + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_MSB, &dl_data)) { + return -ENODEV; + } + read_data = ((unsigned int) (dl_data)) << 8; + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_LSB, &dl_data)) { + return -ENODEV; + } + read_data |= dl_data; + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_SENSOR_DLIGHT_IC: + { + if(sensor->eeprom.dlight_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(WriteByte_DL_IC(adap)) + return -ENODEV; + } + break; + + case BMI_SENSOR_DLIGHT_IWAIT: + { + struct sensor_dl_rw *dl = NULL; + unsigned char dl_data; + unsigned int read_data; + + if(sensor->eeprom.dlight_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + // write all register + if ((dl = kmalloc(sizeof(struct sensor_dl_rw), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dl, (struct sensor_dl_rw *) arg, sizeof(struct sensor_dl_rw))) { + kfree(dl); + return -EFAULT; + } + + dl_data = dl->cmd; + if(WriteByte_PL(adap, SENSOR_DL_CMD, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->control; + if(WriteByte_PL(adap, SENSOR_DL_CONT, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->int_thi; + if(WriteByte_PL(adap, SENSOR_DL_INT_THI, dl_data)) { + kfree(dl); + return -ENODEV; + } + + dl_data = dl->int_tlo; + if(WriteByte_PL(adap, SENSOR_DL_INT_TLO, dl_data)) { + kfree(dl); + return -ENODEV; + } + + // enable interrupt + ret = down_interruptible(&sensor->sem); + sensor->dlight_int_en = 1; + sensor->dlight_int_fl = 0; + up(&sensor->sem); + // wait on interrupt + ret = wait_event_interruptible(sensor->dlight_wait_queue, (sensor->dlight_int_fl == 1)); + if(ret) + return ret; + + // read sensor data + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_MSB, &dl_data)) { + return -ENODEV; + } + read_data = ((unsigned int) (dl_data)) << 8; + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_LSB, &dl_data)) { + return -ENODEV; + } + read_data |= dl_data; + dl->sensor_data = read_data; + + if(copy_to_user((struct sensor_dl_rw *) arg, dl, sizeof(struct sensor_dl_rw))) { + kfree(dl); + return -EFAULT; + } + + kfree(dl); + } + break; + + case BMI_SENSOR_MIC_EN: + { + if(sensor->eeprom.sound_present != SENSOR_DEVICE_PRESENT) + return -ENODEV; + + if(ReadByte_IOX(adap, IOX_OUTPUT1_REG, &iox_data)) + return -ENODEV; + + if(arg == BMI_SENSOR_ON) + iox_data |= (0x1 << SENSOR_IOX_MIC_EN); + else + iox_data &= ~(0x1 << SENSOR_IOX_MIC_EN); + + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + return -ENODEV; + } + break; + + default: + return -ENOTTY; + } + + return 0; +} + +// control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + +/* + * PIM functions + */ + +// interrupt handler +static void sensor_work_handler(struct work_struct * work) +{ + struct bmi_sensor *sensor = work_to_sensor(work); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(sensor->bdev); + int slot = bmi_device_get_slot(sensor->bdev); + unsigned char iox0; + unsigned char iox1; + unsigned char i2c_dummy; + struct sensor_acc_rw acc_rw; + + if(ReadByte_IOX(adap, IOX_INPUT0_REG, &iox0)) { + printk(KERN_ERR "bmi_sensor.c: sensor_work_handler - IOX0 error\n"); + return; + } + + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox1)) { + printk(KERN_ERR "bmi_sensor.c: sensor_work_handler - IOX1 error\n"); + return; + } + + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + if(sensor->pl_int_en) { + if((iox1 & (0x1 << SENSOR_IOX_PL_INT)) == 0) { + sensor->pl_int_en = 0; + sensor->pl_int_fl = 1; + // clear interrupts + if(ReadByte_PL(adap, SENSOR_PL_CMD1, &i2c_dummy)) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + wake_up_all(&sensor->pl_wait_queue); + } + } + } + + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + if(sensor->temp_int_en) { + if((iox1 & (0x1 << SENSOR_IOX_TEMP_INT)) == 0) { + sensor->temp_int_en = 0; + sensor->temp_int_fl = 1; + // disable interrupts + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF1_WR, SENSOR_TEMP_CONF1_STOP)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write error\n"); + } + wake_up_all(&sensor->temp_wait_queue); + } + } + } + + if(sensor->eeprom.motion_present == SENSOR_DEVICE_PRESENT) { + if(sensor->mot_int_en) { + if(sensor->mot_state != bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET)) { + sensor->mot_state = bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET); + sensor->mot_int_en = 0; + sensor->mot_int_fl = 1; + wake_up_all(&sensor->mot_wait_queue); + } + } + } + + if((sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) + || (sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT)) { + if(sensor->acc_int1_en) { + if((iox0 & (0x1 << SENSOR_IOX_ACC_INT1)) == 0) { + sensor->acc_int1_en = 0; + sensor->acc_int1_fl = 1; + wake_up_all(&sensor->acc_wait1_queue); + } + } + + if(sensor->acc_int2_en) { + if((iox0 & (0x1 << SENSOR_IOX_ACC_INT2)) == 0) { + sensor->acc_int2_en = 0; + sensor->acc_int2_fl = 1; + wake_up_all(&sensor->acc_wait2_queue); + } + } + + if(sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) { + // clear interrupts + acc_rw.address = SENSOR_ACC_IS; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC_IS read error\n"); + } + + acc_rw.address = SENSOR_ACC_DX0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DX0) error\n"); + } + + acc_rw.address = SENSOR_ACC_DY0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DY0) error\n"); + } + + acc_rw.address = SENSOR_ACC_DZ0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DZ0) error\n"); + } + } else { // LIS302DL + // clear interrupts + if(sensor->acc_int1_en) { + if((iox0 & (0x1 << SENSOR_IOX_ACC_INT1)) == 0) { + acc_rw.address = SENSOR_A3_SRC1; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3_SRC1 read error\n"); + } + } + } + + if(sensor->acc_int2_en) { + if((iox0 & (0x1 << SENSOR_IOX_ACC_INT2)) == 0) { + acc_rw.address = SENSOR_A3_SRC2; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3_SRC2 read error\n"); + } + } + } + + acc_rw.address = SENSOR_A3_STAT; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3_STAT read error\n"); + } + + acc_rw.address = SENSOR_A3_OUTX; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3 read (OUTX) error\n"); + } + + acc_rw.address = SENSOR_A3_OUTY; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3 read (OUTY) error\n"); + } + + acc_rw.address = SENSOR_A3_OUTZ; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: A3 read (OUTZ) error\n"); + } + } + } + + if(sensor->eeprom.dcompass_present == SENSOR_DEVICE_PRESENT) { + if(sensor->dcomp_int_en) { + if((iox1 & (0x1 << SENSOR_IOX_DCOMP_INT)) != 0) { + sensor->dcomp_int_en = 0; + sensor->dcomp_int_fl = 1; + // clear interrupts + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_TMPS, &i2c_dummy)) { + printk(KERN_ERR "bmi_sensor.c: TMPS error\n"); + } + + wake_up_all(&sensor->dcomp_wait_queue); + } + } + } + + if(sensor->eeprom.dlight_present == SENSOR_DEVICE_PRESENT) { + if(sensor->dlight_int_en) { + if((iox1 & (0x1 << SENSOR_IOX_PL_INT)) == 0) { + sensor->dlight_int_en = 0; + sensor->dlight_int_fl = 1; + // clear interrupts + if(ReadByte_PL(adap, SENSOR_DL_CONT, &i2c_dummy)) { + printk(KERN_ERR "bmi_sensor.c: DL read error\n"); + } + i2c_dummy &= ~(SENSOR_DL_CONT_INT); + if(WriteByte_PL(adap, SENSOR_DL_CONT, i2c_dummy)) { + printk(KERN_ERR "bmi_sensor.c: DL write error\n"); + } + if(WriteByte_DL_IC(adap)) { + printk(KERN_ERR "bmi_sensor.c: DL interrupt clear error\n"); + } + wake_up_all(&sensor->pl_wait_queue); + } + } + } + + + if((iox0 & (0x1 << SENSOR_IOX_USB_FL_N)) == 0) { + sensor->usb_int_en = 0; + sensor->usb_int_fl = 1; + // disable USB power + if(ReadByte_IOX(adap, IOX_INPUT0_REG, &i2c_dummy)) // clear IOX interrupts + printk(KERN_ERR "bmi_sensor.c: USB IOX read error\n"); + wake_up_all(&sensor->usb_wait_queue); + } + + return; +} + +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + struct bmi_sensor *sensor = (struct bmi_sensor *) dummy; + + schedule_work (&sensor->work_item); + return IRQ_HANDLED; +} + +/* + * BMI functions + */ + +/*------------------------------------- + * + * bmi device sysfs attributes + * + *------------------------------------- + */ + +static ssize_t show_humidity(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_HUMIDITY)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + + return sprintf(buf, "0x%x\n", (adc_data[0] << 8) | adc_data[1]); +} +static DEVICE_ATTR(humidity, S_IRUGO, show_humidity, NULL); + +static ssize_t show_acompass(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char adc_data[2]; + unsigned int compass_x; + unsigned int compass_y; + unsigned int compass_z; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_X)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + compass_x = (adc_data[0] << 8) | adc_data[1]; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Y)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + compass_y = (adc_data[0] << 8) | adc_data[1]; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Z)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + compass_z = (adc_data[0] << 8) | adc_data[1]; + + return sprintf(buf, "X=0x%x\nY=0x%x\nZ=0x%x\n", + compass_x, compass_y, compass_z); +} +static DEVICE_ATTR(acompass, S_IRUGO, show_acompass, NULL); + +static ssize_t show_dcompass(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char compass_i; + unsigned char compass_t; + unsigned char compass_x; + unsigned char compass_y; + unsigned char compass_z; + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_MS1, SENSOR_DCOMP_MS1_SENSOR)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP MS1 write error\n"); + } + + mdelay(20); + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_ST, &compass_i)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + if((compass_i & SENSOR_DCOMP_ST_INT) == 0) { + printk(KERN_ERR "bmi_sensor.c: DCOMP interrupt error\n"); + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_TMPS, &compass_t)) { + printk(KERN_ERR "bmi_sensor.c: TMPS error\n"); + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1X, &compass_x)) { + printk(KERN_ERR "bmi_sensor.c: H1X error\n"); + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1Y, &compass_y)) { + printk(KERN_ERR "bmi_sensor.c: H1Y error\n"); + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1Z, &compass_z)) { + printk(KERN_ERR "bmi_sensor.c: H1Z error\n"); + } + + return sprintf(buf, "T=0x%x\nX=0x%x\nY=0x%x\nZ=0x%x\n", + compass_t, compass_x, compass_y, compass_z); +} +static DEVICE_ATTR(dcompass, S_IRUGO, show_dcompass, NULL); + +static ssize_t show_als(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char pl_data[2]; + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_ALS_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write error\n"); + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_LSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + return sprintf(buf, "0x%x\n", (pl_data[0] << 8) | pl_data[1]); +} +static DEVICE_ATTR(als, S_IRUGO, show_als, NULL); + +static ssize_t show_ir(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char pl_data[2]; + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_IR_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write error\n"); + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_LSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + return sprintf(buf, "0x%x\n", (pl_data[0] << 8) | pl_data[1]); +} +static DEVICE_ATTR(ir, S_IRUGO, show_ir, NULL); + +static ssize_t show_proximity(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char pl_data[2]; + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_PROX_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write error\n"); + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_LSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: PL read error\n"); + } + + return sprintf(buf, "0x%x\n", (pl_data[0] << 8) | pl_data[1]); +} +static DEVICE_ATTR(proximity, S_IRUGO, show_proximity, NULL); + +static ssize_t show_snda(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_AVG)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + + return sprintf(buf, "0x%x\n", (adc_data[0] << 8) | adc_data[1]); +} +static DEVICE_ATTR(sound_avg, S_IRUGO, show_snda, NULL); + +static ssize_t show_sndp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_PEAK)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + + return sprintf(buf, "0x%x\n", (adc_data[0] << 8) | adc_data[1]); +} +static DEVICE_ATTR(sound_peak, S_IRUGO, show_sndp, NULL); + +static ssize_t show_temp_l(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char temp_datam; + unsigned char temp_datal; + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF1_WR, SENSOR_TEMP_CONF1_STOP)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_ONE_SHOT, 0x0)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + mdelay(400); + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (LOCAL MSB) error\n"); + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (LOCAL LSB) error\n"); + } + + return sprintf(buf, "0x%x\n", (temp_datam << 8) | temp_datal); +} +static DEVICE_ATTR(temp_local, S_IRUGO, show_temp_l, NULL); + +static ssize_t show_temp_sr(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char temp_datam; + unsigned char temp_datal; + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF1_WR, SENSOR_TEMP_CONF1_STOP)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_ONE_SHOT, 0x0)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + mdelay(400); + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (REM MSB) error\n"); + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (REM LSB) error\n"); + } + + return sprintf(buf, "0x%x\n", (temp_datam << 8) | temp_datal); +} +static DEVICE_ATTR(temp_sremote, S_IRUGO, show_temp_sr, NULL); + +static ssize_t show_temp_ur(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char temp_datam; + unsigned char temp_datal; + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF1_WR, SENSOR_TEMP_CONF1_STOP)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_ONE_SHOT, 0x0)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + } + + mdelay(400); + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (UREM MSB) error\n"); + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (UREM LSB) error\n"); + } + + return sprintf(buf, "0x%x\n", (temp_datam << 8) | temp_datal); +} +static DEVICE_ATTR(temp_uremote, S_IRUGO, show_temp_ur, NULL); + +static ssize_t show_motion(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + int slot = bmi_device_get_slot(bdev); + + return sprintf(buf, "0x%x\n", bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET)); +} +static DEVICE_ATTR(motion, S_IRUGO, show_motion, NULL); + +static ssize_t show_accel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + int slot = bmi_device_get_slot(bdev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + struct sensor_acc_rw acc_rw; + int x; + int y; + int z; + + if(bmi_sensor[slot].eeprom.accel_present != SENSOR_DEVICE_PRESENT) { + acc_rw.address = SENSOR_ACC_DX0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DX0) error\n"); + } + x = (acc_rw.data[0] << 8) | acc_rw.data[1]; + + acc_rw.address = SENSOR_ACC_DY0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DY0) error\n"); + } + y = (acc_rw.data[0] << 8) | acc_rw.data[1]; + + acc_rw.address = SENSOR_ACC_DZ0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DZ0) error\n"); + } + z = (acc_rw.data[0] << 8) | acc_rw.data[1]; + } else { + acc_rw.address = SENSOR_A3_OUTX; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (OUTX) error\n"); + } + x = acc_rw.data[0]; + + acc_rw.address = SENSOR_A3_OUTY; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (OUTY) error\n"); + } + y = acc_rw.data[0]; + + acc_rw.address = SENSOR_A3_OUTZ; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (OUTZ) error\n"); + } + z = acc_rw.data[0]; + } + return sprintf(buf, "X=0x%x\nY=0x%x\nZ=0x%x\n", x, y, z); +} +static DEVICE_ATTR(accel, S_IRUGO, show_accel, NULL); + +static ssize_t show_aprox(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + int slot = bmi_device_get_slot(bdev); + struct bmi_sensor *sensor = &bmi_sensor[slot]; + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned int read_data; + unsigned char iox_data; + unsigned char aprox_data; + int ret; + + // start burst to LED + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + printk(KERN_ERR "bmi_sensor.c: IOX read error\n"); + iox_data |= (0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + printk(KERN_ERR "bmi_sensor.c: IOX write error\n"); + + // set up timer + sensor->aprox_timer.expires = jiffies + sensor->aprox_duration; + add_timer (&sensor->aprox_timer); + + // wait for timer + ret = down_interruptible(&sensor->sem); + sensor->aprox_int_en = 1; + sensor->aprox_int_fl = 0; + up(&sensor->sem); + wait_event_interruptible(sensor->aprox_wait_queue, (sensor->aprox_int_fl == 1)); + + // stop burst to LED + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + printk(KERN_ERR "bmi_sensor.c: IOX read error\n"); + iox_data &= ~(0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + printk(KERN_ERR "bmi_sensor.c: IOX write error\n"); + + // digital output + read_data = (iox_data & (0x1 << SENSOR_IOX_PROX_OUT)) << 14; + + // read ADC - analog output + if(WriteByte_ADC(adap, SENSOR_ADC_APROXIMITY | SENSOR_ADC_PD_OFF)) + printk(KERN_ERR "bmi_sensor.c: IOX write error\n"); + + mdelay(1); + + if(ReadByte_ADC(adap, &aprox_data)) + printk(KERN_ERR "bmi_sensor.c: IOX read error\n"); + read_data |= aprox_data; + + return sprintf(buf, "Analog Proxiimity = 0x%x\n", read_data); +} +static DEVICE_ATTR(aprox, S_IRUGO, show_aprox, NULL); + +static ssize_t show_alight(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_LIGHT)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + } + + return sprintf(buf, "0x%x\n", (adc_data[0] << 8) | adc_data[1]); +} +static DEVICE_ATTR(alight, S_IRUGO, show_alight, NULL); + +static ssize_t show_dlight(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bmi_device *bdev = dev_to_bmi_device(dev); + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + unsigned char dl_data[2]; + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_MSB, &dl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: DL read error\n"); + } + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_LSB, &dl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: DL read error\n"); + } + + return sprintf(buf, "0x%x\n", (dl_data[0] << 8) | dl_data[1]); +} +static DEVICE_ATTR(dlight, S_IRUGO, show_dlight, NULL); + + +// read calibration/equipage EEPROM +int read_eeprom(struct i2c_adapter *adap, struct sensor_eeprom_raw *eeprom) +{ + unsigned char ee_data; + + if(ReadByte_EE(adap, 0x0, &ee_data)) + return -ENODEV; + eeprom->xsf_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x1, &ee_data)) + return -ENODEV; + eeprom->xsf_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x2, &ee_data)) + return -ENODEV; + eeprom->ysf_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x3, &ee_data)) + return -ENODEV; + eeprom->ysf_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x4, &ee_data)) + return -ENODEV; + eeprom->zsf_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x5, &ee_data)) + return -ENODEV; + eeprom->zsf_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x6, &ee_data)) + return -ENODEV; + eeprom->xoff_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x7, &ee_data)) + return -ENODEV; + eeprom->xoff_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x8, &ee_data)) + return -ENODEV; + eeprom->yoff_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x9, &ee_data)) + return -ENODEV; + eeprom->zoff_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xA, &ee_data)) + return -ENODEV; + eeprom->zoff_msb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xB, &ee_data)) + return -ENODEV; + eeprom->yoff_lsb = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xC, &ee_data)) + return -ENODEV; + eeprom->xdac = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xD, &ee_data)) + return -ENODEV; + eeprom->ydac = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xE, &ee_data)) + return -ENODEV; + eeprom->zdac = (__u8) ee_data; + + if(ReadByte_EE(adap, 0xF, &ee_data)) + return -ENODEV; + eeprom->adc_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x10, &ee_data)) + return -ENODEV; + eeprom->humidity_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x11, &ee_data)) + return -ENODEV; + eeprom->acompass_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x12, &ee_data)) + return -ENODEV; + eeprom->light_proximity_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x13, &ee_data)) + return -ENODEV; + eeprom->sound_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x14, &ee_data)) + return -ENODEV; + eeprom->temperature_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x15, &ee_data)) + return -ENODEV; + eeprom->motion_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x16, &ee_data)) + return -ENODEV; + eeprom->accel_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x17, &ee_data)) + return -ENODEV; + eeprom->dcompass_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x18, &ee_data)) + return -ENODEV; + eeprom->aproximity_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x19, &ee_data)) + return -ENODEV; + eeprom->alight_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x1A, &ee_data)) + return -ENODEV; + eeprom->dlight_present = (__u8) ee_data; + + if(ReadByte_EE(adap, 0x1B, &ee_data)) + return -ENODEV; + eeprom->acc302_present = (__u8) ee_data; + + return 0; +} + +// probe - insert PIM +int bmi_sensor_probe(struct bmi_device *bdev) +{ + int err = 0; + int slot = bmi_device_get_slot(bdev); + struct bmi_sensor *sensor = &bmi_sensor[slot]; + struct i2c_adapter *adap = bmi_device_get_i2c_adapter(bdev); + struct cdev *cdev; + struct class *bmi_class; + dev_t dev_id; + int irq; + unsigned char iox_data; + + sensor->bdev = 0; + + // Create 1 minor device + cdev = &sensor->cdev; + cdev_init(cdev, &cntl_fops); + + dev_id = MKDEV(major, slot); + err = cdev_add(cdev, dev_id, 1); + if(err) { + return err; + } + + // Create class device + bmi_class = bmi_get_bmi_class(); + sensor->class_dev = device_create(bmi_class, NULL, + MKDEV(major, slot), NULL, + "bmi_sensor_cntl_m%i", slot+1); + + if(IS_ERR(sensor->class_dev)) { + printk(KERN_ERR "Unable to create " + "class_device for bmi_sensor_cntl_m%i; errno = %ld\n", + slot+1, PTR_ERR(sensor->class_dev)); + goto error; + } + + // bind driver and bmi_device + sensor->bdev = bdev; + bmi_device_set_drvdata(bdev, sensor); + + printk(KERN_INFO "bmi_sensor.c: probe slot %d\n", slot); + + // configure IOX + if(factory_test || fcc_test) { + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, 0x98)) { // USB/HUM on, MOT_INT off, COMPASS RST High + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, 0x08)) { // Speaker Peak Clear, all other outputs low + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + } else { + if(WriteByte_IOX(adap, IOX_OUTPUT0_REG, 0x80)) { // USB/HUM/MOT_INT off, COMPASS RST High + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, 0x0A)) { // Speaker Peak Clear/analog proximity enable high, all other outputs low + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + } + + if(WriteByte_IOX(adap, IOX_CONTROL0_REG, 0x27)) { // IOX[5,2:0]=IN, IOX[7:6,4:3]=OUT + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + + if(WriteByte_IOX(adap, IOX_CONTROL1_REG, 0xb4)) { // IOX[7,5:4,2]=IN, IOX[6,3,1:0]=OUT + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto error; + } + + // Initialize GPIOs (turn LED's on) + bmi_slot_gpio_configure_as_output(slot, SENSOR_GPIO_RED_LED, SENSOR_GPIO_LED_ON); // Red LED=ON + bmi_slot_gpio_configure_as_output(slot, SENSOR_GPIO_GREEN_LED, SENSOR_GPIO_LED_ON); // Green LED=ON + bmi_slot_gpio_configure_as_input(slot, SENSOR_GPIO_PDOUT); // proximity real-time state + bmi_slot_gpio_configure_as_input(slot, SENSOR_GPIO_MOT_DET); // motion real-time state + + mdelay(200); + + // turn LED's off + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_RED_LED, SENSOR_GPIO_LED_OFF); // Red LED=OFF + bmi_slot_gpio_write_bit(slot, SENSOR_GPIO_GREEN_LED, SENSOR_GPIO_LED_OFF); // Green LED=OFF + + // bmi_sensor initialization + init_MUTEX(&sensor->sem); + sprintf(sensor->work_name, "sensor_m%d", slot + 1); + init_waitqueue_head(&sensor->pl_wait_queue); + sensor->pl_int_en = 0; + sensor->pl_int_fl = 0; + init_waitqueue_head(&sensor->temp_wait_queue); + sensor->temp_int_en = 0; + sensor->temp_int_fl = 0; + init_waitqueue_head(&sensor->mot_wait_queue); + sensor->mot_int_en = 0; + sensor->mot_int_fl = 0; + sensor->mot_state = bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET); // initial motion detector state + init_waitqueue_head(&sensor->acc_wait1_queue); + sensor->acc_int1_en = 0; + sensor->acc_int1_fl = 0; + init_waitqueue_head(&sensor->acc_wait2_queue); + sensor->acc_int2_en = 0; + sensor->acc_int2_fl = 0; + init_waitqueue_head(&sensor->usb_wait_queue); + sensor->usb_int_en = 0; + sensor->usb_int_fl = 0; + init_waitqueue_head(&sensor->dcomp_wait_queue); + sensor->dcomp_int_en = 0; + sensor->dcomp_int_fl = 0; + sensor->aprox_duration = 200; + init_timer(&sensor->aprox_timer); + sensor->aprox_timer.data = (unsigned long) &bmi_sensor[slot]; + sensor->aprox_timer.function = aptimer; + init_waitqueue_head(&sensor->aprox_wait_queue); + sensor->aprox_int_en = 0; + sensor->aprox_int_fl = 0; + + sensor->workqueue = create_singlethread_workqueue(sensor->work_name); + if (!sensor->workqueue) { + printk(KERN_ERR "bmi_sensor.c: Can't create_singlethread_workqueue() in slot %d\n", + slot); + goto error; + } + INIT_WORK(&sensor->work_item, sensor_work_handler); + + // initialize EEPROM for presence + if(factory_test && eeprom_init) { + unsigned char addr = SENSOR_PRESENT_START; + + // presence + while(addr <= SENSOR_PRESENT_END) { + if(eeprom_init & 0x1) { + WriteByte_EE(adap, addr++, SENSOR_DEVICE_PRESENT); + } else { + WriteByte_EE(adap, addr++, SENSOR_DEVICE_NOT_PRESENT); + } + eeprom_init = eeprom_init >> 1; + mdelay(5); + } + } + + if(factory_test && xdac_init) { + WriteByte_EE(adap, SENSOR_EE_XDAC, xdac_init & 0xFF); + mdelay(5); + } + + if(factory_test && ydac_init) { + WriteByte_EE(adap, SENSOR_EE_YDAC, ydac_init & 0xFF); + mdelay(5); + } + + if(factory_test && zdac_init) { + WriteByte_EE(adap, SENSOR_EE_ZDAC, zdac_init & 0xFF); + mdelay(5); + } + + // read EEPROM for calibration/presence + if(read_eeprom(adap, &sensor->eeprom)) { + printk(KERN_ERR "bmi_sensor.c: Can't read calibration EEPROM in slot %d\n", + slot); + goto error; + } + + sensor->comp_xsf = (sensor->eeprom.xsf_msb << 8) | sensor->eeprom.xsf_lsb; + sensor->comp_ysf = (sensor->eeprom.ysf_msb << 8) | sensor->eeprom.ysf_lsb; + sensor->comp_zsf = (sensor->eeprom.zsf_msb << 8) | sensor->eeprom.zsf_lsb; + sensor->comp_xoff = (sensor->eeprom.xoff_msb << 8) | sensor->eeprom.xoff_lsb; + sensor->comp_yoff = (sensor->eeprom.yoff_msb << 8) | sensor->eeprom.yoff_lsb; + sensor->comp_zoff = (sensor->eeprom.zoff_msb << 8) | sensor->eeprom.zoff_lsb; + + if(sensor->eeprom.adc_present == SENSOR_DEVICE_PRESENT) { + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_CH0)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + goto error; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + goto error; + } + + if((adc_data[0] & 0xF0) != 0x0) { + printk(KERN_ERR "bmi_sensor.c: ADC compare error\n"); + goto error; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: ADC present in slot %d\n", slot); + } + } + + if(sensor->eeprom.humidity_present == SENSOR_DEVICE_PRESENT) { + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_HUMIDITY)) { + printk(KERN_ERR "bmi_sensor.c: ADC write (Humidity) error\n"); + goto error; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Humidity) error\n"); + goto error; + } + + printk(KERN_INFO "bmi_sensor.c: initial Humidity = %d\n", + (adc_data[0] << 8) | adc_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_humidity)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (humidity) failed.\n", + slot); + goto sysfs_err1; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: HUMIDITY Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.acompass_present == SENSOR_DEVICE_PRESENT) { + unsigned char adc_data[2]; + unsigned int compass_x; + unsigned int compass_y; + unsigned int compass_z; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_X)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + goto sysfs_err1; + + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + device_remove_file(&sensor->bdev->dev, &dev_attr_humidity); + goto sysfs_err1; + } + compass_x = (adc_data[0] << 8) | adc_data[1]; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Y)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + goto sysfs_err1; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + goto sysfs_err1; + } + compass_y = (adc_data[0] << 8) | adc_data[1]; + + if(WriteByte_ADC(adap, SENSOR_ADC_ACOMPASS_Z)) { + printk(KERN_ERR "bmi_sensor.c: ADC write error\n"); + goto sysfs_err1; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read error\n"); + goto sysfs_err1; + } + compass_z = (adc_data[0] << 8) | adc_data[1]; + + printk(KERN_INFO "bmi_sensor.c: initial COMPASS (X,Y,Z) = %d,%d,%d\n", + compass_x, compass_y, compass_z); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_acompass)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (acompass) failed.\n", + slot); + goto sysfs_err1; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: ACOMPASS Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.dcompass_present == SENSOR_DEVICE_PRESENT) { + unsigned char hxga; + unsigned char hyga; + unsigned char hzga; + unsigned char compass_i; + unsigned char compass_t; + unsigned char compass_x; + unsigned char compass_y; + unsigned char compass_z; + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_MS1, SENSOR_DCOMP_MS1_EEPROM)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_MS1 write error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_EE_EHXGA, &hxga)) { + printk(KERN_ERR "bmi_sensor.c: EE_EHXGA read error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_EE_EHYGA, &hyga)) { + printk(KERN_ERR "bmi_sensor.c: EE_EHYGA read error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_EE_EHZGA, &hzga)) { + printk(KERN_ERR "bmi_sensor.c: EE_EHZGA read error\n"); + goto sysfs_err2; + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_MS1, SENSOR_DCOMP_MS1_PD)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_MS1 write error\n"); + goto sysfs_err2; + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HXGA, hxga)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HXGA write error\n"); + goto sysfs_err2; + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HYGA, hyga)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HYGA write error\n"); + goto sysfs_err2; + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HZGA, hzga)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HZGA write error\n"); + goto sysfs_err2; + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HXDA, sensor->eeprom.xdac)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HXDA write error\n"); + goto sysfs_err2; + + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HYDA, sensor->eeprom.ydac)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HYDA write error\n"); + goto sysfs_err2; + + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_HZDA, sensor->eeprom.zdac)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_HZDA write error\n"); + goto sysfs_err2; + + } + + if(WriteByte_DCOMP(adap, SENSOR_DCOMP_MS1, SENSOR_DCOMP_MS1_SENSOR)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_MS1 write error\n"); + goto sysfs_err2; + + } + + mdelay(20); + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_ST, &compass_i)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_ST read error\n"); + goto sysfs_err2; + } + + if((compass_i & SENSOR_DCOMP_ST_INT) == 0) { + printk(KERN_ERR "bmi_sensor.c: DCOMP interrupt error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_TMPS, &compass_t)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_TMPS error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1X, &compass_x)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_H1X error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1Y, &compass_y)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_H1Y error\n"); + goto sysfs_err2; + } + + if(ReadByte_DCOMP(adap, SENSOR_DCOMP_H1Z, &compass_z)) { + printk(KERN_ERR "bmi_sensor.c: DCOMP_H1Z error\n"); + goto sysfs_err2; + } + + printk(KERN_INFO "bmi_sensor.c: initial COMPASS (T,X,Y,Z) = 0x%x,0x%x,0x%x,0x%x\n", + compass_t, compass_x, compass_y, compass_z); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_dcompass)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (dcompass) failed.\n", + slot); + goto sysfs_err2; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: DCOMPASS Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + unsigned char pl_data[2]; + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_ALS_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write (ALS) error\n"); + goto sysfs_err3; + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: PL read (ALS) error\n"); + goto sysfs_err3; + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: PL read (ALS) error\n"); + goto sysfs_err3; + } + printk(KERN_INFO "bmi_sensor.c: initial PL ALS = %d\n", + (pl_data[0] << 8) | pl_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_als)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (ALS) failed.\n", + slot); + goto sysfs_err3; + } + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_IR_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write (IR) error\n"); + goto sysfs_err4; + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (IR) error\n"); + goto sysfs_err4; + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (IR) error\n"); + goto sysfs_err4; + } + printk(KERN_INFO "bmi_sensor.c: initial IR = %d\n", + (pl_data[0] << 8) | pl_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_ir)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (IR) failed.\n", + slot); + goto sysfs_err4; + } + + if(WriteByte_PL(adap, SENSOR_PL_CMD1, SENSOR_PL_CMD1_PROX_1X)) { + printk(KERN_ERR "bmi_sensor.c: PL write (Proximity) error\n"); + goto sysfs_err5; + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Proximity) error\n"); + goto sysfs_err5; + } + + if(ReadByte_PL(adap, SENSOR_PL_DATA_MSB, &pl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Proximity) error\n"); + goto sysfs_err5; + } + printk(KERN_INFO "bmi_sensor.c: initial Proximity = %d\n", + (pl_data[0] << 8) | pl_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_proximity)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (Proximity) failed.\n", + slot); + goto sysfs_err5; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: Combined LIGHT/PROXIMITY Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.sound_present == SENSOR_DEVICE_PRESENT) { + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_AVG)) { + printk(KERN_ERR "bmi_sensor.c: ADC write (Sound Average) error\n"); + goto sysfs_err6; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Sound Average) error\n"); + goto sysfs_err6; + } + + printk(KERN_INFO "bmi_sensor.c: initial Sound Average = %d\n", + (adc_data[0] << 8) | adc_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_sound_avg)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (Sound Average) failed.\n", + slot); + goto sysfs_err6; + } + + if(WriteByte_ADC(adap, SENSOR_ADC_SOUND_PEAK)) { + printk(KERN_ERR "bmi_sensor.c: ADC write (Sound Peak) error\n"); + goto sysfs_err7; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Sound Peak) error\n"); + goto sysfs_err7; + } + + printk(KERN_INFO "bmi_sensor.c: initial Sound Peak = %d\n", + (adc_data[0] << 8) | adc_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_sound_peak)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (Sound Peak) failed.\n", + slot); + goto sysfs_err7; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: SOUND PRESSURE Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + unsigned char temp_datam; + unsigned char temp_datal; + + if(ReadByte_TEMP(adap, SENSOR_TEMP_MAN_ID, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (Manufacturer ID) error\n"); + goto sysfs_err8; + } + + if(temp_datam != SENSOR_TEMP_MAN_ID_DATA) { + printk(KERN_ERR "bmi_sensor.c: TEMP MAN ID error (read=0x%x, expected=0x%x\n", + temp_datam, SENSOR_TEMP_MAN_ID_DATA); + goto sysfs_err8; + } + + printk(KERN_INFO "bmi_sensor.c: TEMP Manufacturer ID = 0x%x\n", temp_datam); + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REV_ID, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (Revision ID) error\n"); + goto sysfs_err8; + } + + if(temp_datam != SENSOR_TEMP_REV_ID_DATA) { + printk(KERN_ERR "bmi_sensor.c: TEMP REV ID error (read=0x%x, expected=0x%x\n", + temp_datam, SENSOR_TEMP_REV_ID_DATA); + goto sysfs_err8; + } + + printk(KERN_INFO "bmi_sensor.c: TEMP Revision ID = 0x%x\n", temp_datam); + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF2, 0x0)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF2) error\n"); + goto sysfs_err8; + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONF1_WR, SENSOR_TEMP_CONF1_STOP)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + goto sysfs_err8; + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_CONV_WR, SENSOR_TEMP_CONV_P364)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + goto sysfs_err8; + } + + if(WriteByte_TEMP(adap, SENSOR_TEMP_ONE_SHOT, 0x0)) { + printk(KERN_ERR "bmi_sensor.c: TEMP write (CONF1) error\n"); + goto sysfs_err8; + } + + mdelay(400); + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (LOCAL MSB) error\n"); + goto sysfs_err8; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_LOC_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (LOCAL LSB) error\n"); + goto sysfs_err8; + } + + printk(KERN_INFO "bmi_sensor.c: initial Local temperature = 0x%x\n", + (temp_datam << 8) | temp_datal); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_temp_local)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (TEMP local) failed.\n", + slot); + goto sysfs_err8; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (REM MSB) error\n"); + goto sysfs_err9; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_REM_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (REM LSB) error\n"); + goto sysfs_err9; + } + + printk(KERN_INFO "bmi_sensor.c: initial Remote temperature = 0x%x\n", + (temp_datam << 8) | temp_datal); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_temp_sremote)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (TEMP sremote) failed.\n", + slot); + goto sysfs_err9; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_MSB, &temp_datam)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (UREM MSB) error\n"); + goto sysfs_err10; + } + + if(ReadByte_TEMP(adap, SENSOR_TEMP_UREM_LSB, &temp_datal)) { + printk(KERN_ERR "bmi_sensor.c: TEMP read (UREM LSB) error\n"); + goto sysfs_err10; + } + + if(device_create_file(&sensor->bdev->dev, &dev_attr_temp_uremote)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (TEMP uremote) failed.\n", + slot); + goto sysfs_err10; + } + + printk(KERN_INFO "bmi_sensor.c: initial Remote temperature (unsigned) = 0x%x\n", + (temp_datam << 8) | temp_datal); + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: TEMPERATURE Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.motion_present == SENSOR_DEVICE_PRESENT) { + if(device_create_file(&sensor->bdev->dev, &dev_attr_motion)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (motion) failed.\n", + slot); + goto sysfs_err11; + } + + printk(KERN_INFO "bmi_sensor.c: initial Motion state = 0x%x\n", + bmi_slot_gpio_read_bit(slot, SENSOR_GPIO_MOT_DET)); + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: MOTION Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) { + struct sensor_acc_rw acc_rw; + + acc_rw.address = SENSOR_ACC_ID; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (ID) error\n"); + goto sysfs_err12; + } + + if(acc_rw.data[0] != SENSOR_ACC_ID_DATA) { + printk(KERN_ERR "bmi_sensor.c: ACC ID error (read=0x%x, expected=0x%x)\n", + acc_rw.data[0], SENSOR_ACC_ID_DATA); + goto sysfs_err12; + } + + acc_rw.address = SENSOR_ACC_RATE; + acc_rw.count = 1; + acc_rw.data[0] = SENSOR_ACC_RC_3200_1600; + if(WriteByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC write (RATE) error\n"); + goto sysfs_err12; + } + + acc_rw.address = SENSOR_ACC_POWER; + acc_rw.count = 1; + acc_rw.data[0] = SENSOR_ACC_P_NORM; + if(WriteByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC write (RATE) error\n"); + goto sysfs_err12; + } + + acc_rw.address = SENSOR_ACC_DF; + acc_rw.count = 1; + acc_rw.data[0] = SENSOR_ACC_DF_LENGTH; + if(WriteByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC write (DF) error\n"); + goto sysfs_err12; + } + + mdelay(20); + + acc_rw.address = SENSOR_ACC_DX0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DX0) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC X state = 0x%x\n", + (acc_rw.data[0] << 8) | acc_rw.data[1]); + + acc_rw.address = SENSOR_ACC_DY0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DY0) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC Y state = 0x%x\n", + (acc_rw.data[0] << 8) | acc_rw.data[1]); + + acc_rw.address = SENSOR_ACC_DZ0; + acc_rw.count = 2; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC read (DZ0) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC Z state = 0x%x\n", + (acc_rw.data[0] << 8) | acc_rw.data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_accel)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (accel) failed.\n", + slot); + goto sysfs_err12; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: ACCELEROMETER Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT) { + struct sensor_acc_rw acc_rw; + + acc_rw.address = SENSOR_A3_WAI; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 read (WAI) error\n"); + goto sysfs_err12; + } + + if(acc_rw.data[0] != SENSOR_A3_WAI_ID) { + printk(KERN_ERR "bmi_sensor.c: ACC302 ID error (read=0x%x, expected=0x%x)\n", + acc_rw.data[0], SENSOR_A3_WAI_ID); + goto sysfs_err12; + } + + acc_rw.address = SENSOR_A3_CTRL1; + acc_rw.count = 1; + if(factory_test) + acc_rw.data[0] = SENSOR_A3_CTRL1_DR400 | SENSOR_A3_CTRL1_PU | + SENSOR_A3_CTRL1_STP | SENSOR_A3_CTRL1_STM | SENSOR_A3_CTRL1_XYZEN; + else + acc_rw.data[0] = SENSOR_A3_CTRL1_DR400 | SENSOR_A3_CTRL1_PU + | SENSOR_A3_CTRL1_XYZEN; + if(WriteByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 write (RATE) error\n"); + goto sysfs_err12; + } + + acc_rw.address = SENSOR_A3_CTRL3; + acc_rw.count = 1; + acc_rw.data[0] = SENSOR_A3_CTRL3_IL; + if(WriteByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 write (RATE) error\n"); + goto sysfs_err12; + } + + mdelay(20); + + acc_rw.address = SENSOR_A3_OUTX; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 read (OUTX) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC302 X state = 0x%x\n", + acc_rw.data[0]); + + acc_rw.address = SENSOR_A3_OUTY; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 read (OUTY) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC302 Y state = 0x%x\n", + acc_rw.data[0]); + + acc_rw.address = SENSOR_A3_OUTZ; + acc_rw.count = 1; + if(ReadByte_ACC(adap, &acc_rw)) { + printk(KERN_ERR "bmi_sensor.c: ACC302 read (OUTZ) error\n"); + goto sysfs_err12; + } + + printk(KERN_INFO "bmi_sensor.c: initial ACC302 Z state = 0x%x\n", + acc_rw.data[0]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_accel)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (accel) failed.\n", + slot); + goto sysfs_err12; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: ISL302 ACCELEROMETER Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.aproximity_present == SENSOR_DEVICE_PRESENT) { + unsigned char aprox_data; + unsigned int read_data; + int ret; + + // enable sensor + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) { + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto sysfs_err13; + } + iox_data &= ~(0x1 << SENSOR_IOX_PROX_EN_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) { + printk(KERN_ERR "bmi_sensor.c: IOX error in slot %d\n", slot); + goto sysfs_err13; + } + + // start burst to LED + iox_data |= (0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + goto sysfs_err13; + + // set up timer + sensor->aprox_timer.expires = jiffies + sensor->aprox_duration; + add_timer (&sensor->aprox_timer); + + // wait for timer + ret = down_interruptible(&sensor->sem); + sensor->aprox_int_en = 1; + sensor->aprox_int_fl = 0; + up(&sensor->sem); + wait_event_interruptible(sensor->aprox_wait_queue, (sensor->aprox_int_fl == 1)); + + // stop burst to LED + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) + goto sysfs_err13; + iox_data &= ~(0x1 << SENSOR_IOX_PROX_RST_N); + if(WriteByte_IOX(adap, IOX_OUTPUT1_REG, iox_data)) + goto sysfs_err13; + + // digital output + read_data = (iox_data & (0x1 << SENSOR_IOX_PROX_OUT)) << 14; + + // read ADC - analog output + if(WriteByte_ADC(adap, SENSOR_ADC_APROXIMITY | SENSOR_ADC_PD_OFF)) + goto sysfs_err13; + + mdelay(1); + + if(ReadByte_ADC(adap, &aprox_data)) + goto sysfs_err13; + read_data |= aprox_data; + + printk(KERN_INFO "bmi_sensor.c: initial analog proximity = 0x%x\n", read_data); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_aprox)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (aprox) failed.\n", + slot); + goto sysfs_err13; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: Analog PROXIMITY Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.alight_present == SENSOR_DEVICE_PRESENT) { + unsigned char adc_data[2]; + + if(WriteByte_ADC(adap, SENSOR_ADC_LIGHT)) { + printk(KERN_ERR "bmi_sensor.c: ADC write (Analog Light) error\n"); + goto sysfs_err14; + } + + mdelay(1); + + if(ReadByte_ADC(adap, adc_data)) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Humidity) error\n"); + goto sysfs_err14; + } + + printk(KERN_INFO "bmi_sensor.c: initial Analog Light = 0x%x\n", + (adc_data[0] << 8) | adc_data[1]); + + if(device_create_file(&sensor->bdev->dev, &dev_attr_alight)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (alight) failed.\n", + slot); + goto sysfs_err14; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: Analog LIGHT Sensor present in slot %d\n", slot); + } + } + + if(sensor->eeprom.dlight_present == SENSOR_DEVICE_PRESENT) { + unsigned char dl_data[2]; + + if(WriteByte_PL(adap, SENSOR_DL_CMD, SENSOR_DL_CMD_ADC_EN)) { + printk(KERN_ERR "bmi_sensor.c: PL write (Digital Light) error\n"); + goto sysfs_err15; + } + + mdelay(20); + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_MSB, &dl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Digital Light) error\n"); + goto sysfs_err15; + } + + if(ReadByte_PL(adap, SENSOR_DL_SENSOR_MSB, &dl_data[1])) { + printk(KERN_ERR "bmi_sensor.c: ADC read (Digital Light) error\n"); + goto sysfs_err15; + } + printk(KERN_INFO "bmi_sensor.c: initial Digital Light = %d\n", + (dl_data[0] << 8) | dl_data[1]); + + // clear interrupts + if(ReadByte_PL(adap, SENSOR_DL_CONT, &dl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: DL read error\n"); + } + dl_data[0] &= ~(SENSOR_DL_CONT_INT); + if(WriteByte_PL(adap, SENSOR_DL_CONT, dl_data[0])) { + printk(KERN_ERR "bmi_sensor.c: DL write error\n"); + } + if(WriteByte_DL_IC(adap)) { + printk(KERN_ERR "bmi_sensor.c: DL interrupt clear error\n"); + } + + if(device_create_file(&sensor->bdev->dev, &dev_attr_dlight)) { + printk (KERN_ERR + "bmi_sensor.c (%d): attr (Digital Light) failed.\n", + slot); + goto sysfs_err15; + } + + if(factory_test == 1) { + printk(KERN_INFO "bmi_sensor.c: Digital LIGHT Sensor present in slot %d\n", slot); + } + } + + if(ReadByte_IOX(adap, IOX_INPUT0_REG, &iox_data)) { // clear IOX interrupts + printk (KERN_ERR + "bmi_sensor.c(%d): IOX0 interrupt clear fail.\n", + slot); + goto sysfs_err16; + } + + if(ReadByte_IOX(adap, IOX_INPUT1_REG, &iox_data)) { // clear IOX interrupts + printk (KERN_ERR + "bmi_sensor.c(%d): IOX1 interrupt clear fail.\n", + slot); + goto sysfs_err16; + } + + // request PIM interrupt + irq = bmi_device_get_status_irq(bdev); + sprintf(sensor->int_name, "bmi_sensor%d", slot); + //pjg if(request_irq(irq, &module_irq_handler, 0, sensor->int_name, sensor)) { + //pjg printk(KERN_ERR "bmi_sensor.c: Can't allocate irq %d or find Sensor in slot %d\n", + //pjg irq, slot); + //pjg goto sysfs_err16; + //pjg } + + return 0; + +sysfs_err16: + if(sensor->eeprom.dlight_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_dlight); + } +sysfs_err15: + if(sensor->eeprom.alight_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_alight); + } +sysfs_err14: + if(sensor->eeprom.aproximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_aprox); + } +sysfs_err13: + if(sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_accel); + } +sysfs_err12: + if(sensor->eeprom.motion_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_motion); + } +sysfs_err11: + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_uremote); + } +sysfs_err10: + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_sremote); + } +sysfs_err9: + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_local); + } +sysfs_err8: + if(sensor->eeprom.sound_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_sound_peak); + } +sysfs_err7: + if(sensor->eeprom.sound_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_sound_avg); + } +sysfs_err6: + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_proximity); + } +sysfs_err5: + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_ir); + } +sysfs_err4: + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_als); + } +sysfs_err3: + if(sensor->eeprom.dcompass_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_dcompass); + } +sysfs_err2: + if(sensor->eeprom.acompass_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_acompass); + } +sysfs_err1: + if(sensor->eeprom.humidity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_humidity); + } +error: + sensor->class_dev = NULL; + cdev_del(&sensor->cdev); + device_destroy(bmi_class, MKDEV(major, slot)); + bmi_device_set_drvdata(bdev, 0); + sensor->bdev = 0; + printk(KERN_ERR "bmi_sensor.c: probe slot %d FAILED\n", slot); + return -ENODEV; + +} + +// remove PIM +void bmi_sensor_remove(struct bmi_device *bdev) +{ + int slot; + struct bmi_sensor *sensor; + struct class *bmi_class; + int irq; + + slot = bmi_device_get_slot(bdev); + sensor = &bmi_sensor[slot]; + + irq = bmi_device_get_status_irq(bdev); + free_irq(irq, sensor); + + destroy_workqueue(sensor->workqueue); + + if(sensor->eeprom.humidity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_humidity); + } + if(sensor->eeprom.acompass_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_acompass); + } + if(sensor->eeprom.dcompass_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_dcompass); + } + if(sensor->eeprom.light_proximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_als); + device_remove_file(&sensor->bdev->dev, &dev_attr_ir); + device_remove_file(&sensor->bdev->dev, &dev_attr_proximity); + } + if(sensor->eeprom.sound_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_sound_avg); + device_remove_file(&sensor->bdev->dev, &dev_attr_sound_peak); + } + if(sensor->eeprom.temperature_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_local); + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_sremote); + device_remove_file(&sensor->bdev->dev, &dev_attr_temp_uremote); + } + if(sensor->eeprom.motion_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_motion); + } + if(sensor->eeprom.acc302_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_accel); + } + if(sensor->eeprom.accel_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_accel); + } + if(sensor->eeprom.aproximity_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_aprox); + del_timer(&sensor->aprox_timer); + } + if(sensor->eeprom.alight_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_alight); + } + if(sensor->eeprom.dlight_present == SENSOR_DEVICE_PRESENT) { + device_remove_file(&sensor->bdev->dev, &dev_attr_dlight); + } + + bmi_slot_gpio_configure_all_as_inputs(slot); + + bmi_class = bmi_get_bmi_class(); + device_destroy(bmi_class, MKDEV(major, slot)); + + sensor->class_dev = 0; + + cdev_del(&sensor->cdev); + + // de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata(bdev, 0); + sensor->bdev = 0; + + return; +} + +/* + * module routines + */ + +static int __init bmi_sensor_init(void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI SENSOR Driver"); + if(retval) { + return -ENODEV; + } + + major = MAJOR(dev_id); + retval = bmi_register_driver(&bmi_sensor_driver); + if(retval) { + unregister_chrdev_region(dev_id, 4); + return -ENODEV; + } + + if(factory_test) { + printk(KERN_INFO "bmi_sensor.c: Factory Test mode enabled\n"); + if(eeprom_init) + printk(KERN_INFO "bmi_sensor.c: eeprom init = 0x%x\n", eeprom_init); + if(xdac_init) + printk(KERN_INFO "bmi_sensor.c: XDAC init = 0x%x\n", xdac_init); + if(ydac_init) + printk(KERN_INFO "bmi_sensor.c: YDAC init = 0x%x\n", ydac_init); + if(zdac_init) + printk(KERN_INFO "bmi_sensor.c: ZDAC init = 0x%x\n", zdac_init); + } + + if(fcc_test) + printk(KERN_INFO "bmi_sensor.c: FCC Test mode enabled\n"); + + printk(KERN_INFO "bmi_sensor.c: BMI_SENSOR Driver v%s \n", BMISENSOR_VERSION); + + return 0; +} + +static void __exit bmi_sensor_cleanup(void) +{ + dev_t dev_id; + + bmi_unregister_driver(&bmi_sensor_driver); + + dev_id = MKDEV(major, 0); + unregister_chrdev_region(dev_id, 4); + return; +} + +module_init(bmi_sensor_init); +module_exit(bmi_sensor_cleanup); + +module_param(factory_test, ushort, S_IRUGO); +MODULE_PARM_DESC(factory_test, "Factory Test code enable"); + +module_param(eeprom_init, int, S_IRUGO); +MODULE_PARM_DESC(eeprom_init, "Factory presence EEPROM programming"); + +module_param(xdac_init, ushort, S_IRUGO); +MODULE_PARM_DESC(xdac_init, "Factory EEPROM XDAC programming"); + +module_param(ydac_init, ushort, S_IRUGO); +MODULE_PARM_DESC(ydac_init, "Factory EEPROM YDAC programming"); + +module_param(zdac_init, ushort, S_IRUGO); +MODULE_PARM_DESC(zdac_init, "Factory EEPROM ZDAC programming"); + +module_param(fcc_test, ushort, S_IRUGO); +MODULE_PARM_DESC(fcc_test, "FCC Test code enable"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Giacomini "); +MODULE_DESCRIPTION("BMI Sensor device driver"); +MODULE_SUPPORTED_DEVICE("bmi_sensor_cntl_mX"); + --- /dev/null +++ git/drivers/bmi/pims/sound/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BMI_AUDIO) += bmi_audio.o + --- /dev/null +++ git/drivers/bmi/pims/sound/bmi_audio.c @@ -0,0 +1,4434 @@ +/* + * bmi_audio.c + * + * BMI audio device driver for audio PIMs + * + * The BMI Audio driver and this API were developed to support + * audio playback and record on the BUG audio PIMs. + * + * The following operating modes are supported: + * + * Operating Mode PIM + * ---------------------------- ------- + * Stereo DAC Playback Yes + * Stereo ADC Record Yes + * Output Amplifier Control Yes + * Input Mixer Control Yes + * + * This file also implements the sound driver mixer interface for ALSA. + * + * Playback and Recording supports 11025, 22050, and 44100 kHz for stereo. + * + * Future Enhanement Options: + * 48000-based rates + * efx control + * I2S -> TDM mode + * power management + */ + +/* + * This code was derived from the following sources: + * + * @file pmic_audio.c + * @file mxc-alsa-mixer.c + * @file mxc-alsa-pmic.c + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#undef CODEC // disable CODEC access for code testing +#define CODEC // enable CODEC access for code testing + +#define DEBUG // enable debug printk +#undef DEBUG // disable debug printk + +#ifdef DEBUG + +# define DDPRINTK(fmt, args...) printk(KERN_ERR"%s :: %d :: %s - " \ + fmt, __FILE__,__LINE__,__FUNCTION__ , ## args) +# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +# define PRINTK(fmt) printk(KERN_INFO fmt) + +# define FUNC_START DPRINTK(" func start\n") +# define FUNC_END DPRINTK(" func end\n") + +# define FUNC_ERR printk(KERN_ERR"%s :: %d :: %s err= %d \n", \ + __FILE__,__LINE__,__FUNCTION__ ,err) + +#else // DEBUG + +#define DDPRINTK(fmt, args...) do {} while(0) +#define DPRINTK(fmt, args...) do {} while(0) +#define PRINTK(fmt) do {} while(0) + +#endif // DEBUG + +#include +#include +#include +#include +#include + +// BMI/BUG interfaces +#include +#include +#include +#include +#include + +// control interface - LED/RESET/MODULE ACTIVATION +#include +#include +#include +#include + +// I2C interface - IOX/CODEC +#include +#include + +// Input interface - BUTTONS/JACKS +#include +#include + +// includes from mxc-alsa-mixer.c: +#include +#include +#include +#include +#include +#include +#include "../../../mxc/bug_audio/bug-alsa-common.h" + +// includes from mxc-alsa-pmic.c: +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +#include +#endif // CONFIG_PM + +#include +#include +#include +#include + +#include "../../../../arch/arm/mach-mx3/crm_regs.h" +#include "../../../mxc/ssi/ssi.h" +#include "../../../mxc/ssi/registers.h" +#include "../../../mxc/dam/dam.h" + +// Global variables +static ushort fcc_test = 0; +static ushort output_ints = 0; + +// BMI defines +#define BMIAUDIO_VERSION "1.0" +#define MAX_STRG (20) +#define WORK_DELAY (1) // input debounce + +// BMI private device structure +struct bmi_audio +{ + struct bmi_device *bdev; // BMI device + struct cdev cdev; // control character device + struct device *class_dev; // control device class + unsigned int active; // PIM active + unsigned int irq; // interrupt number + char int_name[MAX_STRG]; // interrupt name for /proc + struct input_dev *input_dev; // button/insertion input device +}; + +static struct bmi_audio bmi_audio[BMI_AUDIO_PIM_NUM]; // PIM structures +static int major; // control device major + +// +// I2C +// + +// I2C Slave Addresses +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define BMI_CODEC_I2C_ADDRESS 0x18 // 7-bit address + +// I2C IOX register definitions +#define IOX_INPUT_REG 0x0 // IOX input data register +#define IOX_OUTPUT_REG 0x1 // IOX output data register +#define IOX_POLARITY_REG 0x2 // IOX polarity data register +#define IOX_CONTROL 0x3 // IOX direction control register +#define IOX_AMP (0) // bit 0 - amplifier off (O - low active) +#define IOX_SPARE (1) // bit 1 - spare +#define IOX_VOLP (2) // bit 2 - VOLP (I - interrupt) +#define IOX_VOLD (3) // bit 3 - VOLD (I - interrupt) +#define IOX_HP_INS (4) // bit 4 - HP_INS (I - interrupt) +#define IOX_MIC_INS (5) // bit 5 - MIC_INS (I - interrupt) +#define IOX_LI_INS (6) // bit 6 - LI_INS (I - interrupt) +#define IOX_LO_INS (7) // bit 7 - LO_INS (I - interrupt) + +// +// I2C routines +// + +// read byte from I2C IO expander +static int ReadByte_IOX (struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// write byte to I2C IO expander +static int WriteByte_IOX (struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +#ifdef CODEC +// read byte from I2C CODEC +static int ReadByte_CODEC (struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + // Read Byte with Pointer + rmsg[0].addr = BMI_CODEC_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_CODEC_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer (adap, rmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "ReadByte_CODEC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} + +// write byte to I2C CODEC +static int WriteByte_CODEC (struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_CODEC_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_CODEC_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + + ret = i2c_transfer (adap, wmsg, num_msgs); + + if (ret == 2) { + ret = 0; + } + else { + printk (KERN_ERR "WriteByte_CODEC() - i2c_transfer() failed.\n"); + ret = -1; + } + return ret; +} +#endif // CODEC + +// +// control cdev routines +// + +// open +int cntl_open (struct inode *inode, struct file *file) +{ + struct bmi_audio *audio; + + audio = container_of (inode->i_cdev, struct bmi_audio, cdev); + file->private_data = audio; + return 0; + +} + +// release +int cntl_release (struct inode *inode, struct file *file) +{ + file->private_data = 0; + return 0; +} + +static int configure_CODEC(struct i2c_adapter *adap, struct bmi_audio *audio); + +// ioctl +int cntl_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct bmi_audio *audio = (struct bmi_audio *) (file->private_data); + struct codec_xfer codec_xfer; + struct i2c_adapter *adap; + unsigned char iox_data; + int slot; + + // error if audio/bdev not present + if (audio == 0) + return -ENODEV; + + if (audio->bdev == 0) + return -ENODEV; + + adap = bmi_device_get_i2c_adapter (audio->bdev); + slot = bmi_device_get_slot (audio->bdev); + + // get codec transfer structure + if ((cmd == BMI_AUDIO_WCODEC) || (cmd == BMI_AUDIO_RCODEC)) { + if (copy_from_user (&codec_xfer, (struct codec_xfer *) arg, sizeof(struct codec_xfer))) { + printk (KERN_INFO "bmi_audio.c: ioctl(%d): copy_from_user error\n", slot); + return -EFAULT; + } + } + + // ioctl's + switch (cmd) { + + case BMI_AUDIO_RLEDOFF: + bmi_slot_gpio_write_bit (slot, GPIO_RED, BMI_GPIO_ON); // Red LED=OFF + break; + + case BMI_AUDIO_RLEDON: + bmi_slot_gpio_write_bit (slot, GPIO_RED, BMI_GPIO_OFF); // Red LED=ON + break; + + case BMI_AUDIO_GLEDOFF: + bmi_slot_gpio_write_bit (slot, GPIO_GREEN, BMI_GPIO_ON); // Greem LED=OFF + break; + + case BMI_AUDIO_GLEDON: + bmi_slot_gpio_write_bit (slot, GPIO_GREEN, BMI_GPIO_OFF); // Greem LED=ON + break; + + case BMI_AUDIO_SPKON: + { + printk(KERN_INFO "BMI_AUDIO: SPKOFF Called...\n"); + if (ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + printk(KERN_INFO "BMI_AUDIO: IOX Output Read: 0x%x\n", iox_data); + if (WriteByte_IOX (adap, IOX_OUTPUT_REG, (iox_data | (1 << IOX_AMP)))) + return -ENODEV; + } + break; + case BMI_AUDIO_SPKOFF: + { + printk(KERN_INFO "BMI_AUDIO: SPKON Called...\n"); + if (ReadByte_IOX (adap, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + printk(KERN_INFO "BMI_AUDIO: IOX Output Read: 0x%x\n", iox_data); + if (WriteByte_IOX (adap, IOX_OUTPUT_REG, (iox_data & ~(1 << IOX_AMP)))) + return -ENODEV; + } + break; + case BMI_AUDIO_SETRST: + bmi_slot_gpio_configure_as_output (slot, GPIO_RESET, BMI_GPIO_OFF); // RST = 0; + break; + + case BMI_AUDIO_CLRRST: + bmi_slot_gpio_configure_as_output (slot, GPIO_RESET, BMI_GPIO_ON); // RST = 1; + break; + + case BMI_AUDIO_GETSTAT: + { + int read_data; + + if(ReadByte_IOX (adap, IOX_INPUT_REG, &iox_data)) + return -ENODEV; + + read_data = iox_data | (bmi_read_gpio_data_reg(slot) << 8); + + if(put_user(read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_AUDIO_ACTIVATE: + audio->active = 0; + switch (slot) { + case 0: + if (bmi_audio[2].active == 0) { + audio->active = 1; + } + break; + case 1: + if (bmi_audio[3].active == 0) { + audio->active = 1; + } + break; + case 2: + if (bmi_audio[0].active == 0) { + audio->active = 1; + } + break; + case 3: + if (bmi_audio[1].active == 0) { + audio->active = 1; + } + break; + } +#ifdef CODEC + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (audio->active) { // power-up ADC + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, CODEC_L_PGA(0x00) | CODEC_LX_PGA_PU)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, CODEC_L_PGA(0x00) | CODEC_LX_PGA_PU)) + return -ENODEV; + configure_CODEC(adap, audio); + } else { // power-down ADC + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, 0x38)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, 0x38)) + return -ENODEV; + } +#endif // CODEC + + case BMI_AUDIO_DEACTIVATE: +#ifdef CODEC + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + // power-down ADC + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, 0x38)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, 0x38)) + return -ENODEV; +#endif // CODEC + audio->active = 0; + break; + + case BMI_AUDIO_WCODEC: +#ifdef CODEC + // write page register + if (WriteByte_CODEC (adap, 0x0, codec_xfer.page)) + return -ENODEV; + // write codec register + if (WriteByte_CODEC (adap, codec_xfer.reg, codec_xfer.data)) + return -ENODEV; +#endif // CODEC + break; + + case BMI_AUDIO_RCODEC: +#ifdef CODEC + // write page register + if (WriteByte_CODEC (adap, 0x0, codec_xfer.page)) + return -ENODEV; + // read codec register + if (ReadByte_CODEC (adap, codec_xfer.reg, &codec_xfer.data)) + return -ENODEV; + if (copy_to_user ((struct codec_xfer *) arg, &codec_xfer, + sizeof(struct codec_xfer))) + return -EFAULT; +#endif // CODEC + break; + + default: + return -ENOTTY; + } + + return 0; +} + +// control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + +// functionality from mxc-alsa-pmic.h: +#define DAM_PORT_4 port_4 +#define DAM_PORT_5 port_5 +#define TX_WATERMARK 0x4 +#define RX_WATERMARK 0x6 + +// functionality from mxc-alsa-pmic.c: + +/* + * driver buffer policy. + * Customize here if the sound is not correct + */ +#define MAX_BUFFER_SIZE (32*1024) +#define DMA_BUF_SIZE (8*1024) +#define MIN_PERIOD_SIZE 64 +#define MIN_PERIOD 2 +#define MAX_PERIOD 255 + +#define AUD_MUX_CONF 0x0031010 +#define MASK_2_TS 0xfffffffc +#define SOUND_CARD13_NAME "PIM_AUDIO13" +#define SOUND_CARD24_NAME "PIM_AUDIO24" + +// These defines enable DMA chaining for playback and capture respectively. +#define MXC_SOUND_PLAYBACK_CHAIN_DMA_EN 1 +#define MXC_SOUND_CAPTURE_CHAIN_DMA_EN 1 + +// ID for the PIM cards +static char id13[] = "PIM_AUDIO13"; // slots 1 & 3 +static char id24[] = "PIM_AUDIO24"; // slots 2 & 4 + +#define MXC_ALSA_MAX_PCM_DEV 1 +#define MXC_ALSA_MAX_PLAYBACK 1 +#define MXC_ALSA_MAX_CAPTURE 1 + +#define PLAYBACK_STREAM (0) +#define CAPTURE_STREAM (1) + +#define NUM_CARDS (2) + +/* + * This structure is the global configuration of the soundcard + * that are accessed by the mixer as well as by the playback/recording + * stream. This contains various settings. + */ +typedef struct audio_mixer_control { + + /* + * This variable holds the current volume for ouput devices + */ + int vol_for_output[OP_MAXDEV]; + + /* + * This variable holds the current volume for input devices + */ + int vol_for_input[IP_MAXDEV]; + + /* + * This variable holds the current volume for playback devices. + */ + int master_volume_out; + + /* + * These variables holds the current state of the jack indicators + */ + int hp_indicator; + int mic_indicator; + int li_indicator; + int lo_indicator; + + /* + * Semaphore used to control the access to this structure. + */ + struct semaphore sem; + + /* + * These variables are set by PCM stream + */ + int codec_playback_active; + int codec_capture_active; + +} audio_mixer_control_t; + +/* + * This structure represents an audio stream in term of + * channel DMA, HW configuration on CODEC and AudioMux/SSI + */ +typedef struct audio_stream { + /* + * identification string + */ + char *id; + + /* + * numeric identification + */ + int stream_id; + + /* + * SSI ID on the ARM side + */ + int ssi; + + /* + * DAM port on the ARM side + */ + int dam_port; + + /* + * device identifier for DMA + */ + int dma_wchannel; + + /* + * we are using this stream for transfer now + */ + int active:1; + + /* + * current transfer period + */ + int period; + + /* + * current count of transfered periods + */ + int periods; + + /* + * are we recording - flag used to do DMA trans. for sync + */ + int tx_spin; + + /* + * Previous offset value for resume + */ + unsigned int old_offset; + + /* + * for locking in DMA operations + */ + spinlock_t dma_lock; + + /* + * Alsa substream pointer + */ + struct snd_pcm_substream *stream; + +} audio_stream_t; + +/* + * This structure represents the CODEC sound card with its + * streams and its shared parameters + */ +typedef struct snd_card_mxc_bmi_audio { + /* + * ALSA sound card handle + */ + struct snd_card *card; + + /* + * ALSA pcm driver type handle + */ + struct snd_pcm *pcm[MXC_ALSA_MAX_PCM_DEV]; + + /* + * playback & capture streams handle + * We can support a maximum of 1 playback streams + * We can support a maximum of 1 capture streams + */ + audio_stream_t s[MXC_ALSA_MAX_CAPTURE + MXC_ALSA_MAX_PLAYBACK]; + +} mxc_bmi_audio_t; + +/* + * bmi audio chip parameters for IP/OP and volume controls + */ +audio_mixer_control_t audio_mixer_control[NUM_CARDS]; + +/* + * Global variable that represents the CODEC soundcard. + */ +mxc_bmi_audio_t *mxc_audio[NUM_CARDS] = { NULL, NULL }; + +/* + * Supported playback rates array + */ +static unsigned int playback_rates_stereo[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 88200, + 96000 +}; + +/* + * Supported capture rates array + */ +static unsigned int capture_rates_stereo[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 88200, + 96000 +}; + +/* + * this structure represents the sample rates supported + * by CODEC for playback operations on DAC. + */ +static struct snd_pcm_hw_constraint_list hw_playback_rates_stereo = { + .count = ARRAY_SIZE(playback_rates_stereo), + .list = playback_rates_stereo, + .mask = 0, +}; + +/* + * this structure represents the sample rates supported + * by capture operations on ADC. + */ +static struct snd_pcm_hw_constraint_list hw_capture_rates_stereo = { + .count = ARRAY_SIZE(capture_rates_stereo), + .list = capture_rates_stereo, + .mask = 0, +}; + +/* + * This function configures audio multiplexer to support + * audio data routing in CODEC slave mode. + * + * @param ssi SSI of the ARM to connect to the DAM. + */ +void configure_dam_bmi_master(int ssi) +{ + int source_port; + int target_port; + + if (ssi == SSI1) { + PRINTK("DAM: port 1 -> port 4\n"); + source_port = port_1; + target_port = port_4; + } else { + PRINTK("DAM: port 2 -> port 5\n"); + source_port = port_2; + target_port = port_5; + } + + dam_reset_register (source_port); + dam_reset_register (target_port); + + dam_select_mode (source_port, normal_mode); + dam_select_mode (target_port, normal_mode); + + dam_set_synchronous (source_port, true); + dam_set_synchronous (target_port, true); + + dam_select_RxD_source (source_port, target_port); + dam_select_RxD_source (target_port, source_port); + + dam_select_TxFS_direction (source_port, signal_in); + dam_select_TxFS_direction (target_port, signal_out); + dam_select_TxFS_source (target_port, false, source_port); + + dam_select_TxClk_direction (source_port, signal_in); + dam_select_TxClk_direction (target_port, signal_out); + dam_select_TxClk_source (target_port, false, source_port); + + dam_select_RxFS_direction (source_port, signal_in); + dam_select_RxFS_direction (target_port, signal_out); + dam_select_RxFS_source (source_port, false, target_port); + dam_select_RxFS_source (target_port, false, source_port); + + dam_select_RxClk_direction (source_port, signal_in); + dam_select_RxClk_direction (target_port, signal_out); + dam_select_RxClk_source (source_port, false, target_port); + dam_select_RxClk_source (target_port, false, source_port); +} + +void ssi_rx_sampleRate (int ssi, int samplerate) { + struct clk *ssi_clk; + + ssi_rx_clock_divide_by_two (ssi, false); + ssi_rx_clock_prescaler (ssi, false); + ssi_rx_frame_rate (ssi, 2); + + ssi_clk = clk_get (NULL, "usb_pll.0"); + + if (ssi == SSI1) { + ssi_clk = clk_get (NULL, "ssi_clk.0"); + } else { + ssi_clk = clk_get (NULL, "ssi_clk.1"); + } + + clk_set_rate (ssi_clk, clk_round_rate (ssi_clk, 11289600)); + switch (samplerate) { + case 8000: + ssi_rx_prescaler_modulus (ssi, 17); + break; + case 11025: + ssi_rx_prescaler_modulus (ssi, 12); + break; + case 16000: + ssi_rx_prescaler_modulus (ssi, 8); + break; + case 22050: + ssi_rx_prescaler_modulus (ssi, 6); + break; + default: + if (samplerate != 44100) + printk (KERN_ERR + "ssi_rx_sampleRate(): samplerate=%d not supported (default to 44100).\n", + samplerate); + + ssi_rx_prescaler_modulus (ssi, 3); + break; + } +} + +void ssi_tx_sampleRate (int ssi, int samplerate) { + struct clk *ssi_clk; + + ssi_tx_clock_divide_by_two (ssi, false); + ssi_tx_clock_prescaler (ssi, false); + ssi_tx_frame_rate (ssi, 2); + + ssi_clk = clk_get (NULL, "usb_pll.0"); + + if (ssi == SSI1) { + ssi_clk = clk_get (NULL, "ssi_clk.0"); + } else { + ssi_clk = clk_get (NULL, "ssi_clk.1"); + } + + clk_set_rate (ssi_clk, clk_round_rate (ssi_clk, 11289600)); + switch (samplerate) { + case 8000: + ssi_tx_prescaler_modulus (ssi, 17); + break; + case 11025: + ssi_tx_prescaler_modulus (ssi, 12); + break; + case 16000: + ssi_tx_prescaler_modulus (ssi, 8); + break; + case 22050: + ssi_tx_prescaler_modulus (ssi, 6); + break; + default: + if (samplerate != 44100) + printk (KERN_ERR + "ssi_tx_sampleRate(): samplerate=%d not supported (default to 44100).\n", + samplerate); + + ssi_tx_prescaler_modulus (ssi, 3); + break; + } +} + +/* + * This function configures the SSI in order to receive audio + * from CODEC (recording). Configuration of SSI consists mainly in + * setting the following: + * + * 1) SSI to use (SSI1 or SSI2) + * 2) SSI mode (normal or network. We use always network mode) + * 3) SSI STCCR register settings, which control the sample rate (BCL and + * FS clocks) + * 4) Watermarks for SSI FIFOs as well as timeslots to be used. + * 5) Enable SSI. + * + * @param substream pointer to the structure of the current stream. + */ +void configure_ssi_rx (int ssi) +{ + DPRINTK("configure_ssi_rx: SSI%d\n", ssi + 1); + + // receive set up + ssi_rx_shift_direction (ssi, ssi_msb_first); + ssi_rx_clock_polarity (ssi, ssi_clock_on_falling_edge); + ssi_rx_frame_sync_active (ssi, ssi_frame_sync_active_low); + ssi_rx_frame_sync_length (ssi, ssi_frame_sync_one_word); + ssi_rx_early_frame_sync (ssi, ssi_frame_sync_one_bit_before); + ssi_rx_word_length (ssi, ssi_16_bits); + ssi_rx_bit0 (ssi, false); + + // FIFO set up + ssi_rx_fifo_full_watermark (ssi, ssi_fifo_0, RX_WATERMARK); + ssi_rx_fifo_enable (ssi, ssi_fifo_0, true); +} + +/* + * This function configures the SSI in order to + * send data to CODEC. Configuration of SSI consists + * mainly in setting the following: + * + * 1) SSI to use (SSI1 or SSI2) + * 2) SSI mode (normal for normal use e.g. playback, network for mixing) + * 3) SSI STCCR register settings, which control the sample rate (BCL and + * FS clocks) + * 4) Watermarks for SSI FIFOs as well as timeslots to be used. + * 5) Enable SSI. + * + * @param substream pointer to the structure of the current stream. + */ +void configure_ssi_tx (int ssi) +{ + DPRINTK("configure_ssi_tx: SSI%d\n", ssi + 1); + + // disable SSI + ssi_clock_off (ssi, false); + + // I2S master, 16 bits, 44.1 KHz + // basic I2S settings + ssi_network_mode (ssi, true); + ssi_synchronous_mode (ssi, true); + ssi_tx_shift_direction (ssi, ssi_msb_first); + ssi_tx_clock_polarity (ssi, ssi_clock_on_falling_edge); + ssi_tx_frame_sync_active (ssi, ssi_frame_sync_active_low); + ssi_tx_frame_sync_length (ssi, ssi_frame_sync_one_word); + ssi_tx_early_frame_sync (ssi, ssi_frame_sync_one_bit_before); + ssi_tx_word_length (ssi, ssi_16_bits); + ssi_tx_bit0 (ssi, false); + + // clocks are being provided by SSI + ssi_tx_frame_direction (ssi, ssi_tx_rx_internally); + ssi_tx_clock_direction (ssi, ssi_tx_rx_internally); + + // FIFO set up + ssi_tx_fifo_enable (ssi, ssi_fifo_0, true); + ssi_tx_fifo_empty_watermark (ssi, ssi_fifo_0, TX_WATERMARK); + + // Clocking + ssi_tx_sampleRate (ssi, 44100); + ssi_system_clock (ssi, true); + + // enable ssi + ssi_i2s_mode (ssi, i2s_master); + ssi_enable (ssi, true); +} + +/* + * This function configures number of channels for next audio operation + * (recording/playback) Number of channels define if sound is stereo + * or mono. + * + * @param substream pointer to the structure of the current stream. + * + */ +void set_bmi_channels (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + audio_stream_t *s; + + chip = snd_pcm_substream_chip (substream); + s = &chip->s[substream->pstr->stream]; + + ssi_tx_mask_time_slot (s->ssi, MASK_2_TS); + ssi_rx_mask_time_slot (s->ssi, MASK_2_TS); +} + +/* + * This function configures the DMA channel used to transfer + * audio from MCU to CODEC + * + * @param substream pointer to the structure of the current stream. + * @param callback pointer to function that will be + * called when a SDMA TX transfer finishes. + * + * @return 0 on success, -1 otherwise. + */ +static int +configure_write_channel (audio_stream_t *s, mxc_dma_callback_t callback, int stream_id) +{ + int ret = -1; + int channel = -1; + + if (s->ssi == SSI1) + channel = mxc_dma_request (MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA1"); + else + channel = mxc_dma_request (MXC_DMA_SSI2_16BIT_TX0, "ALSA TX DMA2"); + + if (channel < 0) { + PRINTK("error requesting a write dma channel\n"); + return -1; + } + + ret = mxc_dma_callback_set (channel, (mxc_dma_callback_t) callback, (void *) s); + if (ret != 0) { + mxc_dma_free (channel); + return -1; + } + s->dma_wchannel = channel; + + return 0; +} + +/* + * This function configures the DMA channel used to transfer + * audio from CODEC to MCU + * + * @param substream pointer to the structure of the current stream. + * @param callback pointer to function that will be + * called when a SDMA RX transfer finishes. + * + * @return 0 on success, -1 otherwise. + */ +static int configure_read_channel (audio_stream_t *s, + mxc_dma_callback_t callback) +{ + int ret = -1; + int channel = -1; + + if (s->ssi == SSI1) + channel = mxc_dma_request (MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA1"); + else + channel = mxc_dma_request (MXC_DMA_SSI2_16BIT_RX0, "ALSA RX DMA2"); + + if (channel < 0) { + PRINTK("error requesting a read dma channel\n"); + return -1; + } + + ret = + mxc_dma_callback_set (channel, (mxc_dma_callback_t) callback, + (void *) s); + if (ret != 0) { + mxc_dma_free (channel); + return -1; + } + s->dma_wchannel = channel; + + return 0; +} + +/* + * This function frees the stream structure + * + * @param s pointer to the structure of the current stream. + */ +static void audio_dma_free (audio_stream_t *s) +{ + /* + * There is nothing to be done here since the dma channel has been + * freed either in the callback or in the stop method + */ +} + +/* + * This function gets the dma pointer position during record. + * Our DMA implementation does not allow to retrieve this position + * when a transfer is active, so, it answers the middle of + * the current period beeing transfered + * + * @param s pointer to the structure of the current stream. + * + */ +static u_int audio_get_capture_dma_pos (audio_stream_t *s) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int offset; + + substream = s->stream; + runtime = substream->runtime; + offset = 0; + + // tx_spin value is used here to check if a transfer is active + if (s->tx_spin) { + offset = (runtime->period_size * (s->periods)) + 0; + if (offset >= runtime->buffer_size) + offset = 0; + DPRINTK("MXC: audio_get_dma_pos offset %d\n", offset); + } else { + offset = (runtime->period_size * (s->periods)); + if (offset >= runtime->buffer_size) + offset = 0; + DPRINTK("MXC: audio_get_dma_pos BIS offset %d\n", offset); + } + + return offset; +} + +/* + * This function gets the dma pointer position during playback. + * Our DMA implementation does not allow to retrieve this position + * when a transfer is active, so, it answers the middle of + * the current period beeing transfered + * + * @param s pointer to the structure of the current stream. + * + */ +static u_int audio_get_playback_dma_pos (audio_stream_t *s) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int offset; + + substream = s->stream; + runtime = substream->runtime; + offset = 0; + + // tx_spin value is used here to check if a transfer is active + if (s->tx_spin) { + offset = (runtime->period_size * (s->periods)) + 0; + if (offset >= runtime->buffer_size) + offset = 0; + DPRINTK("MXC: audio_get_dma_pos offset %d\n", offset); + } else { + offset = (runtime->period_size * (s->periods)); + if (offset >= runtime->buffer_size) + offset = 0; + DPRINTK("MXC: audio_get_dma_pos BIS offset %d\n", offset); + } + + return offset; +} + +/* + * This function stops the current dma transfer for playback + * and clears the dma pointers. + * + * @param substream pointer to the structure of the current stream. + * + */ +static void audio_playback_stop_dma (audio_stream_t *s) +{ + unsigned long flags; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size; + unsigned int offset; + + substream = s->stream; + runtime = substream->runtime; + dma_size = frames_to_bytes (runtime, runtime->period_size); + offset = dma_size * s->periods; + + spin_lock_irqsave (&s->dma_lock, flags); + + PRINTK("MXC : audio_stop_dma active = 0\n"); + s->active = 0; + s->period = 0; + s->periods = 0; + + // this stops the dma channel and clears the buffer ptrs + mxc_dma_disable (s->dma_wchannel); + dma_unmap_single (NULL, runtime->dma_addr + offset, dma_size, + DMA_TO_DEVICE); + + spin_unlock_irqrestore (&s->dma_lock, flags); +} + +/* + * This function stops the current dma transfer for capture + * and clears the dma pointers. + * + * @param substream pointer to the structure of the current stream. + * + */ +static void audio_capture_stop_dma (audio_stream_t *s) +{ + unsigned long flags; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size; + unsigned int offset; + + substream = s->stream; + runtime = substream->runtime; + dma_size = frames_to_bytes (runtime, runtime->period_size); + offset = dma_size * s->periods; + + spin_lock_irqsave (&s->dma_lock, flags); + + PRINTK("MXC : audio_stop_dma active = 0\n"); + s->active = 0; + s->period = 0; + s->periods = 0; + + // this stops the dma channel and clears the buffer ptrs + mxc_dma_disable (s->dma_wchannel); + dma_unmap_single (NULL, runtime->dma_addr + offset, dma_size, + DMA_FROM_DEVICE); + + spin_unlock_irqrestore (&s->dma_lock, flags); +} + +/* + * This function is called whenever a new audio block needs to be + * transferred to CODEC. The function receives the address and the size + * of the new block and start a new DMA transfer. + * + * @param substream pointer to the structure of the current stream. + * + */ +static void audio_playback_dma (audio_stream_t *s) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size = 0; + unsigned int offset; + int ret = 0; + mxc_dma_requestbuf_t dma_request; + int device; + + substream = s->stream; + runtime = substream->runtime; + device = substream->pcm->device; + + DPRINTK("\nDMA direction %d\(0 is playback 1 is capture)\n", + s->stream_id); + + memset (&dma_request, 0, sizeof (mxc_dma_requestbuf_t)); + + if (s->active) { + if (ssi_get_status(s->ssi) & ssi_transmitter_underrun_0) { + ssi_enable (s->ssi, false); + ssi_transmit_enable (s->ssi, false); + ssi_enable (s->ssi, true); + } + dma_size = frames_to_bytes (runtime, runtime->period_size); + DPRINTK("s->period (%x) runtime->periods (%d)\n", + s->period, runtime->periods); + DPRINTK("runtime->period_size (%d) dma_size (%d)\n", + (unsigned int) runtime->period_size, + runtime->dma_bytes); + + offset = dma_size * s->period; + snd_assert (dma_size <= DMA_BUF_SIZE,); + + dma_request.src_addr = (dma_addr_t) (dma_map_single (NULL, + runtime-> + dma_area + + offset, + dma_size, + DMA_TO_DEVICE)); + + if (s->ssi == SSI1) + dma_request.dst_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1STX0); + else + dma_request.dst_addr = (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2STX0); + dma_request.num_of_bytes = dma_size; + + DPRINTK("MXC: Start DMA offset (%d) size (%d)\n", offset, + runtime->dma_bytes); + + mxc_dma_config (s->dma_wchannel, &dma_request, 1, + MXC_DMA_MODE_WRITE); + ret = mxc_dma_enable (s->dma_wchannel); + ssi_transmit_enable (s->ssi, true); + ssi_enable (s->ssi, true); + s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */ + + if (ret) { + DPRINTK("audio_process_dma: cannot queue DMA buffer\ + (%i)\n", ret); + return; + } + s->period++; + s->period %= runtime->periods; + +#ifdef MXC_SOUND_PLAYBACK_CHAIN_DMA_EN + if ((s->period > s->periods) && ((s->period - s->periods) > 1)) { + PRINTK("audio playback chain dma: already double buffered\n"); + return; + } + + if ((s->period < s->periods) + && ((s->period + runtime->periods - s->periods) > 1)) { + PRINTK("audio playback chain dma: already double buffered\n"); + return; + } + + if (s->period == s->periods) { + PRINTK("audio playback chain dma: s->period == s->periods\n"); + return; + } + + if (snd_pcm_playback_hw_avail(runtime) < + 2 * runtime->period_size) { + PRINTK("audio playback chain dma: available data is not enough\n"); + return; + } + + PRINTK("audio playback chain dma:to set up the 2nd dma buffer\n"); + offset = dma_size * s->period; + dma_request.src_addr = (dma_addr_t) (dma_map_single (NULL, + runtime-> + dma_area + + offset, + dma_size, + DMA_TO_DEVICE)); + mxc_dma_config (s->dma_wchannel, &dma_request, 1, + MXC_DMA_MODE_WRITE); + ret = mxc_dma_enable (s->dma_wchannel); + + s->period++; + s->period %= runtime->periods; +#endif // MXC_SOUND_PLAYBACK_CHAIN_DMA_EN + } +} + +/* + * This function is called whenever a new audio block needs to be + * transferred from CODEC. The function receives the address and the size + * of the block that will store the audio samples and start a new DMA transfer. + * + * @param substream pointer to the structure of the current stream. + * + */ +static void audio_capture_dma (audio_stream_t *s) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size; + unsigned int offset; + int ret = 0; + mxc_dma_requestbuf_t dma_request; + + substream = s->stream; + runtime = substream->runtime; + + DPRINTK("\nDMA direction %d\ + (0 is playback 1 is capture)\n", s->stream_id); + + memset (&dma_request, 0, sizeof (mxc_dma_requestbuf_t)); + + if (s->active) { + dma_size = frames_to_bytes (runtime, runtime->period_size); + DPRINTK("s->period (%x) runtime->periods (%d)\n", + s->period, runtime->periods); + DPRINTK("runtime->period_size (%d) dma_size (%d)\n", + (unsigned int) runtime->period_size, + runtime->dma_bytes); + + offset = dma_size * s->period; + snd_assert (dma_size <= DMA_BUF_SIZE,); + + dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL, + runtime-> + dma_area + + offset, + dma_size, + DMA_FROM_DEVICE)); + if (s->ssi == SSI1) + dma_request.src_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1SRX0); + else + dma_request.src_addr = (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2SRX0); + dma_request.num_of_bytes = dma_size; + + DPRINTK("MXC: Start DMA offset (%d) size (%d)\n", offset, + runtime->dma_bytes); + + mxc_dma_config (s->dma_wchannel, &dma_request, 1, + MXC_DMA_MODE_READ); + ret = mxc_dma_enable (s->dma_wchannel); + + s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */ + + if (ret) { + DPRINTK("audio_process_dma: cannot queue DMA buffer\ + (%i)\n", ret); + return; + } + s->period++; + s->period %= runtime->periods; + +#ifdef MXC_SOUND_CAPTURE_CHAIN_DMA_EN + if ((s->period > s->periods) && ((s->period - s->periods) > 1)) { + PRINTK("audio capture chain dma: already double buffered\n"); + return; + } + + if ((s->period < s->periods) + && ((s->period + runtime->periods - s->periods) > 1)) { + PRINTK("audio capture chain dma: already double buffered\n"); + return; + } + + if (s->period == s->periods) { + PRINTK("audio capture chain dma: s->period == s->periods\n"); + return; + } + + if (snd_pcm_capture_hw_avail (runtime) < + 2 * runtime->period_size) { + PRINTK("audio capture chain dma: available data is not enough\n"); + return; + } + + PRINTK("audio capture chain dma:to set up the 2nd dma buffer\n"); + offset = dma_size * s->period; + dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL, + runtime-> + dma_area + + offset, + dma_size, + DMA_FROM_DEVICE)); + mxc_dma_config (s->dma_wchannel, &dma_request, 1, + MXC_DMA_MODE_READ); + ret = mxc_dma_enable (s->dma_wchannel); + + s->period++; + s->period %= runtime->periods; +#endif // MXC_SOUND_CAPTURE_CHAIN_DMA_EN + } +} + +/* + * This is a callback which will be called + * when a TX transfer finishes. The call occurs + * in interrupt context. + * + * @param dat pointer to the structure of the current stream. + * + */ +static void audio_playback_dma_callback (void *data, int error, + unsigned int count) +{ + audio_stream_t *s; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size; + unsigned int previous_period; + unsigned int offset; + + s = data; + substream = s->stream; + runtime = substream->runtime; + previous_period = s->periods; + dma_size = frames_to_bytes (runtime, runtime->period_size); + offset = dma_size * previous_period; + + s->tx_spin = 0; + s->periods++; + s->periods %= runtime->periods; + + // Give back to the CPU the access to the non cached memory + dma_unmap_single (NULL, runtime->dma_addr + offset, dma_size, + DMA_TO_DEVICE); + + /* + * If we are getting a callback for an active stream then we inform + * the PCM middle layer we've finished a period + */ + if (s->active) + snd_pcm_period_elapsed (s->stream); + + spin_lock (&s->dma_lock); + + // Trig next DMA transfer + audio_playback_dma (s); + + spin_unlock (&s->dma_lock); +} + +/* + * This is a callback which will be called when a RX transfer finishes. The + * call occurs in interrupt context. + * + * @param substream pointer to the structure of the current stream. + */ +static void audio_capture_dma_callback (void *data, int error, + unsigned int count) +{ + audio_stream_t *s; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int dma_size; + unsigned int previous_period; + unsigned int offset; + + s = data; + substream = s->stream; + runtime = substream->runtime; + previous_period = s->periods; + dma_size = frames_to_bytes (runtime, runtime->period_size); + offset = dma_size * previous_period; + + s->tx_spin = 0; + s->periods++; + s->periods %= runtime->periods; + + // Give back to the CPU the access to the non cached memory + dma_unmap_single (NULL, runtime->dma_addr + offset, dma_size, + DMA_FROM_DEVICE); + + /* + * If we are getting a callback for an active stream then we inform + * the PCM middle layer we've finished a period + */ + if (s->active) + snd_pcm_period_elapsed (s->stream); + + spin_lock (&s->dma_lock); + + // Trig next DMA transfer + audio_capture_dma (s); + + spin_unlock (&s->dma_lock); +} + +/* + * This function is a dispatcher of command to be executed + * by the driver for playback. + * + * @param substream pointer to the structure of the current stream. + * @param cmd command to be executed + * + * @return 0 on success, -1 otherwise. + */ +static int +snd_mxc_audio_playback_trigger (struct snd_pcm_substream *substream, int cmd) +{ + mxc_bmi_audio_t *chip; + int stream_id = PLAYBACK_STREAM; + audio_stream_t *s; + int err; + int device; + + device = substream->pcm->device; + chip = snd_pcm_substream_chip (substream); + stream_id = substream->pstr->stream; + s = &chip->s[stream_id]; + err = 0; + + // note local interrupts are already disabled in the midlevel code + spin_lock (&s->dma_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + PRINTK("MXC: SNDRV_PCM_TRIGGER_START\n"); + s->tx_spin = 0; + // requested stream startup + s->active = 1; + audio_playback_dma (s); + break; + case SNDRV_PCM_TRIGGER_STOP: + PRINTK("MXC: SNDRV_PCM_TRIGGER_STOP\n"); + // requested stream shutdown + audio_playback_stop_dma (s); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + PRINTK("MXC : SNDRV_PCM_TRIGGER_SUSPEND active = 0\n"); + s->active = 0; + s->periods = 0; + break; + case SNDRV_PCM_TRIGGER_RESUME: + PRINTK("MXC: SNDRV_PCM_TRIGGER_RESUME\n"); + s->active = 1; + s->tx_spin = 0; + audio_playback_dma (s); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + PRINTK("MXC: SNDRV_PCM_TRIGGER_PAUSE_PUSH\n"); + s->active = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + PRINTK("MXC: SNDRV_PCM_TRIGGER_PAUSE_RELEASE\n"); + s->active = 1; + if (s->old_offset) { + s->tx_spin = 0; + audio_playback_dma (s); + break; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock (&s->dma_lock); + return err; +} + +/* + * This function is a dispatcher of command to be executed + * by the driver for capture. + * + * @param substream pointer to the structure of the current stream. + * @param cmd command to be executed + * + * @return 0 on success, -1 otherwise. + */ +static int +snd_mxc_audio_capture_trigger (struct snd_pcm_substream *substream, int cmd) +{ + mxc_bmi_audio_t *chip; + int stream_id; + audio_stream_t *s; + int err; + + chip = snd_pcm_substream_chip (substream); + stream_id = substream->pstr->stream; + s = &chip->s[stream_id]; + err = 0; + + // note local interrupts are already disabled in the midlevel code + spin_lock (&s->dma_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + PRINTK("MXC: SNDRV_PCM_TRIGGER_START\n"); + s->tx_spin = 0; + // requested stream startup + s->active = 1; + audio_capture_dma (s); + break; + case SNDRV_PCM_TRIGGER_STOP: + PRINTK("MXC: SNDRV_PCM_TRIGGER_STOP\n"); + // requested stream shutdown + audio_capture_stop_dma (s); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + PRINTK("MXC : SNDRV_PCM_TRIGGER_SUSPEND active = 0\n"); + s->active = 0; + s->periods = 0; + break; + case SNDRV_PCM_TRIGGER_RESUME: + PRINTK("MXC: SNDRV_PCM_TRIGGER_RESUME\n"); + s->active = 1; + s->tx_spin = 0; + audio_capture_dma (s); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + PRINTK("MXC: SNDRV_PCM_TRIGGER_PAUSE_PUSH\n"); + s->active = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + PRINTK("MXC: SNDRV_PCM_TRIGGER_PAUSE_RELEASE\n"); + s->active = 1; + if (s->old_offset) { + s->tx_spin = 0; + audio_capture_dma (s); + break; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock (&s->dma_lock); + return err; +} + +/* + * This function configures the hardware to allow audio + * playback operations. It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_playback_prepare (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + audio_stream_t *s; + int ssi; + int device = -1; + int stream_id = PLAYBACK_STREAM; + struct snd_pcm_runtime *runtime; + + device = substream->pcm->device; + + chip = snd_pcm_substream_chip (substream); + runtime = substream->runtime; + s = &chip->s[stream_id]; + ssi = s->ssi; + + configure_dam_bmi_master (ssi); + configure_ssi_rx (ssi); + ssi_rx_sampleRate (ssi, runtime->rate); + configure_ssi_tx (ssi); + ssi_tx_sampleRate (ssi, runtime->rate); + set_bmi_channels (substream); + ssi_interrupt_enable (ssi, ssi_tx_dma_interrupt_enable); + ssi_interrupt_enable(ssi, ssi_tx_interrupt_enable); + ssi_interrupt_enable (ssi, ssi_tx_fifo_0_empty); + ssi_enable(ssi, true); + + s->period = 0; + s->periods = 0; + + msleep (100); + + return 0; +} + +/* + * This function gets the current capture pointer position. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + */ +static snd_pcm_uframes_t +snd_mxc_audio_capture_pointer (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + + chip = snd_pcm_substream_chip (substream); + return audio_get_capture_dma_pos (&chip->s[substream->pstr->stream]); +} + +/* + * This function gets the current playback pointer position. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + */ +static snd_pcm_uframes_t +snd_mxc_audio_playback_pointer (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + int device; + int stream_id; + device = substream->pcm->device; + stream_id = PLAYBACK_STREAM; + chip = snd_pcm_substream_chip (substream); + return audio_get_playback_dma_pos (&chip->s[stream_id]); +} + +/* + * This structure reprensents the capabilities of the driver + * in capture mode. + * It is used by ALSA framework. + */ +static struct snd_pcm_hardware snd_mxc_bmi_capture = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_8000_96000 | SNDRV_PCM_RATE_CONTINUOUS), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = MIN_PERIOD_SIZE, + .period_bytes_max = DMA_BUF_SIZE, + .periods_min = MIN_PERIOD, + .periods_max = MAX_PERIOD, + .fifo_size = 0, +}; + +/* + * This structure reprensents the capabilities of the driver + * in playback mode for ST-Dac. + * It is used by ALSA framework. + */ +static struct snd_pcm_hardware snd_mxc_bmi_playback_stereo = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_8000_96000 | SNDRV_PCM_RATE_CONTINUOUS), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = MIN_PERIOD_SIZE, + .period_bytes_max = DMA_BUF_SIZE, + .periods_min = MIN_PERIOD, + .periods_max = MAX_PERIOD, + .fifo_size = 0, +}; + +/* + * This function opens a CODEC audio device in playback mode + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_card_mxc_audio_playback_open (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + struct snd_pcm_runtime *runtime; + int stream_id = -1; + int err; + int device = -1; + + device = substream->pcm->device; + + chip = snd_pcm_substream_chip (substream); + runtime = substream->runtime; + stream_id = PLAYBACK_STREAM; + + err = -1; + + audio_mixer_control[chip->s->ssi].codec_playback_active = 1; + + chip->s[stream_id].stream = substream; + + runtime->hw = snd_mxc_bmi_playback_stereo; + + if ((err = snd_pcm_hw_constraint_integer (runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < + 0) + return err; + if ((err = snd_pcm_hw_constraint_list (runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_playback_rates_stereo)) < 0) + return err; + + msleep (10); + + // setup DMA controller for playback + if ((err = + configure_write_channel (&mxc_audio[chip->s->ssi]->s[stream_id], + audio_playback_dma_callback, + stream_id)) < 0) + return err; + + return 0; +} + +/* + * This function closes an CODEC audio device for playback. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_card_mxc_audio_playback_close (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + audio_stream_t *s; + int ssi; + int device, stream_id = -1; + + device = substream->pcm->device; + stream_id = PLAYBACK_STREAM; + + chip = snd_pcm_substream_chip (substream); + s = &chip->s[stream_id]; + ssi = s->ssi; + + audio_mixer_control[chip->s->ssi].codec_playback_active = 0; + + ssi_tx_fifo_enable (ssi, ssi_fifo_0, false); + ssi_interrupt_disable (ssi, ssi_tx_interrupt_enable); + ssi_interrupt_disable (ssi, ssi_tx_dma_interrupt_enable); + ssi_interrupt_disable (ssi, ssi_tx_fifo_0_empty); + mxc_dma_free ((mxc_audio[ssi]->s[stream_id]).dma_wchannel); + + chip->s[stream_id].stream = NULL; + + return 0; +} + +/* + * This function closes a audio device for capture. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_card_mxc_audio_capture_close (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + audio_stream_t *s; + int ssi; + + chip = snd_pcm_substream_chip (substream); + s = &chip->s[substream->pstr->stream]; + ssi = s->ssi; + + audio_mixer_control[ssi].codec_capture_active = 0; + + ssi_rx_fifo_enable (ssi, ssi_fifo_0, false); + ssi_interrupt_disable(ssi, ssi_rx_interrupt_enable); + ssi_interrupt_disable (ssi, ssi_rx_dma_interrupt_enable); + ssi_interrupt_disable (ssi, ssi_rx_fifo_0_full); + mxc_dma_free ((mxc_audio[ssi]->s[1]).dma_wchannel); + + chip->s[substream->pstr->stream].stream = NULL; + + return 0; +} + +/* + * This function configure the Audio HW in terms of memory allocation. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_hw_params (struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime; + int ret; + + runtime = substream->runtime; + ret = + snd_pcm_lib_malloc_pages (substream, params_buffer_bytes (hw_params)); + if (ret < 0) + return ret; + + runtime->dma_addr = virt_to_phys (runtime->dma_area); + + DPRINTK("MXC: snd_mxc_audio_hw_params runtime->dma_addr 0x(%x)\n", + (unsigned int) runtime->dma_addr); + DPRINTK("MXC: snd_mxc_audio_hw_params runtime->dma_area 0x(%x)\n", + (unsigned int) runtime->dma_area); + DPRINTK("MXC: snd_mxc_audio_hw_params runtime->dma_bytes 0x(%x)\n", + (unsigned int) runtime->dma_bytes); + + return ret; +} + +/* + * This function frees the audio hardware at the end of playback/capture. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_hw_free (struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages (substream); +} + +/* + * This function configures the hardware to allow audio + * capture operations. It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_capture_prepare (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + audio_stream_t *s; + struct snd_pcm_runtime *runtime; + int ssi; + + chip = snd_pcm_substream_chip (substream); + runtime = substream->runtime; + s = &chip->s[substream->pstr->stream]; + ssi = s->ssi; + + DPRINTK("substream->pstr->stream %d\n", substream->pstr->stream); + DPRINTK("SSI%d\n", ssi + 1); + + configure_dam_bmi_master (ssi); + configure_ssi_tx (ssi); + ssi_tx_sampleRate (ssi, runtime->rate); + configure_ssi_rx (ssi); + ssi_rx_sampleRate (ssi, runtime->rate); + + ssi_interrupt_enable (ssi, ssi_rx_fifo_0_full); + ssi_interrupt_enable (ssi, ssi_rx_dma_interrupt_enable); + ssi_interrupt_enable (ssi, ssi_rx_interrupt_enable); + set_bmi_channels (substream); + ssi_receive_enable (ssi, true); + + msleep(20); + + s->period = 0; + s->periods = 0; + + return 0; +} + +/* + * This function opens an audio device in capture mode on Codec. + * It is called by ALSA framework. + * + * @param substream pointer to the structure of the current stream. + * + * @return 0 on success, -1 otherwise. + */ +static int snd_card_mxc_audio_capture_open (struct snd_pcm_substream *substream) +{ + mxc_bmi_audio_t *chip; + struct snd_pcm_runtime *runtime; + int stream_id; + int err; + + chip = snd_pcm_substream_chip (substream); + runtime = substream->runtime; + stream_id = substream->pstr->stream; + err = -1; + + audio_mixer_control[chip->s->ssi].codec_capture_active = 1; + + chip->s[stream_id].stream = substream; + + if (stream_id == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw = snd_mxc_bmi_capture; + } else { + return err; + } + + if ((err = snd_pcm_hw_constraint_integer (runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < + 0) { + return err; + } + + if ((err = snd_pcm_hw_constraint_list (runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_capture_rates_stereo)) < 0) { + return err; + } + + // setup DMA controller for Record + err = configure_read_channel (&mxc_audio[chip->s->ssi]->s[SNDRV_PCM_STREAM_CAPTURE], + audio_capture_dma_callback); + if (err < 0) { + return err; + } + + msleep (50); + + return 0; +} + +/* + * This structure is the list of operation that the driver + * must provide for the capture interface + */ +static struct snd_pcm_ops snd_card_mxc_audio_capture_ops = { + .open = snd_card_mxc_audio_capture_open, + .close = snd_card_mxc_audio_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mxc_audio_hw_params, + .hw_free = snd_mxc_audio_hw_free, + .prepare = snd_mxc_audio_capture_prepare, + .trigger = snd_mxc_audio_capture_trigger, + .pointer = snd_mxc_audio_capture_pointer, +}; + +/* + * This structure is the list of operation that the driver + * must provide for the playback interface + */ +static struct snd_pcm_ops snd_card_mxc_audio_playback_ops = { + .open = snd_card_mxc_audio_playback_open, + .close = snd_card_mxc_audio_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mxc_audio_hw_params, + .hw_free = snd_mxc_audio_hw_free, + .prepare = snd_mxc_audio_playback_prepare, + .trigger = snd_mxc_audio_playback_trigger, + .pointer = snd_mxc_audio_playback_pointer, +}; + +/* + * This functions initializes the capture audio device supported by + * CODEC IC. + * + * @param mxc_audio pointer to the sound card structure + * @param device SSI interface + */ +void init_device_capture (mxc_bmi_audio_t *mxc_audio, int device) +{ + audio_stream_t *audio_stream; + + audio_stream = &mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE]; + + /* + * These parameters defines the identity of + * the device (stereoadc or stereodac) + */ + if (device == 0) { + audio_stream->ssi = SSI1; + audio_stream->dam_port = DAM_PORT_4; + } else { + audio_stream->ssi = SSI2; + audio_stream->dam_port = DAM_PORT_5; + } +} + +/* + * This functions initializes the playback audio device supported by + * CODEC IC. + * + * @param mxc_audio pointer to the sound card structure. + * @param device SSI interface + */ +void init_device_playback (mxc_bmi_audio_t *mxc_audio, int device) +{ + audio_stream_t *audio_stream; + audio_stream = &mxc_audio->s[0]; + + /* These parameters defines the identity of + * the device (codec or stereodac) + */ + if (device == 0) { + audio_stream->ssi = SSI1; + audio_stream->dam_port = DAM_PORT_4; + } else { + audio_stream->ssi = SSI2; + audio_stream->dam_port = DAM_PORT_5; + } +} + +void mxc_bmi_mixer_controls_init (mxc_bmi_audio_t *mxc_audio, int device) +{ + audio_mixer_control_t *audio_control; + struct i2c_adapter *adap = 0; + char iox_data[1]; + int i = 0; + + audio_control = &audio_mixer_control[device]; + + memset (audio_control, 0, sizeof (audio_mixer_control_t)); + sema_init (&audio_control->sem, 1); + + for (i = 0; i < OP_MAXDEV; i++) { + audio_control->vol_for_output[i] = 9; // maximum gain + } + for (i = 0; i < IP_MAXDEV; i++) { + audio_control->vol_for_input[i] = 4; // gain = -6 dB + } + + audio_control->master_volume_out = 127; + + if(device == 0) { + if (bmi_audio[0].active == 1) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active == 1) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + } else { + if (bmi_audio[1].active == 1) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active == 1) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + } + + if (adap) { + // IOX status + if (ReadByte_IOX (adap, IOX_INPUT_REG, iox_data) == 0) { + audio_control->lo_indicator = (*iox_data >> IOX_LO_INS) & 0x1; + audio_control->li_indicator = (*iox_data >> IOX_LI_INS) & 0x1; + audio_control->mic_indicator = (*iox_data >> IOX_MIC_INS) & 0x1; + audio_control->hp_indicator = (*iox_data >> IOX_HP_INS) & 0x1; + } + } +} + +/* + * This functions initializes the audio devices supported by + * CODEC IC. + * + * @param mxc_audio pointer to the sound card structure. + * @param device SSi interface + */ +void mxc_bmi_audio_init (mxc_bmi_audio_t *mxc_audio, int device) +{ + mxc_audio->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Audio out"; + mxc_audio->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK; + mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE].id = "Audio in"; + mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE; + + init_device_playback (mxc_audio, device); + init_device_capture (mxc_audio, device); +} + +/* + * This function initializes the soundcard structure. + * + * @param mxc_audio pointer to the sound card structure. + * @param device the device index (zero based) + * + * @return 0 on success, -1 otherwise. + */ +static int __init snd_card_mxc_audio_pcm (mxc_bmi_audio_t *mxc_audio, int device) +{ + struct snd_pcm *pcm; + int err; + + // Create a new PCM instance with 1 capture stream and 1 playback substream + if (device == 0) { + if ((err = snd_pcm_new (mxc_audio->card, "PIM_AUDIO13", 0, 1, 1, &pcm)) < 0) { + return err; + } + } else { + if ((err = snd_pcm_new (mxc_audio->card, "PIM_AUDIO24", 0, 1, 1, &pcm)) < 0) { + return err; + } + } + + /* + * this sets up our initial buffers and sets the dma_type to isa. + * isa works but I'm not sure why (or if) it's the right choice + * this may be too large, trying it for now + */ + snd_pcm_lib_preallocate_pages_for_all (pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), MAX_BUFFER_SIZE * 2, + MAX_BUFFER_SIZE * 2); + + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_card_mxc_audio_playback_ops); + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_card_mxc_audio_capture_ops); + + pcm->private_data = mxc_audio; + pcm->info_flags = 0; + if (device == 0) + strncpy (pcm->name, SOUND_CARD13_NAME, sizeof (pcm->name)); + else + strncpy (pcm->name, SOUND_CARD24_NAME, sizeof (pcm->name)); + mxc_audio->pcm[0] = pcm; + mxc_bmi_audio_init (mxc_audio, device); + mxc_bmi_mixer_controls_init (mxc_audio, device); + + return 0; +} + +#if 0 //pjg - POWER_MANAGEMENT +#ifdef CONFIG_PM +/* + * This function suspends all active streams. + * + * TBD + * + * @param card pointer to the sound card structure. + * @param state requested state + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_suspend (struct bmi_device *bdev, + pm_message_t state) +{ + struct snd_card *card = bmi_device_get_drvdata (bdev); + mxc_bmi_audio_t *chip = card->private_data; + + snd_power_change_state (card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all (chip->pcm[0]); + //mxc_alsa_audio_shutdown (chip); + + return 0; +} + +/* + * This function resumes all suspended streams. + * + * TBD + * + * @param card pointer to the sound card structure. + * @param state requested state + * + * @return 0 on success, -1 otherwise. + */ +static int snd_mxc_audio_resume (struct bmi_device *bdev) +{ + struct snd_card *card = bmi_device_get_drvdata (bdev); + + snd_power_change_state (card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif // CONFIG_PM +#endif //pjg - POWER_MANAGEMENT + +/* + * This function frees the sound card structure + * + * @param card pointer to the sound card structure. + * + * @return 0 on success, -1 otherwise. + */ +void snd_mxc_audio_free (struct snd_card *card) +{ + mxc_bmi_audio_t *chip; + + chip = card->private_data; + audio_dma_free (&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); + audio_dma_free (&chip->s[SNDRV_PCM_STREAM_CAPTURE]); + mxc_audio[chip->s->ssi] = NULL; + card->private_data = NULL; + kfree (chip); + +} + +/* + * Input interrupt handler and support routines + */ + + // work handler +void bmiaudio_input_work (void *arg, int slot) { + struct bmi_audio *audio = (struct bmi_audio *) arg; + audio_mixer_control_t *audio_control; + struct i2c_adapter *adap; + unsigned char iox_data[1]; + int input_data; + + if (audio->bdev == 0) { + printk (KERN_INFO + "bmi_audio.c: bmi_audio_input work called with no bdev (slot %d)\n", + slot); + return; + } + + if (bmi_device_present (audio->bdev) == 0) { + printk (KERN_INFO + "bmi_audio.c: bmi_audio_input work called with no bdev active (slot %d)\n", + slot); + return; + } + + adap = bmi_device_get_i2c_adapter (audio->bdev); + + // IOX status + if (ReadByte_IOX (adap, IOX_INPUT_REG, iox_data) != 0) { + return; + } + input_data = 0; + if ((*iox_data & GETSTAT_VOLP) == 0) + input_data |= VOLUME_UP; + if ((*iox_data & GETSTAT_VOLD) == 0) + input_data |= VOLUME_DOWN; + if ((*iox_data & GETSTAT_LI_INS) == GETSTAT_LI_INS) + input_data |= LINEIN_INSERTED; + if ((*iox_data & GETSTAT_MIC_INS) != GETSTAT_MIC_INS) + input_data |= MICROPHONE_INSERTED; + if (output_ints) { + if ((*iox_data & GETSTAT_LO_INS) == GETSTAT_LO_INS) + input_data |= LINEOUT_INSERTED; + if ((*iox_data & GETSTAT_HP_INS) == GETSTAT_HP_INS) + input_data |= HEADPHONE_INSERTED; + } + input_report_abs (audio->input_dev, ABS_MISC, input_data); + input_sync (audio->input_dev); + + if ((slot == 0) || (slot == 2)) { + audio_control = &audio_mixer_control[0]; + } else { + audio_control = &audio_mixer_control[1]; + } + + if (down_interruptible (&audio_control->sem) == 0) { + audio_control->lo_indicator = (*iox_data >> IOX_LO_INS) & 0x1; + audio_control->li_indicator = (*iox_data >> IOX_LI_INS) & 0x1; + audio_control->mic_indicator = (*iox_data >> IOX_MIC_INS) & 0x1; + audio_control->hp_indicator = (*iox_data >> IOX_HP_INS) & 0x1; + up (&audio_control->sem); + } + + enable_irq (audio->irq); + return; +} + +void bmiaudio_input_work0 (struct work_struct *work) { + bmiaudio_input_work (&bmi_audio[0], 0); +} + +void bmiaudio_input_work1 (struct work_struct *work) { + bmiaudio_input_work (&bmi_audio[1], 1); +} + +void bmiaudio_input_work2 (struct work_struct *work) { + bmiaudio_input_work (&bmi_audio[2], 2); +} + +void bmiaudio_input_work3 (struct work_struct *work) { + bmiaudio_input_work (&bmi_audio[3], 3); +} + +DECLARE_DELAYED_WORK(bmiaudio_work0, bmiaudio_input_work0); +DECLARE_DELAYED_WORK(bmiaudio_work1, bmiaudio_input_work1); +DECLARE_DELAYED_WORK(bmiaudio_work2, bmiaudio_input_work2); +DECLARE_DELAYED_WORK(bmiaudio_work3, bmiaudio_input_work3); + +static irqreturn_t module_irq_handler (int irq, void *dummy) +{ + disable_irq_nosync (irq); + + switch (irq) { + case M1_IRQ: + schedule_delayed_work (&bmiaudio_work0, WORK_DELAY); + break; + case M2_IRQ: + schedule_delayed_work (&bmiaudio_work1, WORK_DELAY); + break; + case M3_IRQ: + schedule_delayed_work (&bmiaudio_work2, WORK_DELAY); + break; + case M4_IRQ: + schedule_delayed_work (&bmiaudio_work3, WORK_DELAY); + break; + } + return IRQ_HANDLED; +} + +/* + * Configure the CODEC registers to the initial values + */ + +static int configure_CODEC(struct i2c_adapter *adap, struct bmi_audio *audio) +{ + unsigned char codec_data[1]; + + // page select = 0 + if (WriteByte_CODEC (adap, CODEC_PAGE_SEL, 0x00)) + goto nodev; + if (ReadByte_CODEC (adap, CODEC_PAGE_SEL, codec_data)) + goto nodev; + if (*codec_data != 0x00) + goto nodev; + + // ADC,DAC SR = SR/1 + if (WriteByte_CODEC (adap, CODEC_SAMPLE_RATE, (CODEC_SR1 << CODEC_SR_SHIFT) | CODEC_SR1)) + goto nodev; + + // PLL disabled, Q = 2 => Fsref = 46875 Hz + if (WriteByte_CODEC (adap, CODEC_PLLA, CODEC_PLLA_DIS | CODEC_PLLA_Q(2))) + goto nodev; + + // CODEC datapath normal + if (WriteByte_CODEC (adap, CODEC_DATAPATH, + CODEC_DP_44 | CODEC_DP_L(CODEC_DP_REVERSE) | CODEC_DP_R(CODEC_DP_NORMAL))) + goto nodev; + + // CODEC is slave + if (WriteByte_CODEC (adap, CODEC_AIFA, CODEC_AIFA_BCLK_S | CODEC_AIFA_WCLK_S | + CODEC_AIFA_DOUT_TS | CODEC_AIFA_CLK_F | CODEC_AIFA_FX_OFF)) + goto nodev; + + // CODEC is in I2S mode, WL = 32 + if (WriteByte_CODEC (adap, CODEC_AIFB, CODEC_AIFB_I2S | CODEC_AIFB_32)) + goto nodev; + + // HP outputs AC coupled + if (WriteByte_CODEC (adap, CODEC_HS, CODEC_HS_COUPLED | CODEC_HS_ADIFF)) + goto nodev; + + // ADC PGA not muted, maximum gain + if (WriteByte_CODEC (adap, CODEC_LADC_PGA, CODEC_ADC_PGA_G(0x3C))) // 59 dB + goto nodev; + if (WriteByte_CODEC (adap, CODEC_RADC_PGA, CODEC_ADC_PGA_G(0x3C))) // 59 dB + goto nodev; + + // M3R -> [LR]PGA -6 dB gain, M3L muted + if (WriteByte_CODEC (adap, CODEC_M3_LPGA, CODEC_M3_PGA_R(0x04) | CODEC_M3_PGA_LOFF)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_M3_RPGA, CODEC_M3_PGA_R(0x04) | CODEC_M3_PGA_LOFF)) + goto nodev; + + // L1 -> [LR]PGA gain = -6 dB + // selectively activate ADC + if (audio->active) { + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, CODEC_L_PGA(0x04) | CODEC_LX_PGA_PU)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, CODEC_L_PGA(0x04) | CODEC_LX_PGA_PU)) + goto nodev; + } else { + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, CODEC_L_PGA(0x0F))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, CODEC_L_PGA(0x0F))) + goto nodev; + } + + // L2 -> [LR]PGA gain = -6 dB + if (WriteByte_CODEC (adap, CODEC_L2L_LPGA, CODEC_L_PGA(0x04))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_L2R_RPGA, CODEC_L_PGA(0x04))) + goto nodev; + + // MIC Bias = 2V + if (WriteByte_CODEC (adap, CODEC_MIC_BIAS, CODEC_MIC_BIAS_2V)) + goto nodev; + + // {LR]AGC A + if (WriteByte_CODEC (adap, CODEC_MIC_LAGC_A, CODEC_MIC_AGC_EN)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_MIC_LAGC_A, CODEC_MIC_AGC_EN)) + goto nodev; + + // {LR]AGC B + if (WriteByte_CODEC (adap, CODEC_MIC_LAGC_B, CODEC_MIC_AGC_MG(0x40))) // 30 dB, 0xEC=59 dB + goto nodev; + if (WriteByte_CODEC (adap, CODEC_MIC_RAGC_B, CODEC_MIC_AGC_MG(0x40))) // 30 dB, 0xEC=59 dB + goto nodev; + + // DAC powered up + if (WriteByte_CODEC (adap, CODEC_DAC_PWR, + CODEC_DAC_PWR_L_EN | CODEC_DAC_PWR_R_EN | CODEC_DAC_PWR_HP_ISE)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DAC_HPWR, CODEC_DAC_HPWR_HPL_DIFF)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DAC_HPOS, CODEC_DAC_HPOS_SS_DIS)) + goto nodev; + + // DAC volume = max + if (WriteByte_CODEC (adap, CODEC_DAC_LVOL, CODEC_DAC_VOL(0x0))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DAC_RVOL, CODEC_DAC_VOL(0x0))) + goto nodev; + + // MIC3->speaker + if(fcc_test) + if (WriteByte_CODEC (adap, CODEC_PGAR_HPLCOM, 0x80)) + goto nodev; + + // output switching volume = max + if (WriteByte_CODEC (adap, CODEC_DACL1_HPL, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DACL1_HPLCOM, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + + if (WriteByte_CODEC (adap, CODEC_DACR1_HPR, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DACR1_HPRCOM, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + + if (WriteByte_CODEC (adap, CODEC_DACL1_LLOPM, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_DACR1_RLOPM, CODEC_HP_EN | CODEC_HP_VOL(0x0))) + goto nodev; + + // output levels = max + if (WriteByte_CODEC (adap, CODEC_HPLOUT, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_HPLCOM, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_HPROUT, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_HPRCOM, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_LLOPM, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_RLOPM, + CODEC_HPX_LC(0x09) | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + goto nodev; + + // clocking + if (WriteByte_CODEC (adap, CODEC_CLK, CODEC_CLK_CLKDIV)) + goto nodev; + if (WriteByte_CODEC (adap, CODEC_CLKGEN, CODEC_CLKGEN_C_M)) + goto nodev; + return (0); + +nodev: + WriteByte_CODEC (adap, CODEC_L1L_LPGA, 0x38); + WriteByte_CODEC (adap, CODEC_L1R_RPGA, 0x38); + return -ENODEV; +} + +/* + * This function initializes the driver in terms of BMI and + * CODEC functionality. + * + * @return 0 on success, >0 otherwise. + */ +static int mxc_alsa_audio_probe (struct bmi_device *bdev) +{ + int rc = 0; + struct bmi_audio *audio; + struct i2c_adapter *adap; + struct cdev *cdev; + struct class *bmi_class; + int slot; + dev_t dev_id; + int ssi; + unsigned char iox_data[1]; + + // bmi set-up + slot = bmi_device_get_slot (bdev); + adap = bmi_device_get_i2c_adapter (bdev); + audio = &bmi_audio[slot]; + + audio->bdev = 0; + + // Create 1 minor device + cdev = &audio->cdev; + cdev_init (cdev, &cntl_fops); + + dev_id = MKDEV(major, slot); + rc = cdev_add (cdev, dev_id, 1); + if (rc) + return rc; + + // Create class device + bmi_class = bmi_get_bmi_class (); + audio->class_dev = device_create (bmi_class, NULL, MKDEV(major, slot), audio, "bmi_audio_ctrl_m%i", slot+1); + + if (IS_ERR(audio->class_dev)) { + printk (KERN_ERR "Unable to create class_device for bmi_audio_m%i; errno = %ld\n", + slot+1, PTR_ERR(audio->class_dev)); + cdev_del (&audio->cdev); + audio->class_dev = NULL; + return -ENODEV; + } + + // bind driver and bmi_device + audio->bdev = bdev; + + // check for opposite side already active + switch (slot) { + case 0: + ssi = 0; + if (bmi_audio[2].active == 0) { + mxc_audio[0]->card->dev = &bdev->dev; + audio->active = 1; + } + break; + case 1: + ssi = 1; + if (bmi_audio[3].active == 0) { + mxc_audio[1]->card->dev = &bdev->dev; + audio->active = 1; + } + break; + case 2: + ssi = 0; + if (bmi_audio[0].active == 0) { + mxc_audio[0]->card->dev = &bdev->dev; + audio->active = 1; + } + break; + case 3: + ssi = 1; + if (bmi_audio[1].active == 0) { + mxc_audio[1]->card->dev = &bdev->dev; + audio->active = 1; + } + break; + } + + bmi_device_set_drvdata (bdev, &bmi_audio[slot]); + + // Initialize GPIOs (turn LED's on ) + // bmi_slot_gpio_configure_as_output (int slot, int gpio, int data) + bmi_slot_gpio_configure_as_output (slot, GPIO_RED, BMI_GPIO_OFF); // Red LED=ON + bmi_slot_gpio_configure_as_output (slot, GPIO_GREEN, BMI_GPIO_OFF); // Green LED=ON + bmi_slot_gpio_configure_as_output (slot, GPIO_RESET, BMI_GPIO_OFF); // Assert RST = 0; + bmi_slot_gpio_configure_as_input (slot, GPIO_SPARE); // unused + + mdelay (200); + + // turn LED's off + bmi_slot_gpio_write_bit (slot, GPIO_RED, BMI_GPIO_ON); // Red LED=OFF + bmi_slot_gpio_write_bit (slot, GPIO_GREEN, BMI_GPIO_ON); // Green LED=OFF + + // release reset + bmi_slot_gpio_write_bit (slot, GPIO_RESET, BMI_GPIO_ON); // Reset = 1 + + // configure IOX + if (WriteByte_IOX (adap, IOX_OUTPUT_REG, 0x01)) + goto nodev; + + if (output_ints) { + if (WriteByte_IOX (adap, IOX_CONTROL, 0xFC)) // IOX[1:0]=OUT, IOX[7:2]=IN + goto nodev; + } else { + if (WriteByte_IOX (adap, IOX_CONTROL, 0x6C)) // IOX[7,4,1:0]=OUT, IOX[6:5,3:2]=IN + goto nodev; + } + + if (ReadByte_IOX (adap, IOX_INPUT_REG, iox_data)) // clear interrupts + goto nodev; + + printk (KERN_INFO "bmi_audio.c: probe(%d) IOX = 0x%x\n", slot, *iox_data & 0xFF); + +#ifdef CODEC // CODEC + // configure codec + if (configure_CODEC(adap, audio)) + goto nodev; +#endif // CODEC + + // set up input interrupt + audio->irq = bmi_device_get_status_irq (bdev); + snprintf (audio->int_name, sizeof (audio->int_name), "bmi_audio_stat_m%d", slot + 1); + if (request_irq (audio->irq, &module_irq_handler, 0, audio->int_name, &bmi_audio[slot])) { + printk (KERN_ERR "bmi_audio.c: Can't allocate irq %d or find audio in slot %d\n", + audio->irq, slot + 1); + device_destroy (bmi_class, MKDEV(major, slot)); + cdev_del (&audio->cdev); + audio->class_dev = NULL; + return -EBUSY; + } + + // power stablization delay + mdelay (500); + return 0; + +nodev: + device_destroy (bmi_class, MKDEV(major, slot)); + cdev_del (&audio->cdev); + audio->class_dev = NULL; + return -ENODEV; +} + +void mxc_alsa_audio_remove (struct bmi_device *bdev) +{ + struct bmi_audio *audio = (struct bmi_audio *) (bmi_device_get_drvdata (bdev)); + int slot = bmi_device_get_slot (bdev); + struct class *bmi_class; + + // release sound card srtucture + if (bmi_audio[slot].active && ((slot == 0) || (slot == 2))) + mxc_audio[0]->card->dev = NULL; + else if (bmi_audio[slot]. active && ((slot == 1) || (slot == 3))) + mxc_audio[1]->card->dev = NULL; + + // remove input interrupt scheduled work + free_irq (bmi_device_get_status_irq (bdev), &bmi_audio[slot]); + switch(slot) { + case 0: + cancel_delayed_work(&bmiaudio_work0); + break; + case 1: + cancel_delayed_work(&bmiaudio_work1); + break; + case 2: + cancel_delayed_work(&bmiaudio_work2); + break; + case 3: + cancel_delayed_work(&bmiaudio_work3); + break; + } + + + // deconfigure GPIO + bmi_slot_gpio_configure_all_as_inputs (slot); + + // remove class device + bmi_class = bmi_get_bmi_class (); + device_destroy (bmi_class, MKDEV(major, slot)); + + // clean slot structure + cdev_del (&audio->cdev); + audio->class_dev = NULL; + audio->bdev = NULL; + audio->active = 0; + + // de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + + return; +} + +// BMI device ID table +static struct bmi_device_id bmi_audio_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_AUDIO, + .revision = BMI_ANY, + }, + { 0, }, // terminate list +}; +MODULE_DEVICE_TABLE(bmi, bmi_audio_tbl); + +static struct bmi_driver bmi_audio_driver = { + .name = "bmi_audio", + .id_table = bmi_audio_tbl, + .probe = mxc_alsa_audio_probe, + .remove = mxc_alsa_audio_remove, +#if 0 //pjg - POWER_MANAGEMENT +#ifdef CONFIG_PM + .suspend = snd_mxc_audio_suspend, + .resume = snd_mxc_audio_resume, +#endif +#endif //pjg - POWER_MANAGEMENT + .driver = { + .name = "pim_ALSA", + }, +}; + +// mxc-alsa-mixer.c + +/* + * These are the functions implemented in the ALSA PCM driver that + * are used for mixer operations + * + */ + + // + // DAC Volume control + // + // PIM_AUDIO13 +static int bmi_pb_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 127 - volume; + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_DAC_LVOL, CODEC_DAC_VOL(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_DAC_RVOL, CODEC_DAC_VOL(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].master_volume_out = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_pb_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_pb_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + char val[1]; + + *val = 0x0; + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (ReadByte_CODEC (adap, CODEC_DAC_LVOL, val)) + return -ENODEV; + } else { + *val = 0; + } +#endif // CODEC + + uvalue->value.integer.value[0] = 127 - ((int) *val); + +#ifdef CODEC + if (adap) + return 0; + else + return -1; +#endif // CODEC + return 0; +} + +struct snd_kcontrol_new bmi_control_pb_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .index = 0x00, + .info = bmi_pb_volume_info0, + .get = bmi_pb_volume_get0, + .put = bmi_pb_volume_put0, + .private_value = 0xffab1, +}; + +// PIM_AUDIO24 +static int bmi_pb_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 127 - volume; + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_DAC_LVOL, CODEC_DAC_VOL(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_DAC_RVOL, CODEC_DAC_VOL(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].master_volume_out = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_pb_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_pb_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + char val[1]; + + *val = 0x0; + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (ReadByte_CODEC (adap, CODEC_DAC_LVOL, val)) + return -ENODEV; + } else { + *val = 0; + } +#endif // CODEC + + uvalue->value.integer.value[0] = 127 - ((int) *val); + +#ifdef CODEC + if (adap) + return 0; + else + return -1; +#endif // CODEC + return 0; +} + +struct snd_kcontrol_new bmi_control_pb_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .index = 0x00, + .info = bmi_pb_volume_info1, + .get = bmi_pb_volume_get1, + .put = bmi_pb_volume_put1, + .private_value = 0xffab1, +}; + +// +// LI (L1) Volume control +// +// PIM_AUDIO13 +static int bmi_li_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, CODEC_L_PGA(volume) | CODEC_LX_PGA_PU)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, CODEC_L_PGA(volume) | CODEC_LX_PGA_PU)) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_input[IP_LINEIN] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_li_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_li_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_input[IP_LINEIN]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_li_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Linein Capture Volume/Mute", + .index = 0x00, + .info = bmi_li_volume_info0, + .get = bmi_li_volume_get0, + .put = bmi_li_volume_put0, + .private_value = 0xffab3, +}; + +// PIM_AUDIO24 +static int bmi_li_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1L_LPGA, CODEC_L_PGA(volume) | CODEC_LX_PGA_PU)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L1R_RPGA, CODEC_L_PGA(volume) | CODEC_LX_PGA_PU)) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_input[IP_LINEIN] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_li_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_li_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_input[IP_LINEIN]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_li_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Linein Capture Volume/Mute", + .index = 0x00, + .info = bmi_li_volume_info1, + .get = bmi_li_volume_get1, + .put = bmi_li_volume_put1, + .private_value = 0xffab3, +}; + +// +// MIC (L2) Volume control +// +// PIM_AUDIO13 +static int bmi_mic_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L2L_LPGA, CODEC_L_PGA(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L2R_RPGA, CODEC_L_PGA(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_input[IP_MIC] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_mic_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_mic_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_input[IP_MIC]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_mic_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Volume/Mute", + .index = 0x00, + .info = bmi_mic_volume_info0, + .get = bmi_mic_volume_get0, + .put = bmi_mic_volume_put0, + .private_value = 0xffab4, +}; + +// PIM_AUDIO24 +static int bmi_mic_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L2L_LPGA, CODEC_L_PGA(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_L2R_RPGA, CODEC_L_PGA(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_input[IP_MIC] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_mic_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_mic_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_input[IP_MIC]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_mic_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Volume/Mute", + .index = 0x00, + .info = bmi_mic_volume_info1, + .get = bmi_mic_volume_get1, + .put = bmi_mic_volume_put1, + .private_value = 0xffab4, +}; + +// +// EMIC (L3) Volume control +// +// PIM_AUDIO13 +static int bmi_emic_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_M3_LPGA, CODEC_M3_PGA_R(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_M3_RPGA, CODEC_M3_PGA_R(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_input[IP_EMIC] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_emic_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_emic_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_input[IP_EMIC]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_emic_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Emic Capture Volume/Mute", + .index = 0x00, + .info = bmi_emic_volume_info0, + .get = bmi_emic_volume_get0, + .put = bmi_emic_volume_put0, + .private_value = 0xffab5, +}; + +// PIM_AUDIO24 +static int bmi_emic_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + volume = 8 - volume; + if (volume == 0) + volume = 0xF; // mute + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_M3_LPGA, CODEC_M3_PGA_R(volume))) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_M3_RPGA, CODEC_M3_PGA_R(volume))) + return -ENODEV; + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_input[IP_EMIC] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_emic_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_emic_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_input[IP_EMIC]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_emic_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Emic Capture Volume/Mute", + .index = 0x00, + .info = bmi_emic_volume_info1, + .get = bmi_emic_volume_get1, + .put = bmi_emic_volume_put1, + .private_value = 0xffab5, +}; + +// +// HEADPHONE (HP[LR]OUT) Volume control +// +// PIM_AUDIO13 +static int bmi_hp_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_HPLOUT, 0x0)) // mute + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPROUT, 0x0)) // mute + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_HPLOUT, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPROUT, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_output[OP_HEADPHONE] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_hp_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_hp_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_output[OP_HEADPHONE]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_hp_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Volume/Mute", + .index = 0x00, + .info = bmi_hp_volume_info0, + .get = bmi_hp_volume_get0, + .put = bmi_hp_volume_put0, + .private_value = 0xffab4, +}; + + // PIM_AUDIO24 +static int bmi_hp_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_HPLOUT, 0x0)) // mute + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPROUT, 0x0)) // mute + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_HPLOUT, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPROUT, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_output[OP_HEADPHONE] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_hp_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_hp_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_output[OP_HEADPHONE]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_hp_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Volume/Mute", + .index = 0x00, + .info = bmi_hp_volume_info1, + .get = bmi_hp_volume_get1, + .put = bmi_hp_volume_put1, + .private_value = 0xffab4, +}; + +// +// Speaker (HP[LR]COM) Volume control +// +// PIM_AUDIO13 +static int bmi_spkr_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_HPLCOM, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPRCOM, 0x0)) + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_HPLCOM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPRCOM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_output[OP_SPEAKER] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_spkr_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_spkr_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_output[OP_SPEAKER]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_spkr_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Speaker Playback Volume/Mute", + .index = 0x00, + .info = bmi_spkr_volume_info0, + .get = bmi_spkr_volume_get0, + .put = bmi_spkr_volume_put0, + .private_value = 0xffab5, +}; + +// PIM_AUDIO24 +static int bmi_spkr_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_HPLCOM, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPRCOM, 0x0)) + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_HPLCOM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_HPRCOM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_output[OP_SPEAKER] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_spkr_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_spkr_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_output[OP_SPEAKER]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_spkr_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Speaker Playback Volume/Mute", + .index = 0x00, + .info = bmi_spkr_volume_info1, + .get = bmi_spkr_volume_get1, + .put = bmi_spkr_volume_put1, + .private_value = 0xffab5, +}; + +// +// Line out ([LR]LOP) Volume control +// +// PIM_AUDIO13 +static int bmi_lo_volume_put0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[0].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[0].bdev); + } else if (bmi_audio[2].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[2].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_LLOPM, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_RLOPM, 0x0)) + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_LLOPM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_RLOPM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + audio_mixer_control[0].vol_for_output[OP_LINEOUT] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[0].sem); + + return 0; +} +static int bmi_lo_volume_info0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_lo_volume_get0 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[0].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[0].vol_for_output[OP_LINEOUT]; + + up (&audio_mixer_control[0].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_lo_vol0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Lineout Playback Volume/Mute", + .index = 0x00, + .info = bmi_lo_volume_info0, + .get = bmi_lo_volume_get0, + .put = bmi_lo_volume_put0, + .private_value = 0xffab6, +}; + +// PIM_AUDIO24 +static int bmi_lo_volume_put1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct i2c_adapter *adap = 0; + int volume; + + // calculate register value + volume = uvalue->value.integer.value[0]; + + // get I2C dapter + if (bmi_audio[1].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[1].bdev); + } else if (bmi_audio[3].active) { + adap = bmi_device_get_i2c_adapter (bmi_audio[3].bdev); + } + +#ifdef CODEC + // set volume + if (adap) { + // write page register + if (WriteByte_CODEC (adap, 0x0, 0x0)) + return -ENODEV; + if (volume == 0) { + if (WriteByte_CODEC (adap, CODEC_LLOPM, 0x0)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_RLOPM, 0x0)) + return -ENODEV; + } else { + if (WriteByte_CODEC (adap, CODEC_LLOPM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + if (WriteByte_CODEC (adap, CODEC_RLOPM, CODEC_HPX_LC(volume) + | CODEC_HPX_EN | CODEC_HPX_PD | CODEC_HPX_PC)) + return -ENODEV; + } + } else { + return -1; + } +#endif // CODEC + + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + audio_mixer_control[1].vol_for_output[OP_LINEOUT] = uvalue->value.integer.value[0]; + + up (&audio_mixer_control[1].sem); + + return 0; +} +static int bmi_lo_volume_info1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 9; + uinfo->value.integer.step = 1; + return 0; +} + +static int bmi_lo_volume_get1 (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + if (down_interruptible (&audio_mixer_control[1].sem)) + return -EINTR; + + uvalue->value.integer.value[0] = audio_mixer_control[1].vol_for_output[OP_LINEOUT]; + + up (&audio_mixer_control[1].sem); + + return 0; +} + +struct snd_kcontrol_new bmi_control_lo_vol1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Lineout Playback Volume/Mute", + .index = 0x00, + .info = bmi_lo_volume_info1, + .get = bmi_lo_volume_get1, + .put = bmi_lo_volume_put1, + .private_value = 0xffab6, +}; + +/* + * This function registers the control components of ALSA Mixer + * It is called by ALSA PCM init. + * + * @param card pointer to the ALSA sound card structure. + * @param device SSI interface + * + * @return 0 on success, -ve otherwise. + */ +int bug_alsa_create_ctl (struct snd_card *card, void *p_value, int device) +{ + int rc = 0; + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_pb_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_pb_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_li_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_li_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_mic_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_mic_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_emic_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_emic_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_hp_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_hp_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_spkr_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_spkr_vol1, p_value))) < 0) + return rc; + } + + if (device == 0) { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_lo_vol0, p_value))) < 0) + return rc; + } else { + if ((rc = + snd_ctl_add (card, snd_ctl_new1 (&bmi_control_lo_vol1, p_value))) < 0) + return rc; + } + + return 0; +} + +/************************************************************************** + * Module initialization and termination functions. + * + * Note that if this code is compiled into the kernel, then the + * module_init() function will be called within the device_initcall() + * group. + ************************************************************************** + */ + +/* + * @name Audio Driver Loading/Unloading Functions + * These non-exported internal functions are used to support the audio + * device driver initialization and de-initialization operations. + */ + +/* + * @brief This is the audio device driver initialization function. + * + * This function is called by the kernel when this device driver is first + * loaded. + */ + +char const input_name0[MAX_STRG] = "bmi_audio_status_m1"; +char const input_name1[MAX_STRG] = "bmi_audio_status_m2"; +char const input_name2[MAX_STRG] = "bmi_audio_status_m3"; +char const input_name3[MAX_STRG] = "bmi_audio_status_m4"; + +static int __init bmi_audio_init (void) +{ + int rc = 0; + dev_t dev_id; + int idn; + int iidn; + struct snd_card *card; + struct snd_card *card1; + + printk (KERN_INFO "BMI Audio driver loading...\n"); + + // alloc char driver with 4 minor numbers + rc = alloc_chrdev_region (&dev_id, 0, 4, "BMI AUDIO Driver"); + if (rc) { + printk (KERN_ERR "bmi_audio_init: Can't allocate chrdev region\n"); + return -ENODEV; + } + major = MAJOR(dev_id); + + // Allocate and Register input devices - bmi_audio_status_m[1234] + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) { + bmi_audio[idn].input_dev = input_allocate_device(); + if (!bmi_audio[idn].input_dev) { + for (iidn = BMI_AUDIO_M1; iidn < idn; iidn++) + input_unregister_device (bmi_audio[iidn].input_dev); + unregister_chrdev_region (dev_id, 4); + printk (KERN_ERR "bmi_audio_init: Can't allocate input_dev[%d]\n", idn); + return -ENOMEM; + } + + // set up input device + switch (idn) { + case BMI_AUDIO_M1: + bmi_audio[idn].input_dev->name = input_name0; + bmi_audio[idn].input_dev->phys = input_name0; + break; + case BMI_AUDIO_M2: + bmi_audio[idn].input_dev->name = input_name1; + bmi_audio[idn].input_dev->phys = input_name1; + break; + case BMI_AUDIO_M3: + bmi_audio[idn].input_dev->name = input_name2; + bmi_audio[idn].input_dev->phys = input_name2; + break; + case BMI_AUDIO_M4: + bmi_audio[idn].input_dev->name = input_name3; + bmi_audio[idn].input_dev->phys = input_name3; + break; + } + bmi_audio[idn].input_dev->id.bustype = BUS_BMI; + //bmi_audio[idn].input_dev->private = &bmi_audio[idn]; + bmi_audio[idn].input_dev->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); + bmi_audio[idn].input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + + // register input device + if (input_register_device (bmi_audio[idn].input_dev)) { + printk (KERN_ERR "bmi_audio_init() - input_register_device failed.\n"); + for (iidn = BMI_AUDIO_M1; iidn < idn; iidn++) + input_unregister_device (bmi_audio[iidn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + } + + // clear bmi devices and active bits + bmi_audio[0].bdev = NULL; + bmi_audio[1].bdev = NULL; + bmi_audio[2].bdev = NULL; + bmi_audio[3].bdev = NULL; + bmi_audio[0].active = 0; + bmi_audio[1].active = 0; + bmi_audio[2].active = 0; + bmi_audio[3].active = 0; + + // allocate private structure + mxc_audio[0] = kcalloc (1, sizeof (mxc_bmi_audio_t), GFP_KERNEL); + if (mxc_audio[0] == NULL) { + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + // allocate private structure + mxc_audio[1] = kcalloc (1, sizeof (mxc_bmi_audio_t), GFP_KERNEL); + if (mxc_audio[1] == NULL) { + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + // register the soundcards + // modules 1 and 3 + card = snd_card_new (1, id13, THIS_MODULE, sizeof (mxc_bmi_audio_t)); + if (card == NULL) { + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + card->private_data = (void *) mxc_audio[0]; + card->private_free = snd_mxc_audio_free; + + // register pcm + mxc_audio[0]->card = card; + if ((rc = snd_card_mxc_audio_pcm (mxc_audio[0], 0)) < 0) { + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + // register mixer + if (0 == bug_alsa_create_ctl (card, (void *) &audio_mixer_control[0], 0)) + printk (KERN_INFO "Control ALSA component registered\n"); + + spin_lock_init (&(mxc_audio[0]->s[0].dma_lock)); + spin_lock_init (&(mxc_audio[0]->s[1].dma_lock)); + + strcpy (card->driver, "PIM_AUDIO13"); + strcpy (card->shortname, "PIM13-audio"); + sprintf (card->longname, "PIM13 Freescale MX31"); + + // register sound card + if ((rc = snd_card_register (card)) == 0) { + PRINTK(KERN_INFO "MXC PIM13 audio support initialized\n"); + } else { + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + // modules 2 and 4 + card1 = snd_card_new (2, id24, THIS_MODULE, sizeof (mxc_bmi_audio_t)); + if (card1 == NULL) { + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + card1->private_data = (void *) mxc_audio[1]; + card1->private_free = snd_mxc_audio_free; + + // register pcm + mxc_audio[1]->card = card1; + if ((rc = snd_card_mxc_audio_pcm (mxc_audio[1], 1)) < 0) { + snd_card_free (card1); + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return -ENODEV; + } + + // register mixer + if (0 == bug_alsa_create_ctl (card1, (void *) &audio_mixer_control[1], 1)) + printk (KERN_INFO "Control ALSA component registered\n"); + + spin_lock_init (&(mxc_audio[1]->s[0].dma_lock)); + spin_lock_init (&(mxc_audio[1]->s[1].dma_lock)); + + strcpy (card1->driver, "PIM_AUDIO24"); + strcpy (card1->shortname, "PIM24-audio"); + sprintf (card1->longname, "PIM24 Freescale MX31"); + + // register sound card + if ((rc = snd_card_register (card1)) == 0) { + PRINTK(KERN_INFO "MXC PIM24 audio support initialized\n"); + } else { + snd_card_free (card1); + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + } + + // register with BMI + rc = bmi_register_driver (&bmi_audio_driver); + if (rc) { + printk (KERN_ERR "bmi_audio.c: Can't register bmi_audio_driver\n"); + snd_card_free (card1); + snd_card_free (card); + kfree (mxc_audio[1]); + kfree (mxc_audio[0]); + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + unregister_chrdev_region (dev_id, 4); + return rc; + } + + // turn on I2S transceiver + bmi_activate_audio_ports(); + + //configure DAM and SSI + configure_dam_bmi_master (0); + configure_dam_bmi_master (1); + configure_ssi_rx (0); + configure_ssi_rx (1); + configure_ssi_tx (0); + configure_ssi_tx (1); + + printk (KERN_INFO "bmi_audio.c: BMI_AUDIO Driver v%s \n", BMIAUDIO_VERSION); + if(fcc_test) + printk (KERN_INFO "bmi_audio.c: FCC Test mode enabled\n"); + if(output_ints) + printk (KERN_INFO "bmi_audio.c: Output Jack Interrupts enabled\n"); + return 0; +} + +/* + * @brief This is the audio device driver de-initialization function. + * + * This function is called by the kernel when this device driver is about + * to be unloaded. + */ +static void __exit bmi_audio_exit (void) +{ + dev_t dev_id; + int idn; + + printk (KERN_INFO "BMI Audio driver unloading...\n"); + + // delete scheduled work + flush_scheduled_work (); + + // remove bmi functionality + bmi_unregister_driver (&bmi_audio_driver); + + // free sound cards + snd_card_free (mxc_audio[0]->card); + snd_card_free (mxc_audio[1]->card); + + // free data structures + kfree (mxc_audio[0]); + kfree (mxc_audio[1]); + + // remove input devices + for (idn = BMI_AUDIO_M1; idn < BMI_AUDIO_PIM_NUM; idn++) + input_unregister_device (bmi_audio[idn].input_dev); + + // remove control cdev + dev_id = MKDEV(major, 0); + unregister_chrdev_region (dev_id, 4); + + // turn off I2S transceiver + bmi_inactivate_audio_ports(); + + printk (KERN_INFO "BMI Audio driver unloaded.\n"); + return; +} + +/* + * Module entry points and description information. + */ + +module_init (bmi_audio_init); +module_exit (bmi_audio_exit); + +module_param(fcc_test, ushort, S_IRUGO); +MODULE_PARM_DESC(fcc_test, "FCC Test code enable"); + +module_param(output_ints, ushort, S_IRUGO); +MODULE_PARM_DESC(fcc_test, "Output Jack Interrupts enable"); + +MODULE_DESCRIPTION("BMI driver for ALSA"); +MODULE_AUTHOR("EnCADIS Design, Inc. "); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("PIM_AUDIO13"); +MODULE_SUPPORTED_DEVICE("PIM_AUDIO24"); +MODULE_SUPPORTED_DEVICE("bmi_audio_ctrl_m[1234]"); +MODULE_SUPPORTED_DEVICE("bmi_audio_status_m[1234]"); + --- /dev/null +++ git/drivers/bmi/pims/vonhippel/Makefile @@ -0,0 +1,6 @@ +# +# BMI PIMS +# + +obj-$(CONFIG_BMI_VH) += bmi_vh.o + --- /dev/null +++ git/drivers/bmi/pims/vonhippel/bmi_vh.c @@ -0,0 +1,942 @@ +/* + * bmi_vh.c + * + * BMI von Hippel device driver basic functionality + * + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#define BMIVH_VERSION "1.0" +#define RDAC_3_3V (0xAC) // 16.5K = 3.3V + +/* + * Global variables + */ + +static struct i2c_board_info iox_info = { + I2C_BOARD_INFO("VH_IOX", BMI_IOX_I2C_ADDRESS), +}; + +static struct i2c_board_info rdac_info = { + I2C_BOARD_INFO("VH_RDAC", VH_RDAC_I2C_ADDRESS), +}; + +static struct i2c_board_info adc_info = { + I2C_BOARD_INFO("VH_ADC", VH_ADC_I2C_ADDRESS), +}; + +static struct i2c_board_info dac_info = { + I2C_BOARD_INFO("VH_DAC", VH_DAC_I2C_ADDRESS), +}; + + +static ushort factory_test = 0; +static ushort fcc_test = 0; +static struct timer_list fcc_timer; +static int fcc_state = 0x3; + +// private device structure +struct bmi_vh +{ + struct bmi_device *bdev; // BMI device + struct cdev cdev; // control device + struct device *class_dev; // control class device + int open_flag; // single open flag + char int_name[20]; // interrupt name + struct i2c_client *iox; + struct i2c_client *rdac; + struct i2c_client *dac; + struct i2c_client *adc; + struct spi_device *spi; // SPI device + struct spi_board_info vh_spi_info; + char rbuf[BUF_MAX_SIZE]; // SPI read buffer + char wbuf[BUF_MAX_SIZE]; // SPI write buffer +}; + +static struct bmi_vh bmi_vh[4]; // per slot device structure +static int major; // control device major + +/* + * BMI set up + */ + +// BMI device ID table +static struct bmi_device_id bmi_vh_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_VON_HIPPEL, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(bmi, bmi_vh_tbl); + +int bmi_vh_probe (struct bmi_device *bdev); +void bmi_vh_remove (struct bmi_device *bdev); + +// BMI driver structure +static struct bmi_driver bmi_vh_driver = +{ + .name = "bmi_vh", + .id_table = bmi_vh_tbl, + .probe = bmi_vh_probe, + .remove = bmi_vh_remove, +}; + +/* + * I2C set up + */ + +// IOX +// read byte from I2C IO expander +static int ReadByte_IOX (struct i2c_client *client, unsigned char offset, unsigned char *data) +{ + int ret = 0; + + ret = i2c_master_send(client, &offset, 1); + if (ret == 1) + ret = i2c_master_recv(client, data, 1); + if (ret < 0) + printk (KERN_ERR "ReadByte_IOX() - i2c_transfer() failed...%d\n",ret); + return ret; +} + +// write byte to I2C IO expander +static int WriteByte_IOX (struct i2c_client *client, unsigned char offset, unsigned char data) +{ + int ret = 0; + unsigned char msg[2]; + + msg[0] = offset; + msg[1] = data; + ret = i2c_master_send(client, msg, sizeof(msg)); + + if (ret < 0) + printk (KERN_ERR "WriteByte_IOX() - i2c_transfer() failed...%d\n",ret); + + return ret; +} + +// RDAC +// read byte from I2C LDO RDAC +static int ReadByte_RDAC (struct i2c_client *client, unsigned char command, unsigned char *data) +{ + int ret = 0; + + ret = i2c_master_send(client, &command, 1); + + if (ret < 0) + { + printk (KERN_ERR "ReadByte_RDAC() - i2c_master_send() failed...%d\n",ret); + return ret; + } + ret = i2c_master_recv(client, data, 1); + if (ret < 0) + printk (KERN_ERR "ReadByte_RDAC() - i2c_master_recv() failed...%d\n",ret); + return ret; +} + +// write byte to I2C LDO RDAC +static int WriteByte_RDAC (struct i2c_client *client, unsigned char command, + unsigned char data, int send_data) +{ + int ret = 0; + + unsigned char msg[2]; + + msg[0] = command; + msg[1] = data; + + if (send_data) + ret = i2c_master_send(client, msg, 2); + else + ret = i2c_master_send(client, &msg[0], 1); + if (ret < 0) + printk (KERN_ERR "WriteByte_RDAC() - i2c_transfer() failed...%d\n",ret); + + return ret; +} + +// ADC +// read data from I2C ADC +static int ReadByte_ADC (struct i2c_client *client, unsigned char *data) +{ + int ret = 0; + + ret = i2c_master_recv(client, data, 3); + + if (ret < 0) + printk (KERN_ERR "ReadByte_ADC() - i2c_transfer() failed...%d\n",ret); + return ret; +} + +// write command to I2C ADC +static int WriteByte_ADC (struct i2c_client *client, unsigned char w1, unsigned char w2) +{ + int ret = 0; + unsigned char msg[2]; + + msg[0] = w1; + msg[1] = w2; + ret = i2c_master_send(client, msg, sizeof(msg)); + + if (ret < 0) + printk (KERN_ERR "WriteByte_ADC() - i2c_transfer() failed...%d\n",ret); + return ret; +} + +// DAC +// read data from I2C DAC +static int ReadByte_DAC (struct i2c_client *client, unsigned char command, unsigned char *data) +{ + int ret = 0; + + ret = i2c_master_send(client, &command, 1); + if (ret == 1) + ret = i2c_master_recv(client, data, 2); + + if (ret < 0) + printk (KERN_ERR "ReadByte_DAC() - i2c_transfer() failed...%d\n",ret); + return ret; +} + +// write command to I2C DAC +static int WriteByte_DAC (struct i2c_client *client, unsigned char w1, unsigned char w2, int send_w2) +{ + int ret = 0; + unsigned char msg[2]; + + msg[0] = w1; + msg[1] = w2; + if (send_w2) + ret = i2c_master_send(client, msg, sizeof(msg)); + else + ret = i2c_master_send(client, &msg[0], 1); + + if (ret < 0) + printk (KERN_ERR "WriteByte_DAC() - i2c_transfer() failed...%d\n",ret); + return ret; +} + + +/* + * control device operations + */ + +// open +int cntl_open(struct inode *inode, struct file *file) +{ + struct bmi_vh *vh; + + vh = container_of (inode->i_cdev, struct bmi_vh, cdev); + + // Enforce single-open behavior + + if (vh->open_flag) { + return -EBUSY; + } + vh->open_flag = 1; + + // Save vh_dev pointer for later. + + file->private_data = vh; + return 0; + +} + +// release +int cntl_release(struct inode *inode, struct file *file) +{ + struct bmi_vh *vh; + + vh = (struct bmi_vh *)(file->private_data); + vh->open_flag = 0; + return 0; +} + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + unsigned char iox_data; + unsigned char rdac_data; + // unsigned char buf[4]; + int ret = 0; + + struct bmi_vh *vh; + int slot; + + vh = (struct bmi_vh *)(file->private_data); + + // error if vh not present + if(vh->bdev == 0) + return -ENODEV; + + slot = vh->bdev->slot->slotnum; + adap = vh->bdev->slot->adap; + + // ioctl's + switch (cmd) { + + case BMI_VH_RLEDOFF: + bmi_slot_gpio_set (slot, ~VH_GPIO_RED_LED); // Red LED=OFF + break; + + case BMI_VH_RLEDON: + bmi_slot_gpio_set (slot, VH_GPIO_RED_LED); // Red LED=ON + break; + + case BMI_VH_GLEDOFF: + bmi_slot_gpio_set (slot, ~VH_GPIO_GREEN_LED); // Green LED=OFF + break; + + case BMI_VH_GLEDON: + bmi_slot_gpio_set (slot, VH_GPIO_GREEN_LED); // Green LED=ON + break; + + case BMI_VH_GETSTAT: + { + int read_data; + + if (ReadByte_IOX (vh->iox, IOX_INPUT_REG, &iox_data)) + return -ENODEV; + + read_data = iox_data | (bmi_slot_gpio_get(slot) << 8); + + if (put_user (read_data, (int __user *) arg)) + return -EFAULT; + } + break; + + case BMI_VH_MKGPIO_OUT: + if ((arg < VH_GPIO_0) || (arg > VH_GPIO_RED_LED)) + return -EINVAL; + //bmi_set_module_gpio_dir (slot, arg, BMI_GPIO_OUT); + break; + + case BMI_VH_MKGPIO_IN: + if ((arg < VH_GPIO_0) || (arg > VH_GPIO_RED_LED)) + return -EINVAL; + //bmi_set_module_gpio_dir (slot, arg, BMI_GPIO_IN); + break; + + case BMI_VH_SETGPIO: + if ((arg < VH_GPIO_0) || (arg > VH_GPIO_RED_LED)) + return -EINVAL; + //bmi_set_module_gpio_data (slot, arg, 0x1); + break; + + case BMI_VH_CLRGPIO: + if ((arg < VH_GPIO_0) || (arg > VH_GPIO_RED_LED)) + return -EINVAL; + //bmi_set_module_gpio_data (slot, arg, 0x0); + break; + + case BMI_VH_MKIOX_OUT: + if ((arg < VH_IOX_B0) || (arg > VH_IOX_B5)) + return -EINVAL; + { + unsigned char read_data; + + if (ReadByte_IOX (vh->iox, IOX_CONTROL, &iox_data)) + return -ENODEV; + + read_data = iox_data & ~(0x1 << arg); + + if (WriteByte_IOX (vh->iox, IOX_CONTROL, read_data)) + return -ENODEV; + } + break; + + case BMI_VH_MKIOX_IN: + if ((arg < VH_IOX_B0) || (arg > VH_IOX_B5)) + return -EINVAL; + { + unsigned char read_data; + + if (ReadByte_IOX (vh->iox, IOX_CONTROL, &iox_data)) + return -ENODEV; + + read_data = iox_data & (0x1 << arg); + + if (WriteByte_IOX (vh->iox, IOX_CONTROL, read_data)) + return -ENODEV; + } + break; + + case BMI_VH_SETIOX: + if ((arg < VH_IOX_B0) || (arg > VH_IOX_USB_VEN)) + return -EINVAL; + { + unsigned char read_data; + + if (ReadByte_IOX (vh->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + + read_data = iox_data | (0x1 << arg); + + if (WriteByte_IOX (vh->iox, IOX_OUTPUT_REG, read_data)) + return -ENODEV; + } + break; + + case BMI_VH_CLRIOX: + if ((arg < VH_IOX_B0) || (arg > VH_IOX_USB_VEN)) + return -EINVAL; + { + unsigned char read_data; + + if (ReadByte_IOX (vh->iox, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + + read_data = iox_data & ~(0x1 << arg); + + if (WriteByte_IOX (vh->iox, IOX_OUTPUT_REG, read_data)) + return -ENODEV; + } + break; + + case BMI_VH_SETRDAC: + rdac_data = (unsigned char) (arg & 0xFF); + + if (WriteByte_RDAC (vh->rdac, VH_RD_CMD_RDAC, rdac_data, 1)) + return -ENODEV; + + if (WriteByte_RDAC (vh->rdac, VH_RD_CMD_EE, rdac_data, 1)) + return -ENODEV; + + break; + + case BMI_VH_RDRDAC: + + if (ReadByte_RDAC (vh->rdac, VH_RD_CMD_RDAC, &rdac_data)) + return -ENODEV; + + if(copy_to_user((unsigned int *) arg, &rdac_data, sizeof(int))) + ret = -EFAULT; + else + ret = 0; + + break; + + case BMI_VH_ADCWR: + { + struct vh_adc_wr *adc_wr = NULL; + + if ((adc_wr = kmalloc(sizeof(struct vh_adc_wr), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(adc_wr, (struct vh_adc_wr *)arg, sizeof(struct vh_adc_wr))) { + kfree (adc_wr); + return -EFAULT; + } + if (WriteByte_ADC (vh->adc, adc_wr->w1, adc_wr->w2)) { + kfree (adc_wr); + return -ENODEV; + } + kfree (adc_wr); + } + break; + + case BMI_VH_ADCRD: + { + unsigned char adc_data[3]; + unsigned int ret_data; + + if (ReadByte_ADC(vh->adc, adc_data)) // read ADC conversion + return -ENODEV; + + ret_data = (unsigned int) ((adc_data[0] << 16) | (adc_data[1] << 8) | adc_data[2]); + if(copy_to_user((unsigned int *) arg, &ret_data, sizeof(int))) + ret = -EFAULT; + else + ret = 0; + } + + break; + + case BMI_VH_DACWR: + { + struct vh_dac_wr *dac_wr = NULL; + + if ((dac_wr = kmalloc(sizeof(struct vh_dac_wr), GFP_KERNEL)) == NULL) + return -ENOMEM; + if (copy_from_user(dac_wr, (struct vh_dac_wr *)arg, sizeof(struct vh_dac_wr))) { + kfree (dac_wr); + return -EFAULT; + } + if (dac_wr->w1 == VH_DAC_W1_UALL) { + if (WriteByte_DAC (vh->dac, dac_wr->w1, dac_wr->w2, 0)) { + kfree (dac_wr); + return -ENODEV; + } + } else { + if (WriteByte_DAC (vh->dac, dac_wr->w1, dac_wr->w2, 1)) { + kfree (dac_wr); + return -ENODEV; + } + } + kfree (dac_wr); + } + break; + + case BMI_VH_DACRD: + { + unsigned char dac_data[2]; + unsigned int command; + unsigned int ret_data; + + if (copy_from_user(&command, (unsigned int *)arg, sizeof(int))) { + return -EFAULT; + } + + if (!((command == VH_DAC_W1_RDA) || (command == VH_DAC_W1_RDB))) { + return -EINVAL; + } + + if (ReadByte_DAC(vh->dac, (unsigned char) command, dac_data)) { // read DAC value + return -ENODEV; + } + + ret_data = (unsigned int) ((dac_data[0] << 8) | dac_data[1]); + if(copy_to_user((unsigned int *) arg, &ret_data, sizeof(int))) { + ret = -EFAULT; + } else { + ret = 0; + } + } + + break; + + default: + return -ENOTTY; + } + + return 0; +} + +// control file operations +struct file_operations cntl_fops = { + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + +/* + * PIM functions + */ + +// FCC test timer +void ftimer(unsigned long arg) +{ + struct bmi_vh *bmi_vh = (struct bmi_vh *) arg; + int slot = bmi_vh->bdev->slot->slotnum; + + /* bmi_set_module_gpio_data (slot, VH_GPIO_RED_LED, (fcc_state & 0x2) >> 1); + bmi_set_module_gpio_data (slot, VH_GPIO_GREEN_LED, fcc_state & 0x1);*/ + fcc_state = (fcc_state + 1) % 4; + del_timer (&fcc_timer); + fcc_timer.expires = jiffies + (2 * HZ); + add_timer (&fcc_timer); +} + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + /*if (!factory_test) { + printk (KERN_ERR "Von Hippel USB power error - slot "); + switch (irq) { + case M1_IRQ: + printk (KERN_ERR "1 - powering off\n"); + bmi_slot_power_off (0); + break; + case M2_IRQ: + printk (KERN_ERR "2 - powering off\n"); + bmi_slot_power_off (1); + break; + case M3_IRQ: + printk (KERN_ERR "3 - powering off\n"); + bmi_slot_power_off (2); + break; + case M4_IRQ: + printk (KERN_ERR "3 - powering off\n"); + bmi_slot_power_off (3); + break; + } + } + disable_irq(irq);*/ + return IRQ_HANDLED; +} + +/* + * BMI functions + */ + +// probe - insert PIM +int bmi_vh_probe(struct bmi_device *bdev) +{ + int err; + int slot; + struct bmi_vh *vh; + struct i2c_adapter *adap; + struct cdev *cdev; + struct class *bmi_class; + dev_t dev_id; + int irq; + unsigned char rdac_data[1]; + unsigned char adc_data[3]; + unsigned char dac_data[2]; + unsigned long speed = 1000000; + unsigned char mode = SPI_MODE_2; // von Hippel chip select must be low active + unsigned char bits_per_word = 32; + unsigned char iox_data; + unsigned char buf[4]; + struct spi_xfer spi_xfer; + int gpio_int; + + err = 0; + slot = bdev->slot->slotnum; + adap = bdev->slot->adap; + vh = &bmi_vh[slot]; + + vh->bdev = 0; + vh->open_flag = 0; + + // Create 1 minor device + cdev = &vh->cdev; + cdev_init (cdev, &cntl_fops); + + dev_id = MKDEV(major, slot); + err = cdev_add (cdev, dev_id, 1); + if (err) { + return err; + } + + // Create class device + bmi_class = bmi_get_class (); + vh->class_dev = device_create (bmi_class, NULL, MKDEV (major, slot), NULL, "bmi_vh_control_m%i", slot+1); + + if (IS_ERR(vh->class_dev)) { + printk (KERN_ERR "Unable to create " + "class_device for bmi_vh_m%i; errno = %ld\n", + slot+1, PTR_ERR(vh->class_dev)); + vh->class_dev = NULL; + cdev_del (&vh->cdev); + return -ENODEV; + } + + // bind driver and bmi_device + vh->bdev = bdev; + + + printk (KERN_INFO "bmi_vh.c: probe slot %d\n", slot); + vh->iox = i2c_new_device(bdev->slot->adap, &iox_info); + if (vh->iox == NULL) + printk(KERN_ERR "IOX NULL...\n"); + vh->rdac = i2c_new_device(bdev->slot->adap, &rdac_info); + if (vh->rdac == NULL) + printk(KERN_ERR "RDAC NULL...\n"); + vh->adc = i2c_new_device(bdev->slot->adap, &adc_info); + if (vh->adc == NULL) + printk(KERN_ERR "ADC NULL...\n"); + vh->dac = i2c_new_device(bdev->slot->adap, &dac_info); + if (vh->dac == NULL) + printk(KERN_ERR "DAC NULL...\n"); + + // SPI + strcpy(vh->vh_spi_info.modalias, "spidev"); + vh->vh_spi_info.max_speed_hz = speed; + vh->vh_spi_info.bus_num = bdev->slot->spi_bus_num; + vh->vh_spi_info.chip_select = bdev->slot->spi_cs; + vh->vh_spi_info.mode = mode; + + vh->spi = spi_new_device(spi_busnum_to_master(vh->vh_spi_info.bus_num), &vh->vh_spi_info) ; + if (!vh->spi) + printk(KERN_WARNING "VH: spi_new_device failed\n"); + + bmi_device_set_drvdata (bdev, vh); + // configure IOX + if (factory_test) { + if (WriteByte_IOX(vh->iox, IOX_OUTPUT_REG, 0x55) < 0) { // all outputs high + goto err1; + } + + if (WriteByte_IOX(vh->iox, IOX_CONTROL, 0xAA) < 0) { // IOX[7,5,3,1]=IN, IOX[6,4,2,0]=OUT + goto err1; + } + } else { + if (WriteByte_IOX(vh->iox, IOX_OUTPUT_REG, 0x7F) < 0) { // USB power on, other outputs high + goto err1; + } + + if (WriteByte_IOX(vh->iox, IOX_CONTROL, 0x80) < 0) { // IOX[7]=IN, IOX[6:0]=OUT + goto err1; + } + } + + mdelay(100); + + // read RDAC + if (ReadByte_RDAC(vh->rdac, VH_RD_CMD_RDAC, rdac_data) < 0) { // read LDO RDAC register + goto err1; + } + + printk (KERN_INFO "bmi_vh.c: probe RDAC = 0x%x\n", *rdac_data); + + if (factory_test) { + + mdelay(100); // RDAC recovery time + + // set LDO voltage to 3.3V + *rdac_data = (unsigned char) RDAC_3_3V; + + if (WriteByte_RDAC (vh->rdac, VH_RD_CMD_RDAC, *rdac_data, 1) < 0) { + goto err1; + } + + mdelay(100); + + if (WriteByte_RDAC (vh->rdac, VH_RD_CMD_EE, *rdac_data, 1) < 0) { + goto err1; + } + + mdelay(100); + + // read EEPROM + if (ReadByte_RDAC(vh->rdac, VH_RD_CMD_EE, rdac_data) < 0) { // read LDO EEPROM + goto err1; + } + + printk (KERN_INFO "bmi_vh.c: probe EEPROM = 0x%x\n", *rdac_data); + + mdelay(100); + } + + // read ADC + if (ReadByte_ADC(vh->adc, adc_data) < 0) { // read initial ADC conversion + goto err1; + } + + printk (KERN_INFO "bmi_vh.c: probe ADC = 0x%x%x%x\n", adc_data[0], adc_data[1], adc_data[2]); + + if (factory_test) { + + // power up DAC + if (WriteByte_DAC(vh->dac, VH_DAC_W1_EC, VH_DAC_BCH | VH_DAC_PU, 1) < 0) { + goto err1; + } + + // Write DAC data + if (WriteByte_DAC(vh->dac, VH_DAC_W1_ALL | 0x0, 0xF0, 1) < 0) { // write A, B, inputs and update + goto err1; + } + } + + // read DAC + if (ReadByte_DAC(vh->dac, VH_DAC_W1_RDA, dac_data) < 0) { // read initial DAC A value + goto err1; + } + + printk (KERN_INFO "bmi_vh.c: probe DAC = 0x%x%x\n", dac_data[0], dac_data[1]); + + + + // Initialize GPIOs (turn LED's on) + if (factory_test) { + bmi_slot_gpio_configure (slot, VH_GPIO_RED_LED); // (test I2S) + bmi_slot_gpio_configure (slot, VH_GPIO_GREEN_LED); // (test I2S) + bmi_slot_gpio_configure (slot, VH_GPIO_1); // (test I2S) + bmi_slot_gpio_configure (slot, VH_GPIO_0 | VH_GPIO_1); // (test interrupt) + } else { + bmi_slot_gpio_configure (slot, RED_LED |GREEN_LED ); // Red LED=ON + bmi_slot_gpio_set (slot, ~(RED_LED | GREEN_LED)); + mdelay(200); + + // turn LED's off + bmi_slot_gpio_set (slot, (RED_LED | GREEN_LED)); // Red, Green LED=OFF + + if (WriteByte_IOX(vh->iox, IOX_OUTPUT_REG, 0x70) < 0) { // USB power on, IOX[3:0] low, other outputs high + printk (KERN_ERR "bmi_vh.c: probe() - write IOX failed\n"); + //bmi_device_spi_cleanup(bdev); + goto err1; + } + } + + // request PIM interrupt + irq = bdev->slot->status_irq; + sprintf (vh->int_name, "bmi_vh%d", slot); + if (request_irq(irq, &module_irq_handler, 0, vh->int_name, vh)) { + printk (KERN_ERR "bmi_vh.c: Can't allocate irq %d or find von Hippel in slot %d\n", + irq, slot); + //bmi_device_spi_cleanup(bdev); + goto err1; + + //return -EBUSY; + } + + if (fcc_test) { + init_timer (&fcc_timer); + fcc_timer.data = (unsigned long) &bmi_vh[slot]; + fcc_timer.expires = jiffies + (2 * HZ); + fcc_timer.function = ftimer; + add_timer (&fcc_timer); + } + + + return 0; + + err1: + vh->class_dev = NULL; + cdev_del (&vh->cdev); + device_destroy (bmi_class, MKDEV(major, slot)); + bmi_device_set_drvdata (bdev, 0); + vh->bdev = 0; + i2c_unregister_device(vh->iox); + i2c_unregister_device(vh->rdac); + i2c_unregister_device(vh->adc); + i2c_unregister_device(vh->dac); + spi_unregister_device(vh->spi); + return -ENODEV; +} + +// remove PIM +void bmi_vh_remove(struct bmi_device *bdev) +{ + int slot; + struct bmi_vh *vh; + struct class *bmi_class; + int irq; + + printk(KERN_INFO "bmi_vh: Module Removed...\n"); + slot = bdev->slot->slotnum; + vh = &bmi_vh[slot]; + + i2c_unregister_device(vh->iox); + i2c_unregister_device(vh->rdac); + i2c_unregister_device(vh->adc); + i2c_unregister_device(vh->dac); + spi_unregister_device(vh->spi); + + if (factory_test) { + // disable uart transceiver + bmi_slot_uart_disable (slot); + } + + if (fcc_test) + del_timer (&fcc_timer); + + irq = bdev->slot->status_irq; + free_irq (irq, vh); + + bmi_slot_gpio_configure(slot, 0); + //bmi_device_spi_cleanup(bdev); + + bmi_class = bmi_get_class (); + device_destroy (bmi_class, MKDEV(major, slot)); + + vh->class_dev = 0; + + cdev_del (&vh->cdev); + + // de-attach driver-specific struct from bmi_device structure + bmi_device_set_drvdata (bdev, 0); + vh->bdev = 0; + + return; +} + +/* + * module routines + */ + +static void __exit bmi_vh_cleanup(void) +{ + dev_t dev_id; + + bmi_unregister_driver (&bmi_vh_driver); + + dev_id = MKDEV(major, 0); + unregister_chrdev_region (dev_id, 4); + return; +} + +static int __init bmi_vh_init(void) +{ + dev_t dev_id; + int retval; + + // alloc char driver with 4 minor numbers + retval = alloc_chrdev_region (&dev_id, 0, 4, "BMI VH Driver"); + if (retval) { + return -ENODEV; + } + + major = MAJOR(dev_id); + retval = bmi_register_driver (&bmi_vh_driver); + if (retval) { + unregister_chrdev_region(dev_id, 4); + return -ENODEV; + } + + if(factory_test) + printk (KERN_INFO "bmi_vh.c: Factory Test mode enabled\n"); + + if(fcc_test) + printk (KERN_INFO "bmi_vh.c: FCC Test mode enabled\n"); + + printk (KERN_INFO "bmi_vh.c: BMI_VH Driver v%s \n", BMIVH_VERSION); + + return 0; +} + + +module_init(bmi_vh_init); +module_exit(bmi_vh_cleanup); + +module_param(factory_test, ushort, S_IRUGO); +MODULE_PARM_DESC(factory_test, "Factory Test code enable"); + +module_param(fcc_test, ushort, S_IRUGO); +MODULE_PARM_DESC(fcc_test, "FCC Test code enable"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Buglabs Inc."); +MODULE_DESCRIPTION("BMI von Hippel device driver"); +MODULE_SUPPORTED_DEVICE("bmi_vh_control_mX"); + --- /dev/null +++ git/drivers/bmi/pims/zb/Makefile @@ -0,0 +1,5 @@ +# +# BMI PIMS ZigBee +# +bmi_zb-objs := bmi_zigbee.o bmi_zaccel.o bmi_zprotocol.o bmi_znetdev.o +obj-$(CONFIG_BMI_ZB) += bmi_zb.o --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_zaccel.c @@ -0,0 +1,684 @@ +/* + * + * bmi_zaccel.c + * + * API to Zaccel device + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include "bmi_zigbee.h" +#include "bmi_zaccel.h" + +int reset_count = 0; + +int zb_ReadConfiguration(struct bmi_zb *zb, unsigned char configId, unsigned char *buf) +{ + unsigned char data[128]; + unsigned char i, len; + + data[0] = 1; + data[1] = ZB_READ_CONFIG_REQ_0; + data[2] = ZB_READ_CONFIG_REQ_1; + data[3] = configId; + + // Write to the device + if(zaccel_spi_req(zb, data, 128) < 0) + { + return -1; + } + + /* + * Return value has format shown below. + * data[0] = Zaccetl return status + * data[1] = configID + * data[2] = len + * data[3-130] = value + */ + len = data[0]; + + /* + * truncate output to 128 bytes if it is longer than 128 byte. + * Zaccel data sheet indicates 0-128 bytes value. + * Add 3 byte header to the total length + */ + if(len > 128) + len = 128; + + for(i = 0; i < (len + 3); i++) + { + buf[i] = data[i]; + } + + return(len); +} + +unsigned char zb_WriteConfiguration(struct bmi_zb *zb, unsigned char configId, unsigned char len, unsigned char *value) +{ + unsigned char data[128]; + int rc; + + if(len > 128) + { + /* Len exceeds the max size */ + rc = -1; + } + data[0] = 2 + len; + data[1] = ZB_WRITE_CONFIG_REQ_0; + data[2] = ZB_WRITE_CONFIG_REQ_1; + data[3] = configId; + data[4] = len; + + memcpy((unsigned char *)(&data[5]),value,len); + + /* Write to the device. */ + zaccel_spi_req(zb, data, 128); + + /* read return status */ + rc = data[3]; + + return rc; +} + +void zb_SoftReset(struct bmi_zb *zb) +{ + unsigned char data[8]; + + data[0] = 1; + data[1] = SYS_RESET_REQ_0; + data[2] = SYS_RESET_REQ_1; + data[3] = 0; + + /* Write to the device. */ + zaccel_spi_req(zb, data, 8); +} + +void zb_sysVersion(struct bmi_zb *zb) +{ + unsigned char data[16]; + + /* SYS_VERSION command returns 5 bytes of data */ + + data[0] = 0; + data[1] = SYS_VER_0; + data[2] = SYS_VER_1; + + /* Write to the device. */ + zaccel_spi_req(zb, data, 16); +} + +int zb_sysRFpowerAmp(struct bmi_zb *zb, unsigned char pa, unsigned char power) +{ + unsigned char data[32]; + int rc = 0; + + if(power > SYS_RF_PA_MIN) + { + return -EINVAL; + } + + data[0] = 2; + data[1] = SYS_RF_POWER_AMP_0; + data[2] = SYS_RF_POWER_AMP_1; + data[3] = pa; + data[4] = power; + + /* Write to the device */ + zaccel_spi_req(zb, data, 32); + + /* compare the return values */ + if((data[3] != pa) || (data[4] != power)) + { + rc = -1; + } + + return rc; +} + +unsigned char zb_device_info_len[] = {1, 8, 2, 2, 8, 1, 2, 8}; + +int zb_GetDeviceInfo(struct bmi_zb *zb, unsigned char param,unsigned char *buf) +{ + unsigned char len; + unsigned char data[32]; + unsigned char i; + + if(param > ZB_DEVICE_LAST) + { + printk(KERN_WARNING "zb: invalid device info %d\n",param); + return -EINVAL; + } + + data[0] = 1; + data[1] = ZB_GET_DEVICE_INFO_0; + data[2] = ZB_GET_DEVICE_INFO_1; + data[3] = param; + + /* Write to the device. */ + zaccel_spi_req(zb, data, 32); + + /* data points to the value byte */ + len = zb_device_info_len[param]; + for(i = 0; i < len; i++) + { + buf[i] = data[i+4]; + } + + return(len); +} + +void zb_FindDeviceRequest(struct bmi_zb *zb, unsigned char *searchKey) +{ + unsigned char data[16]; + + data[0] = 8; + data[1] = ZB_FIND_DEVICE_REQ_0; + data[2] = ZB_FIND_DEVICE_REQ_1; + + memcpy(&data[3],searchKey,8); + + /* Write to the device. */ + zaccel_spi_req(zb, data, 16); +} + +void zb_StartDevice(struct bmi_zb *zb, unsigned char option) +{ + zb_WriteConfiguration(zb,ZCD_NV_STARTUP_OPTION,1,&option); + zb_Reset(zb,ZB_RESET); + udelay(10); + zb_Reset(zb,ZB_RELEASE); +} + +int zb_StartRequest(struct bmi_zb *zb) +{ + unsigned char data[8]; + + data[0] = 0; + data[1] = ZB_START_REQ_0; + data[2] = ZB_START_REQ_1; + + zb->z_info.msg_flag &= ~START_CNF_BIT; + + /* Write to the device */ + zaccel_spi_req(zb, data, 8); + + return 0; +} + +unsigned char zb_PermitJoiningRequest(struct bmi_zb *zb, unsigned char *dest, unsigned char timeout) +{ + unsigned char data[8]; + unsigned char rc; + + data[0] = 3; + data[1] = ZB_PERMIT_JOINING_REQ_0; + data[2] = ZB_PERMIT_JOINING_REQ_1; + data[3] = dest[0]; /* LSB of short address */ + data[4] = dest[1]; /* MSB of short address */ + data[5] = timeout; + + /* Write to the device */ + zaccel_spi_req(zb, data, 8); + + rc = data[3]; + + if(rc != Z_SUCCESS) + { + printk(KERN_WARNING "zb_PermitJoiningReques invalid 0x%x\n",rc); + } + + return rc; +} + +void zb_AllowBind(struct bmi_zb *zb, unsigned char timeout) +{ + unsigned char data[8]; + + data[0] = 1; + data[1] = ZB_ALLOW_BIND_0; + data[2] = ZB_ALLOW_BIND_1; + data[3] = timeout; + + /* Write to the device */ + zaccel_spi_req(zb, data, 8); +} + +unsigned char zb_AppRegisterRequest(struct bmi_zb *zb, unsigned char len, unsigned char *app_info) +{ + unsigned char *data; + unsigned char rc; + + data = kmalloc((len + ZCMD_HEADER), GFP_KERNEL); + + data[0] = len; + data[1] = ZB_APP_REGISTER_REQ_0; + data[2] = ZB_APP_REGISTER_REQ_1; + + memcpy(&data[3],app_info,len); + + zaccel_spi_req(zb,data, (len + ZCMD_HEADER)); + + /* Read return status */ + rc = data[3]; + if(rc == Z_SUCCESS) + { + zb->z_info.app_type = SAPI_TYPE; + } + + kfree(data); + + return rc; + +} + +unsigned char zb_AFRegisterRequest(struct bmi_zb *zb, unsigned char len, unsigned char *app_info) +{ + unsigned char *data; + unsigned char rc; + + data = kmalloc((len + ZCMD_HEADER), GFP_KERNEL); + + data[0] = len; + data[1] = AF_REGISTER_0; + data[2] = AF_REGISTER_1; + + memcpy(&data[3],app_info,len); + + zaccel_spi_req(zb,data, (len + ZCMD_HEADER)); + + /* Read return status */ + rc = data[3]; + if(rc == Z_SUCCESS) + { + zb->z_info.app_type = AF_INTERFACE_TYPE; + } + + kfree(data); + + return rc; +} + +void zb_BindRequest(struct bmi_zb *zb, unsigned char create, unsigned char *bind_info) +{ + unsigned char data[16]; + + data[0] = 0x0B; + data[1] = ZB_BIND_DEVICE_0; + data[2] = ZB_BIND_DEVICE_1; + data[3] = create; + + memcpy(&data[4], bind_info, 10); + + zaccel_spi_req(zb,data, 16); +} + +int zb_SendDataRequest(struct bmi_zb *zb, unsigned char *buf, unsigned char len) +{ + unsigned char *data; + + /* + * The user sends data to the remote ZigBee. + * Check if we use the SAPI or the AF send message. + * We expect that the user format the content of the + * message correctly. + */ + data = kmalloc((len + ZCMD_HEADER),GFP_KERNEL); + data[0] = len; + + if(zb->z_info.app_type == AF_INTERFACE_TYPE) + { + data[1] = AF_DATA_REQ_0; + data[2] = AF_DATA_REQ_1; + } + else + { + /* anything else will use SAPI */ + data[1] = ZB_SEND_DATA_REQ_0; + data[2] = ZB_SEND_DATA_REQ_1; + } + + memcpy((unsigned char *)(&data[3]), buf, len ); + + zaccel_spi_req(zb,data,(len + ZCMD_HEADER)); + return 0; +} + +int zb_SysTestLoopback(struct bmi_zb *zb) +{ + unsigned char *data; + unsigned char i, len; + unsigned char buf_len; + unsigned char pattern[32]; + int rc; + + len = 4; + buf_len = len + ZCMD_HEADER; + data = kmalloc(buf_len,GFP_KERNEL); + + data[0] = len; + data[1] = SYS_TEST_LOOPBACK_REQ_0; + data[2] = SYS_TEST_LOOPBACK_REQ_1; + + /* Prepare test pattern */ + pattern[0] = 0xAA; + pattern[1] = 0x55; + pattern[2] = 0x07; + pattern[3] = 0x5A; + pattern[4] = 0x63; + pattern[5] = 0x58; + pattern[6] = 0x74; + pattern[7] = 0x55; + pattern[8] = 0x02; + pattern[9] = 0x04; + pattern[10] = 0x08; + pattern[11] = 0x10; + pattern[12] = 0x20; + pattern[13] = 0xAA; + pattern[14] = 0x55; + + memcpy(&data[3],pattern,len); + +#define TEST_LOOPBACKx +#ifdef TEST_LOOPBACK + printk("Loopback pattern: "); + for(i = 0; i < buf_len; i++) + { + printk("%x ",data[i]); + } + printk(KERN_DEBUG "\n"); +#endif + + zaccel_spi_req(zb,data,buf_len); + + /* Verify loopback results */ + if((data[1] == (SYS_TEST_LOOPBACK_REQ_0 | RSPS_CMD)) && + (data[2] == SYS_TEST_LOOPBACK_REQ_1)) + { + if(data[0] == len) + { + rc = 0; + for(i = 0; i < len; i++) + { + if(data[ZCMD_HEADER + i] != pattern[i]) + { + rc++; + printk(KERN_WARNING "error: zb test byte %d expects %x, gets %x\n",i,pattern[i],data[ZCMD_HEADER + i]); + } + } + } + else + { + rc = -2; + } + } + else + { + rc = -1; + } + + return rc; +} + +void zDeviceReport(struct bmi_zb *zb,unsigned char type, unsigned char len,unsigned char *buf) +{ + unsigned char *msg; + + msg = kmalloc((ZCMD_HEADER + len),GFP_KERNEL); + + msg[0] = len; + msg[1] = ZB_DEVICE_INFO_CHG_0; + msg[2] = ZB_DEVICE_INFO_CHG_0; + memcpy(&msg[3],buf,len); + + len = ZCMD_HEADER + len; + +#ifdef VE_OUT + /* Send message to the application layer through control socket */ + zb_rx(zb->netdev,msg,len,Z_CONTROL_SOCK); +#endif + + kfree(msg); +} + +/* + * This routine send data originated by the user layer to + * the Z-Accel. + */ +void zaccel_xmt(struct work_struct *work) +{ + struct bmi_zb *zb; + struct zaccel_info *z_info; + struct zaccel_xmt_msg *msg; + + zb = container_of(work, struct bmi_zb, xmt_work); + z_info = &zb->z_info; + + while(!list_empty(&z_info->xmt_list)) + { + msg = list_entry(z_info->xmt_list.next, struct zaccel_xmt_msg, list); + + zb_SendDataRequest(zb, msg->buf, msg->len); + + list_del(z_info->xmt_list.next); + kfree(msg); + } +} + +/* + * zaccel_cmd_proc processes messages from the Zaccel. + */ + +void zaccel_cmd_proc(struct bmi_zb *zb, unsigned char *buf) +{ + struct zaccel_info *z_info; + struct net_zb *priv; + unsigned char len; + unsigned short cmd; + int rc; + + z_info = &zb->z_info; + + len = buf[0] + ZCMD_HEADER; + + /* process the message from Z-Accel */ + + cmd = ((((unsigned short)buf[1] << 8) & 0xFF00) | + ((unsigned short)buf[2] & 0x00FF)); + + switch(cmd) + { + case SYS_RESET_IND: + + if(buf[3] == 2) + { + /* count number of reset by watch-dog */ + reset_count++; + } + + printk(KERN_INFO "zb%d SYS_RESET_IND %d \n",zb->slot,reset_count); + /* rcv Z-Accel reset indication. */ + z_info->msg_flag |= RESET_IND_BIT; + wake_up_interruptible(&z_info->wait_queue); + + zb_rx(zb->netdev,buf,len,Z_CONTROL_SOCK); + break; + + case ZB_START_CONFIRM: + /* + * Z-Stack starts. Device is ready to run applicaton. + */ + z_info->msg_flag |= START_CNF_BIT; + wake_up_interruptible(&z_info->wait_queue); + break; + + case ZB_RCV_DATA_IND: + case AF_INCOMING_MSG: + /* + * Receive a packet from a remote device + * Send it to the user throught packet socket. + */ + len = buf[0]; + + if(zb->netdev) + { + rc = zb_rx(zb->netdev,&buf[3],len,Z_PACKET_SOCK); + priv = netdev_priv(zb->netdev); + if(rc >= 0) + { + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + } + else + { + priv->stats.rx_dropped++; + } + } + break; + + case ZB_BIND_CONFIRM: + printk(KERN_DEBUG "Rec BIND CNF - cmd %x %x rc %x len %d\n", + buf[3],buf[4],buf[5],len); + zb_rx(zb->netdev,buf,len,Z_CONTROL_SOCK); + break; + + case ZB_SEND_DATA_CONFIRM: + /* Return the results from zb_SendDataRequest */ + if(zb->netdev) + { + priv = netdev_priv(zb->netdev); + if(buf[4] == Z_SUCCESS) + priv->stats.tx_packets++; + else + { + printk(KERN_WARNING "ZB: send data failed 0x%x\n",buf[4]); + priv->stats.tx_dropped++; + } + zb_rx(zb->netdev,buf,len,Z_CONTROL_SOCK); + } + break; + + case ZDO_STATE_CHANGE_IND: + if((z_info->msg_flag & START_CNF_BIT) == 0) + { + z_info->msg_flag |= START_CNF_BIT; + wake_up_interruptible(&z_info->wait_queue); + } + break; + + default: + + /* send data to the user application */ + zb_rx(zb->netdev,buf,len,Z_CONTROL_SOCK); + break; + } +} + +int init_zaccel(struct bmi_zb *zb) +{ + struct zaccel_info *z_info; + int rc = 0; + + z_info = &zb->z_info; + memset(z_info,0, sizeof(struct zaccel_info)); + + init_waitqueue_head(&z_info->wait_queue); + + INIT_LIST_HEAD(&z_info->xmt_list); + + INIT_WORK(&zb->xmt_work, zaccel_xmt); + + z_info->msg_flag = 0; + z_info->app_type = NO_APP_TYPE; + + /* Release Z-Accel */ + zb_Reset(zb,ZB_RELEASE); + + printk(KERN_DEBUG "wait for ZACCEL_RESET_IND\n"); + /* Wait for the reset indication message from Z-Accel */ + rc = wait_event_interruptible_timeout(z_info->wait_queue, + ((z_info->msg_flag & RESET_IND_BIT) != 0),Z_RESET_TIMEOUT); + + /* + * Run Loopback test to test SPI Interface. If this test fails, and + * SPI interface is bad, we probably would not get the reset indication + * message that we were waiting for previously. + */ + + if(rc == 0) + { + printk(KERN_ERR "bmi_zaccel: Z-Accel device failed\n"); + return -ENODEV; + + } + else + { + /* a short delay to make sure that Z-Accel is done + * initializing. Advice from TI ZigBee Forum discussion. + */ + mdelay(2000); + /* SPI loopback test */ + rc = zb_SysTestLoopback(zb); + if(rc != 0) + { + printk(KERN_ERR "bmi_zaccel: SPI Loopback test FAILED %d\n",rc); + return -ENODEV; + } + } + + /* print string for python factory test. Don't remove */ + printk(KERN_INFO "bmi_zaccel: SPI Loopback test PASSED\n"); + + printk(KERN_INFO "bmi_zaccel: Z-Accel is ready\n"); + + return 0; +} + +void remove_zaccel(struct bmi_zb *zb) +{ + /* hold Zaccel reset */ + zb_Reset(zb,ZB_RESET); +} + + +void zaccel_getdev(struct bmi_zb *zb) +{ + struct zaccel_info *z_info; + struct zaccel_device zdev; + + z_info = &zb->z_info; + + zb_GetDeviceInfo(zb,ZB_DEVICE_STATE, + (unsigned char *)&zdev.state); + zb_GetDeviceInfo(zb,ZB_DEVICE_IEEE_ADDR, + (unsigned char *)&zdev.device_ieee); + zb_GetDeviceInfo(zb,ZB_DEVICE_SHORT_ADDR, + (unsigned char *)&zdev.device_short); + zb_GetDeviceInfo(zb,ZB_PARENT_SHORT_ADDR, + (unsigned char *)&zdev.parent_short); + zb_GetDeviceInfo(zb,ZB_PARENT_IEEE_ADDR, + (unsigned char *)&zdev.parent_ieee); + zb_GetDeviceInfo(zb,ZB_DEVICE_CHANNEL, + (unsigned char *)&zdev.channel); + zb_GetDeviceInfo(zb,ZB_DEVICE_PANID, + (unsigned char *)&zdev.panid); + zb_GetDeviceInfo(zb,ZB_DEVICE_EXT_PANID, + (unsigned char *)&zdev.ext_panid); + + z_info->device.state = zdev.state; + memcpy(&z_info->device.device_ieee,&zdev.device_ieee,8); + memcpy(&z_info->device.device_short,&zdev.device_short,2); + memcpy(&z_info->device.parent_short,&zdev.parent_short,2); + memcpy(&z_info->device.parent_ieee,&zdev.parent_ieee,8); + z_info->device.channel = zdev.channel; + memcpy(&z_info->device.panid,&zdev.panid,2); + memcpy(&z_info->device.ext_panid,&zdev.ext_panid,8); +} --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_zaccel.h @@ -0,0 +1,288 @@ +/* + * File: drivers/bmi/pims/zb/zaccel.h + * Author: V. Thavisri + * + * This is the header file for the CC2480 TI on + * ZigBee module. It is derived from the following source + * + */ + + +#ifndef _ZACCEL_H +#define _ZACCEL_H + +#define RSPS_CMD 0x40 +/* SYS Interface commands */ +#define SYS_RESET_REQ_0 0x41 +#define SYS_RESET_REQ_1 0x00 +#define SYS_RESET_IND 0x4180 +#define SYS_VER_0 0x21 +#define SYS_VER_1 0x02 +#define SYS_OSAL_NV_READ 0x2108 +#define SYS_OSAL_NV_WRITE 0x2109 +#define SYS_OSAL_START_TIMER 0x210A +#define SYS_OSAL_TIMER_EXPIRED 0x4181 +#define SYS_RANDOM 0X210C +#define SYS_ADC_READ 0x210D +#define SYS_GPIO 0x210E +#define SYS_TEST_RF_0 0x41 +#define SYS_TEST_RF_1 0x40 +#define SYS_TEST_LOOPBACK_REQ 0x2141 +#define SYS_TEST_LOOPBACK_REQ_0 0x21 +#define SYS_TEST_LOOPBACK_REQ_1 0x41 +#define SYS_TEST_LOOPBACK_RSP 0x6141 +#define SYS_RF_POWER_AMP_0 0x21 +#define SYS_RF_POWER_AMP_1 0x10 +#define SYS_RF_POWER_AMP_RSP 0x6110 + +/* Configuration Interface Commands */ +#define ZB_READ_CONFIG_REQ_0 0x26 +#define ZB_READ_CONFIG_REQ_1 0x04 +#define ZB_WRITE_CONFIG_REQ_0 0x26 +#define ZB_WRITE_CONFIG_REQ_1 0x05 +#define ZB_WRITE_CONFIG_RSP 0x6605 + +/* ZigBee PIM specific commands */ +#define ZB_DEVICE_INFO_CHG_0 0xFF +#define ZB_DEVICE_INFO_CHG_1 0x00 + +// Simple API Interface +#define ZB_APP_REGISTER_REQ_0 0x26 +#define ZB_APP_REGISTER_REQ_1 0x0A +#define ZB_START_REQ_0 0x26 +#define ZB_START_REQ_1 0x00 +#define ZB_START_CONFIRM 0x4680 +#define ZB_PERMIT_JOINING_REQ_0 0x26 +#define ZB_PERMIT_JOINING_REQ_1 0x08 +#define ZB_BIND_DEVICE_0 0x26 +#define ZB_BIND_DEVICE_1 0x01 +#define ZB_BIND_CONFIRM 0x4681 +#define ZB_ALLOW_BIND_0 0x26 +#define ZB_ALLOW_BIND_1 0x02 +#define ZB_ALLOW_BIND_CONFIRM 0x4682 +#define ZB_SEND_DATA_REQ_0 0x26 +#define ZB_SEND_DATA_REQ_1 0x03 +#define ZB_SEND_DATA_CONFIRM 0x4683 +#define ZB_RCV_DATA_IND 0x4687 +#define ZB_GET_DEVICE_INFO_0 0x26 +#define ZB_GET_DEVICE_INFO_1 0x06 +#define ZB_FIND_DEVICE_REQ_0 0x26 +#define ZB_FIND_DEVICE_REQ_1 0x07 +#define ZB_FIND_DEVICE_CONFIRM 0X4685 + +// AF Interface +#define AF_REGISTER_0 0x24 +#define AF_REGISTER_1 0x00 +#define AF_DATA_REQ_0 0x24 +#define AF_DATA_REQ_1 0x01 +#define AF_DATA_CONFIRM 0x4480 +#define AF_INCOMING_MSG 0x4481 + +// ZDO Interface +#define ZDO_NWK_ADDR_REQ 0x2500 +#define ZDO_IEEE_ADDR_REQ 0x2501 +#define ZDO_NODE_DESC_REQ 0x2502 +#define ZDO_NODE_DESC_RES 0x2582 +#define ZDO_SIMPLE_DESC_REQ 0x2504 +#define ZDO_SIMPLE_DESC_RSP 0x4584 +#define ZDO_ACTIVE_EP_REQ 0x2505 +#define ZDO_ACTIVE_EP_RSP 0x4585 +#define ZDO_MATCH_DESC_REQ 0x2506 +#define ZDO_MATCH_DESC_RSP 0x4586 +#define ZDO_MATCH_DESC_RSP_SENT 0x45C2 +#define ZDO_USER_DESC_REQ 0x2508 +#define ZDO_USER_DESC_RSP 0x4588 +#define ZDO_USER_DESC_SET 0x250B +#define ZDO_USER_DESC_CONF 0x4589 +#define ZDO_END_DEVICE_ANNCE 0x250A +#define ZDO_END_DEVICE_ANNCE_IND 0x45C1 +#define ZDO_END_DEVICE_BIND_REQ 0x2520 +#define ZDO_END_DEVICE_BIND_RES_RSP 0x45A0 +#define ZDO_BIND_REQ 0x2521 +#define ZDO_BIND_RSP 0x45A1 +#define ZDO_UNBIND_REQ 0x2522 +#define ZDO_UNBIND_RSP 0x45A2 +#define ZDO_MGMT_LQI_REQ 0x2531 +#define ZDO_MGMT_LQI_RSP 0x45B1 +#define ZDO_MGMT_LEAVE_REQ 0x2534 +#define ZDO_MGMT_LEAVE_RSP 0x45B4 +#define ZDO_MGMT_PERMIT_JOIN_REQ 0x2536 +#define ZDO_MGMT_PERMIT_JOIN_RSP 0x45B6 +#define ZDO_STATE_CHANGE_IND 0x45C0 + +// ZCD_NV_STARTUP_OPTION value +#define ZCD_STARTOPT_DEFAULT_CONFIG 0x01 +#define ZCD_STARTOPT_DEFAULT_NETWORK 0x02 +#define ZCD_STARTOPT_AUTO_START 0x04 +#define ZCD_STARTOPT_VALID 0x02 // max valid option value +#define ZCD_STARTOPT_MASK 0x03 + +// ZCD_NV_LOGICAL_TYPE +#define ZB_COORDINATOR 0x00 +#define ZB_ROUTER 0x01 +#define ZB_ENDDEVICE 0x02 +#define ZB_VALID_DEVICE 0x02 +#define ZB_DEVICE_MASK 0x03 +#define ZB_INVALID_DEVICE 0xFF + +// ZB_GET_DEVICE_INFO parameters +#define ZB_DEVICE_STATE 0x00 +#define ZB_DEVICE_IEEE_ADDR 0x01 +#define ZB_DEVICE_SHORT_ADDR 0x02 +#define ZB_PARENT_SHORT_ADDR 0x03 +#define ZB_PARENT_IEEE_ADDR 0x04 +#define ZB_DEVICE_CHANNEL 0x05 +#define ZB_DEVICE_PANID 0x06 +#define ZB_DEVICE_EXT_PANID 0x07 +#define ZB_DEVICE_LAST 0x07 + +/* Z-Accel command return value */ +#define Z_SUCCESS 0x00 +#define Z_FAILURE 0x01 +#define Z_INVALID_PARAM 0x02 + +#define Z_ALLOW_BIND 0xFF +#define Z_DENY_BIND 0x00 + +#define Z_PERMIT_JOIN 0xFF +#define Z_DENY_JOIN 0x00 + +#define Z_BIND_CREATE 0x01 +#define Z_BIND_REMOVE 0x00 + +#define Z_CONFIG_OFFSET 6 + +#define SYS_RF_PA_MIN 25 /* valid power level 0 - 25 */ + +// ZB_DEVICE_STATE definition +typedef enum +{ + DEV_HOLD, // Initialized - not started automatically + DEV_INIT, // Initialized - not connected to anything + DEV_NWK_DISC, // Discovering PAN's to join + DEV_NWK_JOINING, // Joining a PAN + DEV_NWK_REJOIN, // ReJoining a PAN, only for end devices + DEV_END_DEVICE_UNAUTH, // Joined but not yet authenticated by trust center + DEV_END_DEVICE, // Started as device after authentication + DEV_ROUTER, // Device joined, authenticated and is a router + DEV_COORD_STARTING, // Started as Zigbee Coordinator + DEV_ZB_COORD, // Started as Zigbee Coordinator + DEV_NWK_ORPHAN // Device has lost information about its parent.. +} devStates_t; + +// zstate - Z-Accel device state +#define ZACCEL_RESET 0 +#define ZACCEL_WAIT_RELEASE 1 +#define ZACCEL_RESET_IND 2 +#define ZACCEL_START_REQ 3 +#define ZACCEL_START_CNF 4 +#define ZACCEL_START_FAIL 5 +#define ZACCEL_APP_START 6 +#define ZACCEL_TERMINATE 7 +#define ZACCEL_POLL_DONE 8 +#define ZACCEL_UNDEFINE 0xFF + +/* periodic timer runs every 50 ms */ +#define Z_TIMER (50*HZ)/1000 +#define Z_RESET_TIMEOUT (20*HZ) +#define Z_CNF_TIMEOUT (15*HZ) +#define Z_POLL_TIMER (50*HZ)/1000 +#define Z_SRDY_TIMEOUT (4*HZ) + +/* frequency to update zbinfo sysfs = (Z_TIMER * UPDATE_TICKS) */ +#define UPDATE_TICKS 20 + +/* Reset option for ioctl */ +#define Z_DONT_RESET 0 +#define Z_HW_RESET 1 +#define Z_SW_RESET 2 + +#define ZCMD_BUF (256 + 3) +#define ZCMD_HEADER 3 + +struct zaccel_app_id +{ + unsigned char endpoint; + unsigned short profile_id; + unsigned short device_id; + unsigned char device_ver; + unsigned char unused; + unsigned char icmd_num; + +}; +struct zaccel_app_struct +{ + struct zaccel_app_id info; + unsigned char commands[1]; +}; + +struct zaccel_version +{ + unsigned char transportRev; + unsigned char product; + unsigned char majorRel; + unsigned char minorRel; + unsigned char hwRev; +}; + +struct zaccel_config +{ + unsigned char device; // ZCD_NV_LOGICAL_TYPE + unsigned long chanlist; + unsigned short panid; +}; + +extern unsigned char zb_device_info_len[]; +#define ZDEVICE_INFO_NUM 8 + +struct zaccel_device +{ + unsigned char state; + unsigned char device_ieee[8]; + unsigned char device_short[2]; + unsigned char parent_short[2]; + unsigned char parent_ieee[8]; + unsigned char channel; + unsigned char panid[2]; + unsigned char ext_panid[8]; +}; + +#define ZBIND_NUM_MAX 64 +struct zaccel_info +{ + unsigned char lastReset; + struct zaccel_version ver; + struct zaccel_device device; + + struct list_head xmt_list; + wait_queue_head_t wait_queue; + unsigned short msg_flag; + unsigned short app_type; +}; + +#define NO_APP_TYPE 0 +#define SAPI_TYPE 1 +#define AF_INTERFACE_TYPE 2 + +#define ZBUF_MAX_SIZE 150 + +/* struct zaccel_info msg_flag bit definition */ +#define START_CNF_BIT 0x0001 +#define RESET_IND_BIT 0x0002 + +/* + * XMT_MSG_SIZE = Maximum length of the ZB_SEND_DATA_REQUEST content. + * 2 bytes Destination, 2 bytes Command ID, 1 byte Handle + * 1 byte Ack, 1 byte Radius, 1 byte len, up to 84 bytes data. + */ +#define XMT_MSG_SIZE 92 + +struct zaccel_xmt_msg +{ + struct list_head list; + unsigned char len; + unsigned char buf[XMT_MSG_SIZE]; +}; + + +#endif // _ZACCEL_H --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_zigbee.c @@ -0,0 +1,1296 @@ +/* + * bmi_zigbee.c + * + * BMI zigbee device driver + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Include files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bmi_zigbee.h" +#include "bmi_zaccel.h" + +#define BMIZIGBEE_VERSION "1.1" + +// Global variables + +static struct bmi_zb bmi_zb[4]; +static int major; + +/* + * BMI set up + */ + + // BMI device ID table +static struct bmi_device_id bmi_zb_tbl[] = +{ + { + .match_flags = BMI_DEVICE_ID_MATCH_VENDOR | BMI_DEVICE_ID_MATCH_PRODUCT, + .vendor = BMI_VENDOR_BUG_LABS, + .product = BMI_PRODUCT_ZIGBEE, + .revision = BMI_ANY, + }, + { 0, }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(bmi, bmi_zb_tbl); + +int bmi_zb_probe(struct bmi_device *bdev); +void bmi_zb_remove(struct bmi_device *bdev); + +static struct semaphore spi_sem; + +// BMI driver structure +static struct bmi_driver bmi_zb_driver = +{ + .name = "bmi_zb", + .id_table = bmi_zb_tbl, + .probe = bmi_zb_probe, + .remove = bmi_zb_remove, +}; + +// IOX +// read byte from I2C IO expander +static int ReadByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char *data) +{ + int ret = 0; + struct i2c_msg rmsg[2]; + int num_msgs; + + + // Read Byte with Pointer + rmsg[0].addr = BMI_IOX_I2C_ADDRESS; + rmsg[0].flags = 0; // write + rmsg[0].len = 1; + rmsg[0].buf = &offset; + + rmsg[1].addr = BMI_IOX_I2C_ADDRESS; + rmsg[1].flags = I2C_M_RD; // read + rmsg[1].len = 1; + rmsg[1].buf = data; + + num_msgs = 2; + ret = i2c_transfer(adap, rmsg, num_msgs); + + if(ret == 2) + { + ret = 0; + } + else + { + printk(KERN_ERR "ReadByte_IOX() - i2c_transfer() zb failed.\n"); + ret = -1; + } + return ret; +} + +// IOX +// write byte from I2C IO expander +static int WriteByte_IOX(struct i2c_adapter *adap, unsigned char offset, unsigned char data) +{ + int ret = 0; + struct i2c_msg wmsg[2]; + int num_msgs; + + // Write Byte with Pointer + wmsg[0].addr = BMI_IOX_I2C_ADDRESS; + wmsg[0].flags = 0; // write + wmsg[0].len = 1; + wmsg[0].buf = &offset; + + wmsg[1].addr = BMI_IOX_I2C_ADDRESS; + wmsg[1].flags = 0; // write + wmsg[1].len = 1; + wmsg[1].buf = &data; + + num_msgs = 2; + ret = i2c_transfer(adap, wmsg, num_msgs); + + if(ret == 2) + { + ret = 0; + } + else + { + printk(KERN_ERR "WriteByte_IOX() - i2c_transfer() zb failed.\n"); + ret = -1; + } + return ret; +} + +// char device file operation controls the module LEDs and reset + +// open +int cntl_open(struct inode *inode, struct file *file) +{ + struct bmi_zb *zb; + + zb = container_of(inode->i_cdev, struct bmi_zb, cdev); + + zb->open_flag++; + + // Save zb pointer for later. + file->private_data = zb; + return 0; +} + +// release +int cntl_release(struct inode *inode, struct file *file) +{ + struct bmi_zb *zb; + + zb = (struct bmi_zb *)(file->private_data); + zb->open_flag = 0; + return 0; +} + + +// ioctl +int cntl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_adapter *adap; + struct bmi_zb *zb; + unsigned char buf[80]; + int slot; + int i; + + zb = (struct bmi_zb *)(file->private_data); + + // error if zb not present + if(zb->bdev == 0) + return -ENODEV; + + slot = zb->slot; + adap = zb->adap; + + zb->adap = adap; + + switch(cmd) + { + case BMI_ZB_RLEDOFF: + bmi_slot_gpio_write_bit(slot, ZB_GPIO_RED_LED, ZB_GPIO_LED_OFF); + break; + + case BMI_ZB_RLEDON: + bmi_slot_gpio_write_bit(slot, ZB_GPIO_RED_LED, ZB_GPIO_LED_ON); + break; + + case BMI_ZB_GLEDOFF: + bmi_slot_gpio_write_bit(slot, ZB_GPIO_GREEN_LED, ZB_GPIO_LED_OFF); + break; + + case BMI_ZB_GLEDON: + bmi_slot_gpio_write_bit(slot, ZB_GPIO_GREEN_LED, ZB_GPIO_LED_ON); + break; + + /* + * Below are unpublished commands, used for testing only. + */ + + case BMI_ZB_RESET: + i = (__user arg) & 0xF; + + if(i == 0) + { + printk("external reset\n"); + zb_Reset(zb,ZB_RESET); + mdelay(10); + zb_Reset(zb,ZB_RELEASE); + } + else if (i == 1) + { + printk("soft reset\n"); + zb_SoftReset(zb); + } + else if (i == 2) + { + printk("set startup option to default\n"); + buf[0] = 3; + zb_WriteConfiguration(zb,ZCD_NV_STARTUP_OPTION,1,(unsigned char *)&buf[0]); + } + + break; + + case BMI_ZB_SPI_SIG: + zb_ReadSRDY(zb,1); + zb_ReadMRDY(zb); + break; + + case BMI_ZB_LOOPBACK: + zb_SysTestLoopback(zb); + break; + + case BMI_ZB_STARTREQ: + zb_StartRequest(zb); + break; + + case BMI_ZB_UPDATE_STATE: + zaccel_getdev(zb); + break; + + default: + return -ENOTTY; + } + + return 0; + +} + +struct file_operations zb_fops = +{ + .owner = THIS_MODULE, + .ioctl = cntl_ioctl, + .open = cntl_open, + .release = cntl_release, +}; + + +void set_MRDY(struct bmi_zb *zb) +{ + /* Assert MRDY and SS pins */ + bmi_slot_gpio_write_bit(zb->slot, ZB_GPIO_MRDY, 0); + bmi_slot_gpio_write_bit(zb->slot, ZB_GPIO_SS, 0); +} + + +void clr_MRDY(struct bmi_zb *zb) +{ + /* De-assert MRDY and SS pins */ + bmi_slot_gpio_write_bit(zb->slot, ZB_GPIO_MRDY, 1); + bmi_slot_gpio_write_bit(zb->slot, ZB_GPIO_SS, 1); +} + +static unsigned long jiff_interval(unsigned long start_time) +{ + unsigned long tick; + + tick = jiffies; + if(tick >= start_time) + { + tick = tick - start_time; + } + else + { + tick = (0xFFFFFFFF - start_time) + tick; + } + + return tick; +} + +void zenable_irq(struct bmi_zb *zb) +{ + zb->enable = 1; +#ifdef VE_OUT + printk("e-IRQ %d depth %d\n",zb->irq,zb->int_depth); +#endif + if(zb->int_depth != 0) + { + zb->int_depth--; + enable_irq(zb->irq); + } + else + { + printk("zenable_irq: unbalance irq %d\n",zb->irq); + } +} + +int wait_for_srdy(struct bmi_zb *zb,unsigned char type) +{ + int rc = 0; + int err = 0; + + zb->srdy_state = ZB_SPI_WAIT_SRDY_H; + zb->start_time = jiffies; + + /* + * Queue work to poll for SRDY high. + * From experimenting in the lab, I found that sometimes it took longer + * for the SRDY to go high when the host sent an SREQ command + * than when the host responded to the device AREQ (SPI_POLL_TYPE) command. + * So we delay the work to poll SRDY in SPI_REQ_TYPE. + * This delay is more of a fine tune process, and can be changed + * as long as it doesn't exceed the SPI transaction processing + * to over 1 second. + */ + + if(type == SPI_POLL_TYPE) + { + zb->delay = 0; + } + else if(type == SPI_REQ_TYPE) + { + zb->delay = SPI_CHK_SRDY_JIFFIES; + } + + queue_delayed_work(zb->srdy_wq, &zb->srdy_work,zb->delay); + + /* + * I use wait_event_interruptible instead of + * wait_event_interruptible_timeout here because we schedule + * work queue to poll SRDY. The work schedules another + * work queue, if SRDY has not been changed. + * Using wait_event_interruptible_timeout can cause a race + * condition if the wait_event_interrupt_timeout timeout occurs while + * the work queue is running. + * + * The code wakes up when SRDY is high or when the work queue + * decides that it has waited too long. + */ + err = wait_event_interruptible(zb->srdy_queue, + ((zb->srdy_state == ZB_SPI_SRDY_HIGH) || + (zb->srdy_state == ZB_SPI_SRDY_EXP))); + + if(err != 0) + { + printk(KERN_WARNING "zb-%d wait_for_srdy err %d\n",zb->slot,err); + } + + if(zb->srdy_state == ZB_SPI_SRDY_EXP) + { + rc = -1; + } + + return rc; +} + +void zaccel_spi_poll(struct bmi_zb *zb) +{ + struct spi_message msg; + unsigned char buf[ZCMD_HEADER + 256]; + unsigned char len; +#define AREQ_DEBUGx +#ifdef AREQ_DEBUG + int i; +#endif + + struct spi_transfer t = + { + .len = SPI_MSG_HEADER_SIZE, + .cs_change = 0, + .delay_usecs = 0, + }; + + down(&spi_sem); + if(zb_ReadSRDY(zb,0) != 0) + { + /* + * check if SRDY is low. If not, don't run the routine. + * + * This routine is the IRQ bottom half, scheduled in the IRQ + * service routine. + * Sometimes, the SRDY goes high before the routine is executed. + * That happens when the zaccel_spi_req, which is scheduled to + * run on the same queue, is executed before + * zaccel_spi_poll is executed. Sometimes, + * the IRQ puts zaccel_spi_poll on the workqueue multiple times, + * each time when it sees SRDY low. + */ +#ifdef DEBUG_OUT + printk("zaccel_spi_poll SRDY high, depth %d\n",zb->int_depth); +#endif + + if(zb->int_depth != 0) + { + /* If the interrupt is disabled, enable it */ + zenable_irq(zb); + } + + up(&spi_sem); + return; + } + + set_MRDY(zb); + + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + + t.tx_buf = (const void *)buf; + t.rx_buf = buf; + t.len = SPI_MSG_HEADER_SIZE; + + /* Send three bytes of POLL command */ + spi_message_init(&msg); + spi_message_add_tail(&t, &msg); + + if((spi_sync(zb->spi, &msg) != 0) || (msg.status != 0)) + { + /* error reading the data. */ + printk(KERN_WARNING "send spi_sync error %d\n",msg.status); + clr_MRDY(zb); + up(&spi_sem); + zenable_irq(zb); + return; + } + + if(wait_for_srdy(zb,SPI_POLL_TYPE) < 0) + { + /* time out */ + printk(KERN_WARNING "%d - SRDY() high poll timeout\n",zb->slot); + clr_MRDY(zb); + up(&spi_sem); + zenable_irq(zb); + return; + } + + /* Read three bytes to get the message length */ + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + + t.tx_buf = (const void *)buf; + t.rx_buf = buf; + t.len = SPI_MSG_HEADER_SIZE, + t.cs_change = 0, + t.delay_usecs = 0, + + spi_message_init(&msg); + spi_message_add_tail(&t, &msg); + + if((spi_sync(zb->spi, &msg) != 0) || (msg.status != 0)) + { + /* error reading the data. */ + printk(KERN_WARNING "rcv1 spi_sync error %d\n",msg.status); + clr_MRDY(zb); + up(&spi_sem); + zenable_irq(zb); + return; + } + + /* buf[0] contains the length of the message */ + + if((buf[0] != 0) && (buf[0] != 0xFF)) + { + /* + * Read the rest of the message, if length != 0 */ + t.len = buf[0]; + + t.rx_buf = &buf[ZCMD_HEADER]; + spi_message_init(&msg); + + spi_message_add_tail(&t, &msg); + if((spi_sync(zb->spi, &msg) != 0) || (msg.status != 0)) + { + // error reading the data. Set length to zero. + buf[0] = 0; + printk(KERN_WARNING "rcv2 spi_sync error %d\n",msg.status); + } + } + + clr_MRDY(zb); + up(&spi_sem); + zenable_irq(zb); + + /* buf[0] has message length */ + len = buf[0]; + + if((buf[0] != 0xFF) && (buf[0] != 0)) + { +#ifdef AREQ_DEBUG + printk("AREQ-%d: ",zb->slot); + for(i = 0; i < (len + 3); i++) + { + printk("%x ",buf[i]); + } + + printk("\n"); +#endif + } + else + { + if(buf[0] == 0xFF) + printk(KERN_WARNING "invalid 0xFF\n"); + return; + } + + zaccel_cmd_proc(zb,buf); + return; +} + +void zaccel_chk_srdy(struct work_struct *work) +{ + struct bmi_zb *zb; + + zb = container_of(work, struct bmi_zb, srdy_work); + + /* + * check SRDY value. If it matches what we are waiting + * for, we wake up the work on the queue. + * If not, we check the timer for timeout. + * If the timer is not expired, queue another zb->srdy_work, to + * check for the signal next time. + * If the timer is expired, set srdy_state to indicate timeout + * and wake up the work. + */ + + if((zb_ReadSRDY(zb,0) == 0) && (zb->srdy_state == ZB_SPI_WAIT_SRDY_L)) + { + zb->srdy_state = ZB_SPI_SRDY_LOW; + wake_up_interruptible(&zb->srdy_queue); + return; + } + else if((zb_ReadSRDY(zb,0) == 1) && (zb->srdy_state == ZB_SPI_WAIT_SRDY_H)) + { + zb->srdy_state = ZB_SPI_SRDY_HIGH; + wake_up_interruptible(&zb->srdy_queue); + return; + } + else if(jiff_interval(zb->start_time) < SPI_SRDY_H_TIMEOUT) + { + queue_delayed_work(zb->srdy_wq, &zb->srdy_work,zb->delay); + } + else + { + zb_ReadSRDY(zb,1); + printk(KERN_WARNING "zaccel_chk_srdy state %d\n",zb->srdy_state); + zb->srdy_state = ZB_SPI_SRDY_EXP; + wake_up_interruptible(&zb->srdy_queue); + } + +} + +/* work handler to process Z-Accel request */ +void zaccel_poll_proc(struct work_struct *work) +{ + struct bmi_zb *zb; + + zb = container_of(work, struct bmi_zb, spi_work); + + zaccel_spi_poll(zb); +} + +void zaccel_sreq_proc(struct work_struct *work) +{ + struct bmi_zb *zb; + + unsigned char buf[320]; + struct spi_transfer t = + { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .cs_change = 0, + .delay_usecs = 0, + }; + + unsigned char *rbuf; + struct spi_message msg; + unsigned char type; + unsigned short len; + int i; + int rc = 0; + + zb = container_of(work, struct bmi_zb, sreq_work); + + down(&spi_sem); + + rbuf = zb->sreq_buf; + len = (size_t)(rbuf[0] + ZCMD_HEADER); + type = rbuf[1] & SPI_CMD_TYPE_MASK; + + t.len = len; + t.tx_buf = rbuf; + +#define SREQ_DEBUGx +#ifdef SREQ_DEBUG + printk("SREQ-%d: ",zb->slot); + + for(i = 0; i < len; i++) + { + printk("%x ",rbuf[i]); + } + printk("\n"); + +#endif + + set_MRDY(zb); + + zb->srdy_state = ZB_SPI_WAIT_SRDY_L; + + if(zb->enable == 1) + { + rc = wait_event_interruptible_timeout(zb->srdy_queue,zb->srdy_state == ZB_SPI_SRDY_LOW,SPI_SRDY_L_TIMEOUT); + if(rc == 0) + { + printk(KERN_WARNING "bmi_zb-%d: SRDY low timeout\n",zb->slot); + clr_MRDY(zb); + zb->srdy_state = ZB_SPI_POLL; + up(&spi_sem); + zb->sreq_ret = -1; + wake_up_interruptible(&zb->sreq_queue); + return; + } + } + else + { + /* + * The interrupt has been disabled, because SRDY is low + * prior to enter this routine. We proceed ahead with the + * SPI transaction. + */ + + if(zb_ReadSRDY(zb,0) != 0) + { + /* Double check that SRDY is low, if not, give warning */ + printk(KERN_WARNING "zaccel_req_proc interrupt disable SRDY high\n"); + } + } + + i = 0; + + spi_message_init(&msg); + spi_message_add_tail (&t, &msg); + + if(spi_sync(zb->spi, &msg) != 0 || msg.status != 0) + { + printk(KERN_WARNING "bmi_zb: a - spi_sync error %d\n",msg.status); + zb->sreq_ret = -ENODEV; + goto done; + } + + if(wait_for_srdy(zb,SPI_REQ_TYPE) < 0) + { + printk(KERN_WARNING "bmi_zb: SRDY wait to go high timeout\n"); + zb->sreq_ret = -1; + goto done; + } + + if(type == SPI_CMD_AREQ) + { + zb->sreq_ret = 0; + goto done; + } + + buf[0] = 0; /* Poll command has zero byte */ + buf[1] = 0; /* Poll command */ + buf[2] = 0; /* Poll command */ + + t.tx_buf = buf; + t.len = SPI_MSG_HEADER_SIZE; + + spi_message_init(&msg); + + spi_message_add_tail(&t, &msg); + if(spi_sync(zb->spi, &msg) != 0 || msg.status != 0) + { + printk(KERN_WARNING "bmi_zb: b- spi_sync error %d\n",msg.status); + zb->sreq_ret = -ENODEV; + goto done; + } + + /* Read the length of the data */ + if(buf[0] != 0) + { + // Set len to the length of the message and offset + // the buffer to after the header field. + t.len = buf[0]; + + t.tx_buf = (unsigned char *)buf + SPI_MSG_HEADER_SIZE; + t.rx_buf = (unsigned char *)buf + SPI_MSG_HEADER_SIZE; + spi_message_init(&msg); + + spi_message_add_tail(&t, &msg); + + rc = spi_sync(zb->spi, &msg); + clr_MRDY(zb); + + if(rc != 0 || msg.status != 0) + { + printk(KERN_WARNING "bmi_zb: c - spi_sync error %d\n",msg.status); + zb->sreq_ret = -ENODEV; + goto done; + } + } + + clr_MRDY(zb); + up(&spi_sem); + zenable_irq(zb); + + /* + * copy data back to the buffer. Make sure that we don't + * copy more data than the space available. + */ + + if(buf[0] != 0xFF) + { + /* copy data to the return buffer, as much as the length + * of the data or the buffer size (zb->sreq_len) + */ + if(zb->sreq_len > (buf[0] + 3)) + { + len = buf[0] + 3; + } + else + { + len = zb->sreq_len; + } + memcpy(rbuf,buf,len); + zb->sreq_ret = zb->sreq_len; + } + else + { + zb->sreq_ret = -2; + } + + /* We're done. Wake up the SREQ work */ + zb->srdy_state = ZB_SPI_POLL; + wake_up_interruptible(&zb->sreq_queue); + +#define SRSP_DEBUGx +#ifdef SRSP_DEBUG + printk("SRSP-%d: ",zb->slot); + if(buf[0] != 0xFF) + { + for(i = 0; i < len; i++) + { + printk("%x ",buf[i]); + } + } + else + { + printk("Invalid length\n"); + } + printk("\n"); +#endif + + /* + * Sometimes, back-to-back write configuration + * can chock the Z-Accel, result in fail SPI transaction. + * Delay the return (wait_event_interruptible_timeout + * will timeout - no one wakes up the queue), to prevent the problem. + */ + rc = 0; + wait_event_interruptible_timeout(zb->delay_queue,(rc != 0), 5); + + return; + +done: + clr_MRDY(zb); + zb->srdy_state = ZB_SPI_POLL; + up(&spi_sem); + zenable_irq(zb); + wake_up_interruptible(&zb->sreq_queue); + return; +} + +int config_ports(struct bmi_zb *zb) +{ + struct i2c_adapter *adap; + int slot; + unsigned char iox_data; + + slot = zb->slot; + adap = zb->adap; + + /* + * Configure GPIO_RED_LED and GPIO_GREEN_LED as output + * and set them to low -- LEDs on + */ + + bmi_slot_gpio_configure_as_output(slot,ZB_GPIO_RED_LED,0); + bmi_slot_gpio_configure_as_output(slot,ZB_GPIO_GREEN_LED,0); + + /* + * Configure GPIO_SS and GPIO_MRDY as output + * and set them to high -- deassert + */ + + bmi_slot_gpio_configure_as_output(slot,ZB_GPIO_MRDY,1); + bmi_slot_gpio_configure_as_output(slot,ZB_GPIO_SS,1); + + /* + * Set ZB_RST to output port. + * Set LSR_MRDY_IOX and LSR_SRDY_IOX to input port. + */ + + if(ReadByte_IOX(adap, IOX_CONTROL, &iox_data)) + { + printk(KERN_ERR "Unable to ReadByte_IOX - zb slot %d\n",slot); + return -ENODEV; + } + + /* Set SRDY and MRDY port to input and RST to output */ + iox_data |= (ZB_IOX_SRDY | ZB_IOX_MRDY); + iox_data &= ~(ZB_IOX_RST); + + if(WriteByte_IOX(adap, IOX_CONTROL, iox_data)) + { + printk(KERN_ERR "Unable to WriteByte_IOX - zb slot %d\n",slot); + return -ENODEV; + } + + return 0; +} + +// interrupt handler +static irqreturn_t module_irq_handler(int irq, void *dummy) +{ + struct bmi_zb *zb; + + disable_irq_nosync(irq); + + switch(irq) + { + case M1_IRQ: + zb = &bmi_zb[0]; + break; + case M2_IRQ: + zb = &bmi_zb[1]; + break; + case M3_IRQ: + zb = &bmi_zb[2]; + break; + case M4_IRQ: + zb = &bmi_zb[3]; + break; + default: + return IRQ_HANDLED; + } + + zb->enable = 0; + zb->int_depth++; + + if(zb->srdy_state == ZB_INT_WAIT_SRDY) + { + /* + * This interrupt is a part of host SREQ or AREQ. + * Set srdy flag to 0 to indicate that SRDY pin is asserted. + * Wake up the zb->spi_queue that is waiting for the event. + */ + zb->srdy_state = ZB_INT_SRDY_LOW; + wake_up_interruptible(&zb->srdy_queue); + } + else + { + /* + * Z-Accel has an AREQ frame to send. + * Schedule SPI receive task to pull data out. + */ + queue_work(zb->spi_wq, &zb->spi_work); + } + + return IRQ_HANDLED; +} + +/* + * BMI functions + */ + +int bmi_zb_probe(struct bmi_device *bdev) +{ + struct bmi_zb *zb; + struct i2c_adapter *adap; + struct class *bmi_class; + struct cdev *cdev; + struct net_device *netdev; + struct net_zb *priv; + dev_t dev_id; + int slot; + int irq; + int err; + char name[IFNAMSIZ]; + + err = 0; + slot = bmi_device_get_slot(bdev); + adap = bmi_device_get_i2c_adapter(bdev); + + zb = &bmi_zb[slot]; + + zb->slot = slot; + zb->adap = adap; + + dev_id = MKDEV(major, slot); + + // Initialize GPIOs, turn on Red and Green LEDs. + if(config_ports(zb)) + { + printk(KERN_ERR "Unable to configure ZB port pins slot %d\n",(slot+1)); + return -EFAULT; + } + + // Hold Z-Accel reset + zb_Reset(zb,ZB_RESET); + + // setup SPI + printk(KERN_INFO "ZB SPI_MODE_2 clock %d\n",ZB_SPI_SPEED); + if(bmi_device_spi_setup(bdev, ZB_SPI_SPEED, SPI_MODE_2, ZB_SPI_BPW)) + { + printk(KERN_ERR "Unable to setup spi\n"); + return -EFAULT; + } + bmi_slot_spi_enable(slot); + + zb->spi = &bdev->spi; + zb->srdy_state = ZB_SPI_POLL; + + INIT_WORK(&zb->spi_work, zaccel_poll_proc); + INIT_WORK(&zb->sreq_work, zaccel_sreq_proc); + INIT_DELAYED_WORK(&zb->srdy_work, zaccel_chk_srdy); + + init_waitqueue_head(&zb->sreq_queue); + init_waitqueue_head(&zb->srdy_queue); + init_waitqueue_head(&zb->delay_queue); + + /* + * create a thread to handle SPI access. + * spi_sem allows one ZigBee module to perform and complete its + * SPI transaction before other Zigbee does its SPI. + */ + if(slot == 0) + { + zb->spi_wq = create_singlethread_workqueue("zaccel_spi0"); + zb->srdy_wq = create_singlethread_workqueue("zaccel_srdy0"); + } + else if(slot == 1) + { + zb->spi_wq = create_singlethread_workqueue("zaccel_spi1"); + zb->srdy_wq = create_singlethread_workqueue("zaccel_srdy1"); + } + else if(slot == 2) + { + zb->spi_wq = create_singlethread_workqueue("zaccel_spi2"); + zb->srdy_wq = create_singlethread_workqueue("zaccel_srdy2"); + } + else if(slot == 3) + { + zb->spi_wq = create_singlethread_workqueue("zaccel_spi3"); + zb->srdy_wq = create_singlethread_workqueue("zaccel_srdy3"); + } + + + if((!zb->spi_wq) && (!zb->srdy_wq)) + { + printk(KERN_ERR "ZB: create workqueue failed %d\n",slot); + if(zb->spi_wq) + { + destroy_workqueue(zb->spi_wq); + } + + if(zb->srdy_wq) + { + destroy_workqueue(zb->srdy_wq); + } + + bmi_slot_spi_disable(slot); + bmi_device_spi_cleanup(bdev); + return -ENOMEM; + } + + // request PIM interrupt + irq = bmi_device_get_status_irq(bdev); + zb->irq = irq; + zb->int_depth = 0; + + sprintf(zb->int_name, "bmi_zb%d", slot); + err = request_irq(irq, &module_irq_handler, 0, zb->int_name, zb); + if(err) + { + printk(KERN_ERR "bmi_zb.c: Can't allocate irq %d nor find ZB in slot %d \n", irq, slot); + destroy_workqueue(zb->spi_wq); + destroy_workqueue(zb->srdy_wq); + bmi_slot_spi_disable(slot); + bmi_device_spi_cleanup(bdev); + return -EBUSY; + } + + printk(KERN_INFO "bmi_zb.c: ZIGBEE create class device\n"); + bmi_class = bmi_get_bmi_class(); + zb->class_dev = device_create(bmi_class, NULL, dev_id, zb, + "bmi_zb%i", slot + 1); + + if(IS_ERR(zb->class_dev)) + { + printk(KERN_ERR "Unable to create " + "class_device for bmi_zb_%i; errno = %ld\n", + slot+1, PTR_ERR(zb->class_dev)); + zb->class_dev = NULL; + err = -ENODEV; + goto error; + } + + /* Allocate network device */ + sprintf(name, "zb%d",(slot+1)); + netdev = alloc_netdev(sizeof(struct net_zb),name,zb_net_setup); + if(!netdev) + { + printk(KERN_ERR "zb%d: cannot register net device \n",slot); + err = -ENOMEM; + goto error0; + + } + + priv = netdev_priv(netdev); + priv->zb = zb; + + err = init_zaccel(zb); + if(err < 0) + { + printk(KERN_ERR "zb%d: Z-Accel device not start\n",(slot+1)); + goto error1; + } + + err = register_netdev(netdev); + if(err < 0) + { + printk(KERN_WARNING "zb: cannot register net device\n"); + goto error1; + } + + zb_create_sysfs(netdev); + + zb->netdev = netdev; + + // bind driver and bmi_device + zb->bdev = bdev; + bmi_device_set_drvdata(bdev,zb); + + cdev = &zb->cdev; + cdev_init(cdev, &zb_fops); + err = cdev_add(cdev, dev_id, 1); + if(err < 0) + { + printk(KERN_ERR "Unable to add cdev for ZigBee module\n"); + goto error2; + } + + /* turn LED's off */ + bmi_set_module_gpio_data(slot, ZB_GPIO_RED_LED, ZB_GPIO_LED_OFF); + bmi_set_module_gpio_data(slot, ZB_GPIO_GREEN_LED, ZB_GPIO_LED_OFF); + + return 0; + +error2: + zb_remove_sysfs(netdev); + zb->bdev = NULL; + bmi_device_set_drvdata(bdev,0); + + zb->netdev = NULL; + unregister_netdev(netdev); + +error1: + remove_zaccel(zb); + free_netdev(netdev); + +error0: + zb->class_dev = NULL; + device_destroy(bmi_class,dev_id); + +error: + + free_irq(irq, zb); + zb->irq = 0; + destroy_workqueue(zb->spi_wq); + destroy_workqueue(zb->srdy_wq); + bmi_slot_spi_disable(slot); + bmi_device_spi_cleanup(bdev); + + printk(KERN_ERR "bmi_zb: modprobe error %d\n",err); + return err; +} + +/* remove PIM */ +void bmi_zb_remove(struct bmi_device *bdev) +{ + int slot; + int irq; + struct bmi_zb *zb; + struct class *bmi_class; + struct net_device *netdev; + + slot = bmi_device_get_slot(bdev); + zb = &bmi_zb[slot]; + + /* Free the interrupt first. This is to prevent stranded interrupt + * when we hold the Z-Accel reset. + */ + irq = bmi_device_get_status_irq(bdev); + free_irq(irq, zb); + + destroy_workqueue(zb->spi_wq); + destroy_workqueue(zb->srdy_wq); + + remove_zaccel(zb); + + cdev_del(&zb->cdev); + + /* Unregister and deallocate net_device */ + netdev = zb->netdev; + zb_remove_sysfs(netdev); + unregister_netdev(netdev); + free_netdev(netdev); + zb->netdev = NULL; + + zb->irq = 0; + zb->spi = (struct spi_device *)NULL; + bmi_slot_spi_disable(slot); + bmi_device_spi_cleanup(bdev); + + bmi_slot_gpio_configure_all_as_inputs(slot); + + bmi_class = bmi_get_bmi_class(); + device_destroy(bmi_class, MKDEV(major,slot)); + zb->class_dev = 0; + + /* de-attach driver-specific struct from bmi_device structure */ + bmi_device_set_drvdata(bdev,0); + zb->bdev = 0; + + printk(KERN_INFO "bmi_zb: remove completed\n"); + return; +} + +int zaccel_spi_req(struct bmi_zb *zb, unsigned char *rbuf, unsigned short buf_len) +{ + int err; + + zb->sreq_buf = rbuf; + zb->sreq_len = buf_len; + + queue_work(zb->spi_wq, &zb->sreq_work); + zb->sreq_ret = 0xFF; + err = wait_event_interruptible_timeout(zb->sreq_queue,zb->sreq_ret != 0xFF,(5*HZ)); + if(err == 0) + { + printk(KERN_ERR "zaccel_spi_req timeout %d\n",zb->sreq_ret); + zb->sreq_ret = -ENODEV; + } + + return zb->sreq_ret; +} + +void zb_ReadMRDY(struct bmi_zb *zb) +{ + int mrdy; + int ss; + + mrdy = bmi_slot_gpio_read_bit(zb->slot,ZB_GPIO_MRDY); + ss = bmi_slot_gpio_read_bit(zb->slot,ZB_GPIO_SS); + printk("zb-SPI: MRDY %d SS %d\n",mrdy,ss); +} + +int zb_ReadSRDY(struct bmi_zb *zb, int print) +{ + int irq_pin; + int value; + + irq_pin = bmi_slot_status_irq_state(zb->slot); + + if(irq_pin == 1) + value = 0; + else + value = 1; + + if(print == 1) + printk("zb-SPI: srdy %d\n",value); + return value; +} + +/* Z-Accel hardware reset */ +int zb_Reset(struct bmi_zb *zb, unsigned char state) +{ + unsigned char iox_data; + + if(ReadByte_IOX (zb->adap, IOX_OUTPUT_REG, &iox_data)) + return -ENODEV; + + if(state == ZB_RESET) + { + iox_data &= ~ZB_IOX_RST; + } + else if(state == ZB_RELEASE) + { + zb->z_info.msg_flag &= ~RESET_IND_BIT; + iox_data |= ZB_IOX_RST; + } + + if(WriteByte_IOX (zb->adap, IOX_OUTPUT_REG, iox_data)) + return -ENODEV; + + return 0; +} + +static void __exit bmi_zb_cleanup(void) +{ + dev_t dev_id; + + bmi_unregister_driver(&bmi_zb_driver); + + /* Unregister PF_ZACCEL socket */ + z_sock_exit(); + + dev_id = MKDEV(major, 0); + unregister_chrdev_region(dev_id, 4); + + return; +} + +static int __init bmi_zb_init(void) +{ + dev_t dev_id; + int retval; + + // allocate char device for the module control. + retval = alloc_chrdev_region(&dev_id, 0, 4, "BMI ZigBee Driver"); + if(retval) + { + printk(KERN_ERR "Unable to allocate zb chardev_region\n"); + return -1; + } + major = MAJOR(dev_id); + + /* Register PF_ZACCEL protocol socket */ + retval = z_sock_init(); + if(retval) + { + unregister_chrdev_region(dev_id, 4); + printk(KERN_ERR "ZB: protocol register failed %d \n", retval); + return -1; + } + + init_MUTEX(&spi_sem); + + retval = bmi_register_driver(&bmi_zb_driver); + if(retval) + { + z_sock_exit(); + unregister_chrdev_region(dev_id, 4); + + printk(KERN_ERR "ZB: bmi_unregister_driver failed %d\n", retval); + return -1; + } + + printk(KERN_INFO "bmi_zb.c: BMI_ZIGBEE Driver v%s 0x%x\n", BMI_ZB_VERSION,BMI_PRODUCT_ZIGBEE); + + return 0; +} + + +module_init(bmi_zb_init); +module_exit(bmi_zb_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("V. Thavisri "); +MODULE_DESCRIPTION("BMI ZigBee device driver"); +MODULE_SUPPORTED_DEVICE("bmi_zigbee_control"); --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_zigbee.h @@ -0,0 +1,194 @@ +/* + * File: + * Author: V. Thavisri + * + * Header file for the ZB module on the MX31 BUG platform. + */ +#ifndef BMI_ZB_H +#define BMI_ZB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bmi_zaccel.h" + +#define BMI_ZB_VERSION "1.0" +#define VE_DEBUG + +// GPIO +#define ZB_GPIO_RED_LED 3 +#define ZB_GPIO_GREEN_LED 2 +#define ZB_GPIO_MRDY 1 +#define ZB_GPIO_SS 0 + +#define ZB_GPIO_LED_ON 0 +#define ZB_GPIO_LED_OFF 1 + +#define BMI_IOX_I2C_ADDRESS 0x71 +// I2C IOX register addressess +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + +#define ZB_IOX_RST 0x1 +#define ZB_IOX_MRDY 0x2 +#define ZB_IOX_SRDY 0x4 + +// SPI parameters +#define ZB_SPI_SPEED 2000000 +#define ZB_SPI_BPW 8 + +// SPI state +// check the state when SRDY is asserted to determine which +// SPI transaction the host wants to perform. +#define ZB_SPI_POLL 0 +#define ZB_SPI_WAIT_SRDY_L 1 +#define ZB_SPI_SRDY_LOW 2 +#define ZB_SPI_WAIT_SRDY_H 3 +#define ZB_SPI_SRDY_HIGH 4 +#define ZB_SPI_TIME2POLL 5 +#define ZB_SPI_SRDY_EXP 6 +#define ZB_SPI_WAIT_SRDY_Q 7 + +#define ZB_INT_POLL 0 +#define ZB_INT_WAIT_SRDY 1 +#define ZB_INT_SRDY_LOW 2 + +#define SPI_POLL_TYPE 0 +#define SPI_REQ_TYPE 1 + +#define ZB_RESET 0 +#define ZB_RELEASE 1 + +#define SPI_BUF_MAX_SIZE ZBUF_MAX_SIZE +#define SPI_MSG_HEADER_SIZE 3 + +#define SPI_CMD_POLL 0x00 +#define SPI_CMD_SREQ 0x20 +#define SPI_CMD_AREQ 0x40 +#define SPI_CMD_TYPE_MASK (SPI_CMD_SREQ | SPI_CMD_AREQ) + +#define SPI_CHK_SRDY_JIFFIES 1 +#define SPI_CHK_SRDY_TIME 4000 +#define SPI_SRDY_L_TIMEOUT (HZ>>1) +#define SPI_SRDY_H_TIMEOUT (HZ>>1) + +// Zigbee network private information +struct net_zb +{ + struct net_device *dev; + struct bmi_zb *zb; + struct net_device_stats stats; + unsigned char net_open; + unsigned char socket[Z_NUM_SOCK]; +}; + +struct bmi_zb +{ + struct bmi_device *bdev; + struct cdev cdev; + struct net_device *netdev; + struct device *class_dev; + struct i2c_adapter *adap; + struct spi_device *spi; /* SPI device */ + + struct work_struct spi_work; /* work to process Poll req */ + struct work_struct xmt_work; /* work to xmt to device */ + struct work_struct sreq_work; /* work to process sreq */ + struct work_struct state_work; /* work to check device state */ + + struct delayed_work srdy_work; /* work to chk for SRDY */ + + unsigned long start_time; + unsigned long delay; + + struct workqueue_struct *spi_wq; + struct workqueue_struct *srdy_wq; + + wait_queue_head_t srdy_queue; + unsigned char srdy_state; + unsigned char enable; + unsigned char int_depth; + + wait_queue_head_t sreq_queue; + unsigned char *sreq_buf; + unsigned short sreq_len; + int sreq_ret; + + wait_queue_head_t delay_queue; + + char int_name[20]; /* interrupt name */ + int slot; /* base unit slot number */ + int open_flag; /* single open flag */ + int irq; + struct zaccel_info z_info; +}; + +extern int zb_Reset(struct bmi_zb *zb, unsigned char state); +extern void zb_StartDevice(struct bmi_zb *zb, unsigned char option); +extern int zb_StartRequest(struct bmi_zb *zb); +extern void zb_spi_poll(void *arg); +extern int zaccel_spi_req(struct bmi_zb *zb, unsigned char *data, unsigned short buf_len); +extern int zaccel_SRDY_poll(struct bmi_zb *zb, unsigned char polarity); +extern int zb_GetDeviceInfo(struct bmi_zb *zb, unsigned char param, unsigned char *buf); +extern unsigned char zb_WriteConfiguration(struct bmi_zb *zb, unsigned char configId, unsigned char len, unsigned char *data); +extern int zb_ReadConfiguration(struct bmi_zb *zb, unsigned char configId, + unsigned char *buf); +extern int zb_zcommand(struct bmi_zb *zb, unsigned short cmd, unsigned char len, + unsigned char *buf); +extern void zb_SoftReset(struct bmi_zb *zb); +extern unsigned char zb_AppRegisterRequest(struct bmi_zb *zb, unsigned char len, + unsigned char *app_info); +extern unsigned char zb_PermitJoiningRequest(struct bmi_zb *zb, + unsigned char *dest, unsigned char timeout); +extern void zb_AllowBind(struct bmi_zb *zb, unsigned char timeout); +extern void zb_BindRequest(struct bmi_zb *zb, unsigned char create, + unsigned char *bind_info); +extern int zb_SendDataRequest(struct bmi_zb *zb, unsigned char *buf, + unsigned char len); +extern void zb_FindDeviceRequest(struct bmi_zb *zb, unsigned char *searchKey); +extern unsigned char zb_AFRegisterRequest(struct bmi_zb *zb, unsigned char len, unsigned char *app_info); +extern int zb_sysRFpowerAmp(struct bmi_zb *zb, unsigned char pa, unsigned char power); + +extern void zaccel_cmd_proc(struct bmi_zb *zb,unsigned char *buf); +extern int zb_rx(struct net_device *dev, unsigned char *buf, + unsigned char len, unsigned short type); +extern int z_sock_init(void); +extern int zdev_setopt(struct net_device *dev,int cmd, int len, + unsigned char *buf); +extern int zdev_getopt(struct net_device *dev, int cmd, int *len, + unsigned char *buf); + +extern void zb_net_setup(struct net_device *dev); +extern void zaccel_getdev(struct bmi_zb *zb); + +extern void z_sock_exit(void); +extern int z_sock_init(void); +extern void zb_create_sysfs(struct net_device *net); +extern void zb_remove_sysfs(struct net_device *net); +extern int init_zaccel(struct bmi_zb *zb); +extern void remove_zaccel(struct bmi_zb *zb); +extern int zb_ReadSRDY(struct bmi_zb *zb, int print); +extern void zb_ReadMRDY(struct bmi_zb *zb); +extern void zaccel_spi_poll(struct bmi_zb *zb); +extern int zb_SysTestLoopback(struct bmi_zb *zb); +extern void zb_sysVersion(struct bmi_zb *zb); +extern int zaccel_get_chanlist(struct bmi_zb *zb); + + + +#endif // BMI_ZB_H --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_znetdev.c @@ -0,0 +1,977 @@ +/* + * bmi_znetdev.c + * + * This file contains the zaccel network device driver codes. + */ + +#include +#include +#include +#include +#include +#include "bmi_zigbee.h" +#include "bmi_zaccel.h" + +static int zb_open(struct net_device *dev) +{ + struct bmi_zb *zb; + struct net_zb *priv; + struct zaccel_device *zdev; + + printk(KERN_DEBUG "bmi_znetdev: zb_open\n"); + priv = netdev_priv(dev); + zb = priv->zb; + zdev = &zb->z_info.device; + + priv->net_open = 1; + netif_start_queue(dev); + return 0; +} + +static int zb_close(struct net_device *dev) +{ + struct net_zb *priv; + + printk(KERN_DEBUG "bmi_znetdev: zb_close\n"); + priv = netdev_priv(dev); + + priv->net_open = 0; + netif_stop_queue(dev); + + return 0; +} + +static int zb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + unsigned char len_chk; + unsigned char offset; + struct net_zb *priv; + struct bmi_zb *zb; + struct zaccel_info *z_info; + + unsigned char *zapp; + unsigned char buf[32]; + unsigned short len; + unsigned char type; + unsigned char reason = 0; + unsigned char *user_pt; + unsigned char flag; + unsigned char state; + int i; + int rc = 0; + + priv = netdev_priv(dev); + zb = priv->zb; + z_info = &zb->z_info; + + switch(cmd) + { + case SIOCSRESET: + if(copy_from_user(buf, ifr->ifr_data, (2*sizeof(char)))) + { + printk(KERN_ERR "BMI_ZB_ANY_REQ cannot get commands\n"); + return -EFAULT; + } + + switch(buf[0]) + { + case Z_HW_RESET: + if(dev->flags & IFF_UP) + { + /* set flag to remember that IF is up previously */ + flag = IFF_UP; + dev->flags &= ~IFF_UP; + netif_stop_queue(dev); + } + + zb_Reset(zb,ZB_RESET); + udelay(10); + zb_Reset(zb,ZB_RELEASE); + + break; + + case Z_SW_RESET: + if(dev->flags & IFF_UP) + { + flag = IFF_UP; + dev->flags &= ~IFF_UP; + netif_stop_queue(dev); + } + + zb_SoftReset(zb); + break; + + case Z_DONT_RESET: + return 0; + + default: + printk(KERN_WARNING "zb ioctl reset - unknow reset type %d\n",buf[1]); + return -EINVAL; + } + + /* + * If reset the device, wait for it to come back and + * start Z-stack. + */ + rc = wait_event_interruptible_timeout(z_info->wait_queue, + ((z_info->msg_flag & RESET_IND_BIT) != 0),Z_RESET_TIMEOUT); + + if(rc == 0) + { + /* timeout and no indication received */ + reason = 1; + printk(KERN_ERR "zb: wait for reset IND timeout\n"); + rc = -EAGAIN; + } + else + { + +#ifdef VE_OUT + if(flag & IFF_UP) + { + netif_start_queue(dev); + dev->flags |= IFF_UP; + } +#endif + + rc = 0; + } + + if(rc == -EAGAIN) + { + /* copy extra code to the user space */ + if(copy_to_user(ifr->ifr_data,(void *)&reason,1)) + { + return -EFAULT; + } + } + + break; + + case SIOCGDEVICEINFO: + + if(copy_from_user(buf, ifr->ifr_data, 1)) + { + printk(KERN_ERR "SIOCGDEVICEINFO cannot get parameter\n"); + return -EFAULT; + } + + len = zb_GetDeviceInfo(zb,buf[0],buf); + + if(copy_to_user(ifr->ifr_data, buf, len)) + { + return -EFAULT; + } + + rc = (int)len; + break; + + case SIOCSAPPREGISTER: + if(copy_from_user(buf, ifr->ifr_data, sizeof(char))) + { + printk(KERN_ERR "SIOCSAPPREGISTER cannot get parameter\n"); + return -EFAULT; + } + + len = (unsigned short)buf[0]; + + /* + * Input Format: + * 1-byte message length + * x-bytes message (see below comment for msg format. + */ + + zapp = kmalloc(len,GFP_KERNEL); + if(zapp == NULL) + { + printk(KERN_WARNING "zb_ioctl no buf for cmd 0x%x\n",cmd); + return -ENOMEM; + } + + user_pt = (unsigned char *)ifr->ifr_data; + if(copy_from_user((void *)zapp, &user_pt[1], (unsigned long)len)) + { + kfree(zapp); + return -EFAULT; + } + + /* + * verify the length of the data. + * Format of message is (number in parenthesis = num of bytes) + * + * zapp[offset] content + * 0 appEndpoint(1) + * 1 appProfileID(2) + * 3 deviceId(2) + * 5 deviceVersion(1) + * 6 unused(1) + * 7 inputCommandNum(1) + * [8] [inputCommand(2),inputCommand(2), ...] + * 8 + (inputNum * 2) outputCommandNum(1) + * [outputCommand(2),outputCommand(2), ...] + */ + + /* 8 bytes between appEndpoint to inputCommandNum */ + len_chk = 8; + + /* + * Get the offset to outputCommandNum byte + * and add the number of inputCommand bytes and outputCommandNum + * byte to the len_chk. + */ + offset = (zapp[7] << 1) + 8; + len_chk += (zapp[7] << 1) + 1; + + /* Add the number of output command bytes */ + len_chk += (zapp[offset] << 1); + + if(len_chk != len) + { + /* + * The len of the input declared is less than the + * number of data required. + */ + kfree(zapp); + printk(KERN_WARNING "SIOCSAPPREGISTER: inconsistence len %d %d\n",len_chk,len); + return -EINVAL; + } + + buf[0] = zb_AppRegisterRequest(zb,len,zapp); + + if(buf[0] != 0) + { + rc = -1; + } + + if(copy_to_user(ifr->ifr_data,buf,1)) + { + printk(KERN_WARNING "SIOCAPP result dropped\n"); + } + + kfree(zapp); + + break; + + case SIOCSALLOWBIND: + if(copy_from_user(buf, ifr->ifr_data, sizeof(char))) + { + printk(KERN_ERR "SIOCSALLOWBIND cannot get parameter\n"); + return -EFAULT; + } + + /* + * Input format:\ + * 1-bytes timeout + */ + + zb_AllowBind(zb,buf[0]); + + break; + + case SIOCSSTARTREQ: + zb_StartRequest(zb); + rc = wait_event_interruptible_timeout(z_info->wait_queue, + ((z_info->msg_flag & START_CNF_BIT) != 0),Z_CNF_TIMEOUT); + + if(rc == 0) + { + /* timeout - We didn't receive START_CNF message nor + * ZDO_STATE_CHANGE_IND. This can happens if the + * end-device or router does not find a network. + * Check if the device state had changed from DEV_HOLD + * to anything else. If it does, the stack has started. + */ + zb_GetDeviceInfo(zb,ZB_DEVICE_STATE,(unsigned char *)&state); + if((state != DEV_HOLD) || (state != DEV_INIT)) + { + printk(KERN_DEBUG "stack starts, state %d\n",state); + rc = 0; + } + else + { + rc = -1; + printk(KERN_DEBUG "Stack fails to start\n"); + } + } + else + { + printk(KERN_DEBUG "rcv ZB_START_CONFIRM\n"); + rc = 0; + } + + break; + + case SIOCSPERMITJOINING: + if(copy_from_user(buf, ifr->ifr_data, 3)) + { + printk(KERN_ERR "SIOCSPERMITJOINING: read error\n"); + return -EFAULT; + } + + /* + * Input format: + * 2-bytes 16-bit device address + * 1-bytes timeout + */ +#ifdef VE_OUT + printk(KERN_DEBUG "permitjoining address 0x%x timeout 0x%x\n",*(unsigned short *)buf,buf[2]); +#endif + + buf[0] = zb_PermitJoiningRequest(zb,&buf[0],buf[2]); + + if(buf[0] != 0) + { + rc = -1; + } + + if(copy_to_user(ifr->ifr_data,buf,1)) + { + printk(KERN_WARNING "SIOCSPERMIT result dropped\n"); + } + + break; + + case SIOCSBIND: + if(copy_from_user(buf, ifr->ifr_data, 12)) + { + printk(KERN_ERR "SIOCSBIND cannot get parameter\n"); + return -EFAULT; + } + + if((buf[0] != Z_BIND_CREATE) && (buf[0] != Z_BIND_REMOVE)) + { + return -EINVAL; + } + + zb_BindRequest(zb,buf[0],&buf[1]); + + break; + + case SIOCSFINDDEVICE: + if(copy_from_user(buf, ifr->ifr_data, 8)) + { + printk(KERN_ERR "SIOCSFINDDEVICE cannot get parameter\n"); + return -EFAULT; + } + zb_FindDeviceRequest(zb,buf); + break; + + case SIOCSAFREGISTER: + if(copy_from_user(buf, ifr->ifr_data, sizeof(char))) + { + printk(KERN_ERR "SIOCSAPPREGISTER cannot get parameter\n"); + return -EFAULT; + } + + len = (unsigned short)buf[0]; + + /* + * Input Format: + * 1-byte message length + * x-bytes message (see below comment for msg format. + */ + + zapp = kmalloc(len,GFP_KERNEL); + if(zapp == NULL) + { + printk(KERN_WARNING "zb_ioctl no buf for cmd 0x%x\n",cmd); + return -ENOMEM; + } + + user_pt = (unsigned char *)ifr->ifr_data; + if(copy_from_user((void *)zapp, &user_pt[1], (unsigned long)len)) + { + kfree(zapp); + return -EFAULT; + } + + buf[0] = zb_AFRegisterRequest(zb,len,zapp); + if(buf[0] != 0) + { + rc = -1; + } + + if(copy_to_user(ifr->ifr_data,buf,1)) + { + printk(KERN_WARNING "SIOCAPP result dropped\n"); + } + + kfree(zapp); + break; + + case SIOCSPOWERAMP: + if(copy_from_user(buf, ifr->ifr_data, 2)) + { + printk(KERN_ERR "SIOCSPOWERAMP cannot get parameter\n"); + return -EFAULT; + } + + rc = zb_sysRFpowerAmp(zb,buf[0],buf[1]); + break; + + case SIOCSZCOMMAND: + + /* + * This command passes any Z-Accel command to the device and + * returns the reply back to the user. + * Message format: + * 1-byte length of data field + * 2-bytes command + * 0-128 bytes data + */ + if(copy_from_user(buf, ifr->ifr_data, 1)) + { + printk(KERN_ERR "SIOCSZCOMMAND cannot get parameter\n"); + return -EFAULT; + } + + user_pt = (unsigned char *)ifr->ifr_data; + len = (unsigned short)buf[0] + 3; + + if(len > ZCMD_BUF) + { + printk(KERN_WARNING "SIOCSZCOMMAND message is too long\n"); + return -EINVAL; + } + + zapp = kmalloc(ZCMD_BUF,GFP_KERNEL); + if(zapp == NULL) + { + printk(KERN_WARNING "SIOCSZCOMMAND no buf for the cmd\n"); + return -ENOMEM; + } + + if(copy_from_user(zapp, ifr->ifr_data, len)) + { + printk(KERN_ERR "SIOCSZCOMMAND cannot get parameter\n"); + return -EFAULT; + } + type = zapp[1] & SPI_CMD_TYPE_MASK; + +#define VE_DEBUG +#ifdef VE_DEBUGx + printk("len %d: ",len); + for(i = 0; i < len; i++) + { + printk("%x ",zapp[i]); + } + printk("\n"); +#endif + + zaccel_spi_req(zb,zapp,len); + + /* + * read the length of the data, if it's SREQ command. + */ + + if(type != SPI_CMD_AREQ) + { + len = zapp[0]; + + if(copy_to_user(ifr->ifr_data,zapp,(len + 3))) + { + return -EFAULT; + } + } + rc = 0; + + break; + + default: + printk("zb_ioctl default cmd\n"); + rc = -EINVAL; + break; + + } + + return rc; +} + +/* + * zb_rx passes data from Z-Accel to the user via socket + */ +int zb_rx(struct net_device *dev, unsigned char *buf, unsigned char len, unsigned short type) +{ + struct sk_buff *skb; + struct net_zb *priv; + + if(!dev) + return -EINVAL; + + + priv = netdev_priv(dev); + + if(priv->socket[type] == Z_NO_SOCK) + { + return -EINVAL; + } + + /* + * Check if the device is bound to a socket. + * If not, drop the message. + */ + + dev->last_rx = jiffies; + + skb = dev_alloc_skb(len); + if(skb) + { + memcpy(skb_put(skb,len),buf,len); + + /* + * Set the device to zigbee. + * Set protocol number to zero, so the kernel will not + * pass it through the protocol stack. + */ + + skb->dev = dev; + skb->protocol = type; + + if(type == Z_PACKET_SOCK) + { + /* + * Increment packet count and notify kernel of + * the new packet. + */ + priv->stats.rx_packets++; + } + + netif_rx(skb); + } + else + { + if(type == Z_PACKET_SOCK) + priv->stats.rx_dropped++; + } + + return 0; +} + +int zb_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct bmi_zb *zb; + struct net_zb *priv; + struct zaccel_xmt_msg *msg; + unsigned char *buf; + + priv = netdev_priv(dev); + zb = priv->zb; + + if(skb->len > XMT_MSG_SIZE) + { + dev_kfree_skb(skb); + return -EINVAL; + } + + msg = (struct zaccel_xmt_msg *)kmalloc(sizeof(struct zaccel_xmt_msg), GFP_KERNEL); + buf = msg->buf; + + memcpy(buf,skb->data,skb->len); + + msg->len = skb->len; + + /* + * zb_tx is called by dev_queue_xmit, which requires atomic + * operation. We cannot call zb_SendDataRequest directly here + * to schedule SPI transfer to the Z-Accel because + * it causes "BUG: scheduling while atomic exception." + * We put the data in a message queue and + * schedule a work queue to call zb_SendDataRequest later. + */ + + list_add_tail(&msg->list, &zb->z_info.xmt_list); + schedule_work(&zb->xmt_work); + + /* Move this statistic to a real packet transmission location later */ + priv->stats.tx_packets++; + + dev->trans_start = jiffies; + + /* Free sku buffer */ + dev_kfree_skb(skb); + + return 0; +} + +struct net_device_stats *zb_stats(struct net_device *dev) +{ + struct net_zb *priv = netdev_priv(dev); + + return &priv->stats; +} + +void zb_net_setup(struct net_device *dev) +{ + struct net_zb *priv; + + priv = netdev_priv(dev); + + priv->dev = dev; + priv->socket[Z_PACKET_SOCK] = Z_NO_SOCK; + priv->socket[Z_CONTROL_SOCK] = Z_NO_SOCK; + + dev->open = zb_open; + dev->stop = zb_close; + dev->hard_start_xmit = zb_tx; + dev->tx_queue_len = 32; + dev->get_stats = zb_stats; + dev->do_ioctl = zb_ioctl; + dev->flags = 0; +} + +int zdev_setopt(struct net_device *dev,int cmd, int len, unsigned char *buf) +{ + struct net_zb *priv = netdev_priv(dev); + struct bmi_zb *zb; + int rc; + + zb = priv->zb; + rc = (int)zb_WriteConfiguration(zb,(unsigned char)cmd,(unsigned char)len,buf); + return rc; +} + +int zdev_getopt(struct net_device *dev, int cmd, int *len, unsigned char *buf) +{ + struct net_zb *priv = netdev_priv(dev); + struct bmi_zb *zb; + + zb = priv->zb; + + *len = (int)zb_ReadConfiguration(zb,(unsigned char)cmd,buf); + if(*len != 0) + { + return 0; + } + else + { + return -EFAULT; + } + +} + +static ssize_t show_device(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + int size = 0; + int rc; + unsigned char rbuf[8]; + + rc = zb_ReadConfiguration(zb,ZCD_NV_LOGICAL_TYPE,rbuf); + if(rc <= 0) + { + size = sprintf(buf,"Bad read. Try again\n"); + } + switch(rbuf[Z_CONFIG_OFFSET]) + { + case ZB_COORDINATOR: + size = sprintf(buf,"coordinator\n"); + break; + + case ZB_ROUTER: + size = sprintf(buf,"router\n"); + break; + + case ZB_ENDDEVICE: + size = sprintf(buf,"end-device\n"); + break; + + default: + size = sprintf(buf,"device-error\n"); + break; + } + return size; +} + +static ssize_t store_device(struct device *dev, struct device_attribute *attr, char *buf, size_t count) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + int size; + unsigned char device; + + if(memcmp(buf, "coordinator",(count-1)) == 0) + { + printk(KERN_DEBUG "zb: config to coordinator\n"); + device = ZB_COORDINATOR; + } + else if(memcmp(buf, "router",(count-1)) == 0) + { + printk(KERN_DEBUG "zb: config to router\n"); + device = ZB_ROUTER; + } + else if(memcmp(buf, "end-device",(count-1)) == 0) + { + printk(KERN_DEBUG "zb: config to end-device\n"); + device = ZB_ENDDEVICE; + } + else + { + printk(KERN_DEBUG "zb: invalid config\n"); + size = sprintf(buf,"Invalid device. Try again\n"); + return count; + } + + if(zb_WriteConfiguration(zb,ZCD_NV_LOGICAL_TYPE,1,&device) != 0) + { + size = sprintf(buf,"Bad write. Try again\n"); + } + + return count; +} + +static ssize_t store_chanlist(struct device *dev, struct device_attribute *attr, char *buf, size_t count) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + int size; + unsigned long chanlist; + unsigned char rbuf[4]; + + chanlist = simple_strtol(buf,NULL,16); + rbuf[0] = (unsigned char)(chanlist) & 0xFF; + rbuf[1] = (unsigned char)(chanlist >> 8) & 0xFF; + rbuf[2] = (unsigned char)(chanlist >> 16) & 0xFF; + rbuf[3] = (unsigned char)(chanlist >> 24) & 0xFF; + + if(zb_WriteConfiguration(zb,ZCD_NV_CHANLIST,4,(unsigned char *)rbuf) != 0) + { + size = sprintf(buf,"Bad write. Try again\n"); + } + + return count; +} + +static ssize_t show_chanlist(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + unsigned char rbuf[10]; + int size = 0; + int rc; + + rc = zb_ReadConfiguration(zb,ZCD_NV_CHANLIST,rbuf); + if(rc < 0) + { + size = sprintf(buf,"bad read. Try again\n"); + } + else + { + size = sprintf(buf,"%0x ",rbuf[Z_CONFIG_OFFSET+3]); + size += sprintf((buf+size),"%0x ",rbuf[Z_CONFIG_OFFSET+2]); + size += sprintf((buf+size),"%0x ",rbuf[Z_CONFIG_OFFSET+1]); + size += sprintf((buf+size),"%0x\n",rbuf[Z_CONFIG_OFFSET]); + } + return size; +} + +static ssize_t store_initop(struct device *dev, struct device_attribute *attr, char *buf, size_t count) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + unsigned char option; + int size; + + option = (unsigned char)simple_strtol(buf,NULL,16); + option &= ZCD_STARTOPT_MASK; + + if(zb_WriteConfiguration(zb,ZCD_NV_STARTUP_OPTION,1,(unsigned char *)&option) != 0) + { + size = sprintf(buf,"Bad write. Try again\n"); + } + + return count; +} + +static ssize_t show_initop(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + unsigned char rbuf[10]; + int size = 0; + int rc; + + rc = zb_ReadConfiguration(zb,ZCD_NV_STARTUP_OPTION,(unsigned char *)&rbuf); + if(rc < 0) + { + size = sprintf(buf,"bad read. Try again\n"); + } + else + { + size = sprintf(buf,"%0x\n",rbuf[Z_CONFIG_OFFSET]); + } + + return size; +} + +static ssize_t store_panid(struct device *dev, struct device_attribute *attr, char *buf, size_t count) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + unsigned short panid; + unsigned char rbuf[2]; + int size; + + panid = (unsigned short)simple_strtol(buf,NULL,16); + rbuf[0] = (unsigned char)(panid) & 0xFF; + rbuf[1] = (unsigned char)(panid >> 8) & 0xFF; + + if(zb_WriteConfiguration(zb,ZCD_NV_PANID,2,(unsigned char *)rbuf) != 0) + { + size = sprintf(buf,"Bad write read. Try again\n"); + } + + printk(KERN_DEBUG "zb: config panid 0x%x\n",panid); + return count; +} + +static ssize_t show_panid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + unsigned char rbuf[10]; + int size = 0; + int rc; + + rc = zb_ReadConfiguration(zb,ZCD_NV_PANID,(unsigned char *)&rbuf); + if(rc < 0) + { + size = sprintf(buf,"bad read. Try again\n"); + } + else + { + size = sprintf(buf,"%0x ",rbuf[Z_CONFIG_OFFSET+1]); + size += sprintf((buf+size),"%0x\n",rbuf[Z_CONFIG_OFFSET]); + } + return size; +} + +static ssize_t show_zbinfo(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net = to_net_dev(dev); + struct net_zb *priv = netdev_priv(net); + struct bmi_zb *zb = priv->zb; + struct zaccel_device *z_dev = &zb->z_info.device; + int size = 0; + + zaccel_getdev(zb); + + switch(z_dev->state) + { + case DEV_HOLD: + size = sprintf(buf,"DEV_HOLD\n"); + break; + + case DEV_INIT: + size = sprintf(buf,"DEV_INIT\n"); + break; + + case DEV_NWK_DISC: + size = sprintf(buf,"DEV_NWK_DISC\n"); + break; + + case DEV_NWK_JOINING: + size = sprintf(buf,"DEV_NWK_JOINING\n"); + break; + + case DEV_NWK_REJOIN: + size = sprintf(buf,"DEV_NWK_REJOIN\n"); + break; + + case DEV_END_DEVICE_UNAUTH: + size = sprintf(buf,"DEV_END_DEVICE_UNAUTH\n"); + break; + + case DEV_END_DEVICE: + size = sprintf(buf,"DEV_END_DEVICE\n"); + break; + + case DEV_ROUTER: + size = sprintf(buf,"DEV_ROUTER\n"); + break; + + case DEV_COORD_STARTING: + size = sprintf(buf,"DEV_COORD_STARTING\n"); + break; + + case DEV_ZB_COORD: + size = sprintf(buf,"DEV_ZB_COORD\n"); + break; + + case DEV_NWK_ORPHAN: + size = sprintf(buf,"DEV_NWK_ORHAN\n"); + break; + + default: + size = sprintf(buf,"UNKNOWN STATE %d\n",z_dev->state); + break; + } + + size += sprintf((buf+size),"device IEEE address: %x:%x:%x:%x:%x:%x:%x:%x\n", + z_dev->device_ieee[7],z_dev->device_ieee[6],z_dev->device_ieee[5], + z_dev->device_ieee[4],z_dev->device_ieee[3],z_dev->device_ieee[2], + z_dev->device_ieee[1],z_dev->device_ieee[0]); + size += sprintf((buf+size),"device short address: 0x%x %x\n", + z_dev->device_short[1],z_dev->device_short[0]); + size += sprintf((buf+size),"parent short address: 0x%x %x\n", + z_dev->parent_short[1],z_dev->parent_short[0]); + size += sprintf((buf+size),"parent IEEE address: %x:%x:%x:%x:%x:%x:%x:%x\n", + z_dev->parent_ieee[7],z_dev->parent_ieee[6],z_dev->parent_ieee[5], + z_dev->parent_ieee[4],z_dev->parent_ieee[3],z_dev->parent_ieee[2], + z_dev->parent_ieee[1],z_dev->parent_ieee[0]); + size += sprintf((buf+size),"channel: 0x%0x\n",z_dev->channel); + size += sprintf((buf+size),"PAN ID: 0x%0x %0x\n", + z_dev->panid[1],z_dev->panid[0]); + size += sprintf((buf+size),"extended PAN ID: %x:%x:%x:%x:%x:%x:%x:%x\n", + z_dev->ext_panid[7],z_dev->ext_panid[6],z_dev->ext_panid[5], + z_dev->ext_panid[4],z_dev->ext_panid[3],z_dev->ext_panid[2], + z_dev->ext_panid[1],z_dev->ext_panid[0]); + return size; +} + + +DEVICE_ATTR(device, (S_IRUGO | S_IWUSR), show_device, store_device); +DEVICE_ATTR(panid, (S_IRUGO | S_IWUSR), show_panid, store_panid); +DEVICE_ATTR(chanlist, (S_IRUGO | S_IWUSR), show_chanlist, store_chanlist); +DEVICE_ATTR(initop, (S_IRUGO | S_IWUSR), show_initop, store_initop); +DEVICE_ATTR(zbinfo, S_IRUGO, show_zbinfo, NULL); + +void zb_create_sysfs(struct net_device *net) +{ + struct device *dev = &(net->dev); + + if(device_create_file(dev,&dev_attr_device) < 0) + printk(KERN_WARNING "zb: failed to create device attribute\n"); + + if(device_create_file(dev,&dev_attr_panid) < 0) + printk(KERN_WARNING "zb: failed to create panid attribute\n"); + + if(device_create_file(dev,&dev_attr_chanlist) < 0) + printk(KERN_WARNING "zb: failed to create chanlist attribute\n"); + + if(device_create_file(dev,&dev_attr_initop) < 0) + printk(KERN_WARNING "zb: failed to create initop attribute\n"); + + if(device_create_file(dev,&dev_attr_zbinfo) < 0) + printk(KERN_WARNING "zb: failed to create zinfo attribute\n"); +} + +void zb_remove_sysfs(struct net_device *net) +{ + struct device *dev = &(net->dev); + + device_remove_file(dev,&dev_attr_device); + device_remove_file(dev,&dev_attr_panid); + device_remove_file(dev,&dev_attr_chanlist); + device_remove_file(dev,&dev_attr_initop); + device_remove_file(dev,&dev_attr_zbinfo); +} + --- /dev/null +++ git/drivers/bmi/pims/zb/bmi_zprotocol.c @@ -0,0 +1,619 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "bmi_zigbee.h" + +HLIST_HEAD(zaccel_list); +DEFINE_RWLOCK(zaccel_list_lock); + +struct zaccel_sock { + struct sock sk; + struct packet_type zpacket_type; + spinlock_t bind_lock; + struct sockaddr_zb sockaddr; + struct net_device *dev; + +}; + +static inline struct zaccel_sock *z_sk(struct sock *sk) +{ + return (struct zaccel_sock *)sk; +} + +static void zaccel_sock_destruct(struct sock *sk) +{ + if(!sock_flag(sk,SOCK_DEAD)) { + printk("Attempt to release alive packet socket: %p\n",sk); + return; + } + + sk_refcnt_debug_dec(sk); +} + +static int z_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) +{ + struct sock *sk; + struct zaccel_sock *zsock; + struct sockaddr_zb *zaddr; + + printk("z_packet_getname\n"); + zaddr = (struct sockaddr_zb *)uaddr; + + sk = sock->sk; + zsock = z_sk(sk); + + if(zsock->dev == 0) + { + + printk(KERN_WARNING "sock not bound \n"); + /* the socket is not bound */ + return -ENODATA; + } + + zaddr->z_family = AF_ZACCEL; + zaddr->z_ifindex = zsock->sockaddr.z_ifindex; + zaddr->z_protocol = zsock->sockaddr.z_protocol; + memcpy(&zaddr->z_name, &zsock->sockaddr.z_name, 15); + + *uaddr_len = sizeof(struct sockaddr_zb); + return 0; + +} + +static struct proto zaccel_proto = { + .name = "Z_PACKET", + .owner = THIS_MODULE, + .obj_size = sizeof(struct zaccel_sock), +}; + +static int z_control_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +{ + struct sock *sk; + + sk = pt->af_packet_priv; + + /* put packet in receive queue */ + if(sock_queue_rcv_skb(sk,skb) == 0) + { + return 0; + } + + printk(KERN_WARNING "z_control_rcv drop\n"); + kfree_skb(skb); + return 0; + +} + +static int z_packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +{ + struct sock *sk; + + sk = pt->af_packet_priv; + + /* + * put the packet in the receive queue. + */ + + if(sock_queue_rcv_skb(sk,skb) == 0) + { + return 0; + } + + printk(KERN_WARNING "z_packet_rcv drop\n"); + kfree_skb(skb); + return 0; +} + +/* + * Pull a packet from our recieve queue and hand it to the user. + */ + +static int z_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk; + struct sk_buff *skb; + int skb_len; + struct net_device *dev; + struct zaccel_sock *zsock; + int ifindex; + int err; + + err = -EINVAL; + if(flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT)) + { + goto out; + } + + sk = sock->sk; + + zsock = z_sk(sk); + + ifindex = zsock->sockaddr.z_ifindex; + + dev = dev_get_by_index(&init_net, ifindex); + if(dev == NULL) + { + printk(KERN_WARNING "bmi_zprotocol: dev not found\n"); + return (-ENXIO); + } + + if(!(dev->flags & IFF_UP)) + { + printk("interface not up %d\n",(-ENETDOWN)); + dev_put(dev); + return -ENETDOWN; + } + + dev_put(dev); + + /* + * Get a datagram skbuff + */ + skb = skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err); + + if(skb == NULL) + { + goto out; + } + + /* + * If the input buffer is smaller than the message, truncate + * it. The user loses any data beyond it. + */ + skb_len = skb->len; + if(skb_len > len) + { + skb_len = len; + msg->msg_flags |= MSG_TRUNC; + } + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, skb_len); + if(err) + goto out_free; + + sock_recv_timestamp(msg, sk, skb); + + /* err returns data length if the copy is successful */ + err = skb_len; + +out_free: + skb_free_datagram(sk,skb); + +out: + return err; +} + +static int z_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct sk_buff *skb; + struct net_device *dev; + struct zaccel_sock *zsock; + int ifindex; + int err; + + zsock = z_sk(sk); + + if(zsock->zpacket_type.type == Z_CONTROL_SOCK) + { + return -EPROTOTYPE; + } + + ifindex = zsock->sockaddr.z_ifindex; + + dev = dev_get_by_index(&init_net, ifindex); + if(dev == NULL) + { + printk(KERN_WARNING "bmi_zprotocol: dev not found\n"); + dev_put(dev); + return (-ENXIO); + } + + if(!(dev->flags & IFF_UP)) + { + printk(KERN_WARNING "bmi_zprotocol: interface not up %d\n",(-ENETDOWN)); + dev_put(dev); + return -ENETDOWN; + } + + if(len > 92) + { + /* message is too long */ + dev_put(dev); + return -EINVAL; + } + + skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); + if(skb == NULL) + { + dev_put(dev); + printk(KERN_WARNING "bmi_zprotocol: sock_allock_send_skb failed %d\n",err); + return -ENOMEM; + } + + err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + if(err) + { + kfree_skb(skb); + dev_put(dev); + return -EFAULT; + } + + skb->dev = dev; + + /* + * dev_queue_xmit sends the packet directly to the driver. + */ + + err = dev_queue_xmit(skb); + if (err > 0) + { + kfree_skb(skb); + printk(KERN_WARNING "bmi_zprotocol: dev_queue_xmit failed %d\n",err); + dev_put(dev); + return(-ENETDOWN); + } + + dev_put(dev); + return(len); +} + + +static int z_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sock *sk = sock->sk; + char name[15]; + struct net_device *dev; + struct sockaddr_zb *z_addr; + struct zaccel_sock *zsock; + struct net_zb *priv; + unsigned char type; + int rc; + + z_addr = (struct sockaddr_zb *)uaddr; + + /* Get the name of the device */ + strlcpy(name,z_addr->z_name,sizeof(name)); + + rc = (int)strlen(name); + if(rc > 3) + { + return -EINVAL; + } + + if(memcmp(name,"zb",2)) + { + printk(KERN_WARNING "bmi_zprotocol: invalid name %s\n",name); + return -EINVAL; + } + + if((name[2] < 0x31) && (name[2] > 0x34)) + { + printk(KERN_WARNING "bmi_zprotocol: invalid slot %c\n",name[2]); + return -EINVAL; + } + + + zsock = z_sk(sk); + type = zsock->zpacket_type.type; + + /* search for the network interface by name */ + dev = dev_get_by_name(&init_net, name); + + /* + * check if the device has been bound to this socket type. + * If it has, return with error. + */ + + priv = netdev_priv(dev); + if(priv->socket[type] != Z_NO_SOCK) + { + dev_put(dev); + return -EISCONN; + } + + if(dev) + { + lock_sock(sk); + spin_lock(&zsock->bind_lock); + + if(zsock->dev && (zsock->dev != dev)) + { + /* This socket was bound. Unbind it first. */ + printk(KERN_INFO "unbound to previous device\n"); + __sock_put(sk); + + priv = netdev_priv(zsock->dev); + priv->socket[type] = Z_NO_SOCK; + + zsock->dev = NULL; + spin_unlock(&zsock->bind_lock); + dev_remove_pack(&zsock->zpacket_type); + spin_lock(&zsock->bind_lock); + } + + zsock->zpacket_type.dev = dev; + zsock->zpacket_type.af_packet_priv = sk; + + /* socket-dev information used by sendmsg */ + zsock->sockaddr.z_ifindex = dev->ifindex; + strlcpy(zsock->sockaddr.z_name,name,15); + + zsock->dev = dev; + + /* Add a packet handler to the networking stack. */ + dev_add_pack(&zsock->zpacket_type); + /* increment sk_refcnt */ + sock_hold(sk); + + /* Tell the device that it is bound to a socket */ + priv = netdev_priv(dev); + priv->socket[type] = 1; + + spin_unlock(&zsock->bind_lock); + release_sock(sk); + dev_put(dev); + return 0; + } + else + { + printk(KERN_WARNING "zb: dev %s not found\n",name); + return -ENODEV; + } +} + +static int z_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int rc; + + switch(cmd) + { + case SIOCGIFFLAGS: + case SIOCGIFNAME: + case SIOCGIFMTU: + case SIOCGIFINDEX: + case SIOCETHTOOL: + case SIOCSIFNAME: + case SIOCSIFFLAGS: + case SIOCSIFMTU: + /* + * return -ENOIOCTLCMD to sock_ioctl + * sock_ioctl will call dev_ioctl to take care of these cmds. + */ + rc = -ENOIOCTLCMD; + break; + default: + rc = 0; + break; + } + + return rc; +} + +static int z_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) +{ + struct sock *sk = sock->sk; + struct zaccel_sock *zsock; + unsigned char buf[135]; /* 128 data + 6 fixed header + 1 just in case */ + + zsock = z_sk(sk); + + if(!zsock->dev) + { + printk(KERN_WARNING "bmi_zprotocol: device not attached\n"); + /* + * socket has no device attached + */ + return -ENOTCONN; + } + + if(level != SOL_ZACCEL) + { + return -ENOPROTOOPT; + } + + if(optlen > 135) + { + return -EINVAL; + } + + if(copy_from_user(buf,optval,optlen)) + { + return -EFAULT; + } + + if(zdev_setopt(zsock->dev,optname,optlen,buf)) + { + return -EINVAL; + } + + return 0; +} + +static int z_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) + +{ + struct sock *sk = sock->sk; + struct zaccel_sock *zsock; + unsigned char buf[135]; /* 128 data + 6 fixed header + 1 just in case */ + int len; + int rc; + + zsock = z_sk(sk); + + if(!zsock->dev) + { + /* socket has no device attached */ + return -ENOTCONN; + } + + if(level != SOL_ZACCEL) + return -ENOPROTOOPT; + + rc = zdev_getopt(zsock->dev,optname,&len,buf); + + if(!rc) + { + if(put_user(len, optlen)) + return -EFAULT; + /* Include 3 header bytes to the message */ + if(copy_to_user(optval, buf, (len+3))) + return -EFAULT; + } + + return rc; + +} + +static int z_release(struct socket *sock) +{ + struct sock *sk; + struct zaccel_sock *zsock; + struct net_zb *priv; + + sk = sock->sk; + + if(!sk) + return 0; + + zsock = z_sk(sk); + + write_lock_bh(&zaccel_list_lock); + sk_del_node_init(sk); + write_unlock_bh(&zaccel_list_lock); + + if(zsock->dev) + { + priv = netdev_priv(zsock->dev); + priv->socket[zsock->zpacket_type.type] = Z_NO_SOCK; + + /* remove protocol handler */ + dev_remove_pack(&zsock->zpacket_type); + __sock_put(sk); + } + + /* detach socket from process context */ + sock_orphan(sk); + sock->sk = NULL; + + /* Purge queues */ + skb_queue_purge(&sk->sk_receive_queue); + + sk_refcnt_debug_release(sk); + + sock_put(sk); + return 0; +} + +static const struct proto_ops z_protocol_ops = { + .family = PF_ZACCEL, + .owner = THIS_MODULE, + .release = z_release, + .bind = z_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = z_getname, + .poll = sock_no_poll, + .ioctl = z_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = z_setsockopt, + .getsockopt = z_getsockopt, + .sendmsg = z_sendmsg, + .recvmsg = z_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +int z_protocol_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + struct zaccel_sock *zsock; + + if (net != &init_net) + { + return -EAFNOSUPPORT; + } + + sock->state = SS_UNCONNECTED; + if(sock->type != SOCK_RAW) + { + return -ESOCKTNOSUPPORT; + } + + sock->ops = &z_protocol_ops; + + sk = sk_alloc(net, AF_ZACCEL, GFP_KERNEL, &zaccel_proto); + if(sk == NULL) + { + return -ENOMEM; + } + + sock_init_data(sock, sk); + sk->sk_protocol = protocol; + sk->sk_family = PF_ZACCEL; + sk->sk_destruct = zaccel_sock_destruct; + sk_refcnt_debug_inc(sk); + + zsock = z_sk(sk); + + spin_lock_init(&zsock->bind_lock); + + if(protocol == Z_CONTROL_SOCK) + { + zsock->zpacket_type.func = z_control_rcv; + zsock->zpacket_type.type = Z_CONTROL_SOCK; + sk->sk_protocol = Z_CONTROL_SOCK; + } + else + { + zsock->zpacket_type.func = z_packet_rcv; + zsock->zpacket_type.type = Z_PACKET_SOCK; + sk->sk_protocol = Z_PACKET_SOCK; + } + zsock->zpacket_type.af_packet_priv = sk; + zsock->zpacket_type.dev = NULL; + + zsock->dev = NULL; + + /* Add a socket to the bound sockets list */ + write_lock_bh(&zaccel_list_lock); + sk_add_node(sk,&zaccel_list); + write_unlock_bh(&zaccel_list_lock); + + return 0; + +} + +static struct net_proto_family zaccel_family_ops = { + .family = PF_ZACCEL, + .create = z_protocol_create, + .owner = THIS_MODULE, +}; + +void z_sock_exit(void) +{ + sock_unregister(PF_ZACCEL); +} + +int z_sock_init(void) +{ + int res; + + res = sock_register(&zaccel_family_ops); + if(res) { + printk(KERN_WARNING "Failed to register PF_ZACCEL\n"); + } + + return res; +} --- /dev/null +++ git/drivers/bmi/slots/Kconfig @@ -0,0 +1,21 @@ +# +# BMI Slot Drivers +# + +menu "BMI Hardware Slot support" + +config BUG_SLOT + tristate "Buglabs BUGBase BMI Slots" + default 'n' + depends on BMI && MACH_BUG + help + If you say yes to this option, support will be included for the Buglabs BUGBase BMI Slot/Module Ports. + +config OMAP_SLOT + tristate "TI BeagBoard BMI Slots" + default 'n' + depends on BMI + help + If you say yes to this option, support will be included for the BeagleBoard Slot. + +endmenu --- /dev/null +++ git/drivers/bmi/slots/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for BMI Slot drivers +# + +obj-$(CONFIG_BUG_SLOT) += slots_bug.o +obj-$(CONFIG_OMAP_SLOT) += slots_beagle.o --- /dev/null +++ git/drivers/bmi/slots/slots_beagle.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BMI_GPIO_0 139 +#define BMI_GPIO_1 158 +#define BMI_GPIO_2 137 +#define BMI_GPIO_3 136 + +static int bl_present(struct bmi_slot* slot) +{ + unsigned gpio = irq_to_gpio(slot->present_irq); + if (gpio_get_value(gpio)) + return 0; + else + return 1; +} + +static void bl_power_on(struct bmi_slot* slot) +{ + return; +} + +static void bl_power_off(struct bmi_slot* slot) +{ + return; +} + +static void bl_gpio_config(struct bmi_slot* slot, int mask) /*Configure gpios as inputs/ouputs*/ +{ + int i; + + unsigned char *gpio = (unsigned char*) slot->slot_data; + + for (i = 0; i < 4 ; i++) + { + if ((mask >> i) & 0x1) + gpio_direction_output(gpio[i], 0); + else + gpio_direction_input(gpio[i]); + } + return; +} + +static int bl_gpio_get(struct bmi_slot* slot) +{ + int i; + unsigned char *gpio = (unsigned char*) slot->slot_data; + unsigned char ret = 0; + + for (i = 3; i > -1 ; i--) + { + ret = (ret << 1) | gpio_get_value(gpio[i]); + } + + return ret; +} + +static void bl_gpio_set(struct bmi_slot* slot, int mask) +{ + int i; + unsigned char *gpio = (unsigned char*) slot->slot_data; + + for (i = 0; i < 4 ; i++) + { + if ((mask >> i) & 0x1) + gpio_set_value(gpio[i], 1); + else + gpio_set_value(gpio[i], 0); + } + return; +} + +static void bl_uart_enable(struct bmi_slot* slot) +{ + return; +} + +static void bl_uart_disable(struct bmi_slot* slot) +{ + return; +} + +static void bl_spi_enable(struct bmi_slot* slot) +{ + return; +} + +static void bl_spi_disable(struct bmi_slot* slot) +{ + return; +} + +static void bl_audio_enable(struct bmi_slot* slot) +{ + return; +} + +static void bl_audio_disable(struct bmi_slot* slot) +{ + return; +} + +static void bl_batt_enable(struct bmi_slot* slot) +{ + return; +} + +static void bl_batt_disable(struct bmi_slot* slot) +{ + return; +} + + +struct slot_actions bl_actions = { + .present = bl_present, + .power_on = bl_power_on, + .power_off = bl_power_off, + .gpio_config = bl_gpio_config, + .gpio_get = bl_gpio_get, + .gpio_set = bl_gpio_set, + .uart_enable = bl_uart_enable, + .uart_disable = bl_uart_disable, + .spi_enable = bl_spi_enable, + .spi_disable = bl_spi_disable, + .audio_enable = bl_audio_enable, + .audio_disable = bl_audio_disable, + .batt_enable = bl_batt_enable, + .batt_disable = bl_batt_disable, +}; + +static int omapbmi_slot_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int omapbmi_slot_resume(struct platform_device *pdev) +{ + return 0; +} + +static int omapbmi_slot_probe(struct platform_device *pdev) +{ + struct bmi_slot *slot; + struct resource *irq_pres, *irq_stat; + // struct omap_bmi_platform_data *bmi_plat_data = pdev->dev.platform_data; + int ret = 0; + unsigned char* gpio; + + printk(KERN_INFO "Buglabs BeagleBUG Slots Driver...\n"); + irq_pres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq_pres) { + dev_err(&pdev->dev, "No presence irq resource...\n"); + return -ENODEV; + } + irq_stat = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!irq_stat) { + dev_err(&pdev->dev, "No status irq resource...\n"); + return -ENODEV; + } + + slot = kzalloc(sizeof(struct bmi_slot), GFP_KERNEL); + if (!slot) { + ret = -ENOMEM; + goto err_release; + } + + ret = gpio_request(irq_stat->start, "BMI SINT"); + if (ret) { + printk(KERN_ERR "slots_beagle: GPIO %d request failed...\n",irq_stat->start); + goto err_release; + } + ret = gpio_request(irq_pres->start, "BMI PINT"); + if (ret) { + printk(KERN_ERR "slots_beagle: GPIO %d request failed...\n",irq_pres->start); + goto err_release; + } + + ret = gpio_direction_input(irq_pres->start); + + gpio = kmalloc(4, GFP_KERNEL); + gpio_request(139,"BMI_0"); + gpio_request(158,"BMI_1"); + gpio_request(137,"BMI_2"); + gpio_request(136,"BMI_3"); + + gpio[0] = 139; + gpio[1] = 158; + gpio[2] = 137; + gpio[3] = 136; + + slot->slot_data = (void*)gpio; + slot->present_irq = gpio_to_irq(irq_pres->start); + slot->status_irq = gpio_to_irq(irq_stat->start); + slot->owner = THIS_MODULE; + slot->name = "omap_bug_slot"; + slot->slotdev.parent = &pdev->dev; + slot->adap = i2c_get_adapter(3); + slot->actions = &bl_actions; + slot->spi_bus_num = 3; + slot->spi_cs = 0; + + + ret = bmi_add_slot(slot); + if (ret) { + printk(KERN_ERR "slots_beagle: Trouble instantiating slot...%d\n", ret); + goto err_release; + } + return 0; + err_release: + kfree(slot->slot_data); + kfree(slot); + return ret; +} + +static int omapbmi_slot_remove(struct platform_device *pdev) +{ + struct bmi_slot *slot = platform_get_drvdata(pdev); + //int id = pdev->id; + + bmi_del_slot(slot); + platform_set_drvdata(pdev, NULL); + kfree(slot->slot_data); + kfree(slot); + return 0; +} + + +static struct platform_driver omapbmi_slot_driver = { + .driver = { + .name = "omap_bmi_slot", + .owner = THIS_MODULE, + }, + .probe = omapbmi_slot_probe, + .remove = omapbmi_slot_remove, + .suspend = omapbmi_slot_suspend, + .resume = omapbmi_slot_resume, +}; + +static int __init omap_bmi_slot_init(void) +{ + /* Register the device driver structure. */ + return platform_driver_register(&omapbmi_slot_driver); +} + +/*! + * This function is used to cleanup all resources before the driver exits. + */ +static void __exit omap_bmi_slot_exit(void) +{ + platform_driver_unregister(&omapbmi_slot_driver); +} + +module_init(omap_bmi_slot_init); +module_exit(omap_bmi_slot_exit); + +MODULE_AUTHOR("Matt Isaacs"); +MODULE_DESCRIPTION("OMAP BMI Slot Driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/drivers/bmi/slots/slots_bug.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include + +#include +//#include +#include + +static int bl_present(struct bmi_slot* slot) +{ + int status; + + status = cpld_read_module_present_status(slot->slotnum); + if (status & 0x04) + return 1; + else + return 0; +} + +static void bl_power_on(struct bmi_slot* slot) +{ + //gpio_power_on_slot (slot->slotnum); + return; +} + +static void bl_power_off(struct bmi_slot* slot) +{ + //gpio_power_off_slot (slot->slotnum); + return; +} + +static void bl_gpio_config(struct bmi_slot* slot, int mask) /*Configure gpios as inputs/ouputs*/ +{ + int i; + for (i = 0; i < 4; i++) { + cpld_set_module_gpio_dir(slot->slotnum, i, (mask & 0x1)); + mask = mask >> 1; + } + return; +} + +static int bl_gpio_get(struct bmi_slot* slot) +{ + return cpld_read_gpio_data_reg(slot->slotnum); +} + +static void bl_gpio_set(struct bmi_slot* slot, int mask) +{ + int i; + for (i = 0; i < 4; i++) { + cpld_set_module_gpio_data(slot->slotnum, i, (mask & 0x1)); + mask = mask >> 1; + } + return; +} + +static void bl_uart_enable(struct bmi_slot* slot) +{ + cpld_uart_active(slot->slotnum); + return; +} + +static void bl_uart_disable(struct bmi_slot* slot) +{ + cpld_uart_inactive(slot->slotnum); + return; +} + +static void bl_spi_enable(struct bmi_slot* slot) +{ + //REVIST: + cpld_spi_active(0); + return; +} + +static void bl_spi_disable(struct bmi_slot* slot) +{ + //REVIST: + cpld_spi_inactive(0); + return; +} + +static void bl_audio_enable(struct bmi_slot* slot) +{ + cpld_activate_audio_ports(); + return; +} + +static void bl_audio_disable(struct bmi_slot* slot) +{ + cpld_inactivate_audio_ports(); + return; +} + +static void bl_batt_enable(struct bmi_slot* slot) +{ + cpld_set_module_battery_enable(slot->slotnum); + return; +} + +static void bl_batt_disable(struct bmi_slot* slot) +{ + cpld_set_module_battery_disable(slot->slotnum); + return; +} + +/* +static int mxcbmi_probe(struct platform_device *pdev); +static int mxcbmi_slot_remove(struct platform_device *pdev); +static int mxcbmi_suspend(struct platform_device *pdev, pm_message_t state); +static int mxcbmi_resume(struct platform_device *pdev); +*/ + +struct slot_actions bl_actions = { + .present = bl_present, + .power_on = bl_power_on, + .power_off = bl_power_off, + .gpio_config = bl_gpio_config, + .gpio_get = bl_gpio_get, + .gpio_set = bl_gpio_set, + .uart_enable = bl_uart_enable, + .uart_disable = bl_uart_disable, + .spi_enable = bl_spi_enable, + .spi_disable = bl_spi_disable, + .audio_enable = bl_audio_enable, + .audio_disable = bl_audio_disable, + .batt_enable = bl_batt_enable, + .batt_disable = bl_batt_disable, +}; + +static int mxcbmi_slot_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int mxcbmi_slot_resume(struct platform_device *pdev) +{ + return 0; +} + +static int mxcbmi_slot_probe(struct platform_device *pdev) +{ + struct bmi_slot *slot; + struct resource *res, *irq_pres, *irq_stat; + struct mxc_bmi_platform_data *bmi_plat_data = pdev->dev.platform_data; + int ret = 0; + + printk(KERN_INFO "Buglabs BUGBase Slots Driver...\n"); + irq_pres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq_pres) { + dev_err(&pdev->dev, "No presence irq resource...\n"); + return -ENODEV; + } + irq_stat = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!irq_stat) { + dev_err(&pdev->dev, "No status irq resource...\n"); + return -ENODEV; + } + + slot = kzalloc(sizeof(struct bmi_slot), GFP_KERNEL); + if (!slot) { + ret = -ENOMEM; + goto err_release; + } + + + slot->present_irq = irq_pres->start; + slot->status_irq = irq_stat->start; + slot->owner = THIS_MODULE; + slot->name = "mxc_bug_slot"; + slot->slotdev.parent = &pdev->dev; + slot->adap = i2c_get_adapter(2 + pdev->id); + slot->actions = &bl_actions; + slot->spi_bus_num = 1; + slot->spi_cs = pdev->id; + ret = bmi_add_slot(slot); + if (ret) { + printk(KERN_ERR "slots_bug: Trouble instantiating slot...%d\n", ret); + goto err_release; + } + ret = 0; + err_release: + return ret; +} + +static int mxcbmi_slot_remove(struct platform_device *pdev) +{ + struct bmi_slot *slot = platform_get_drvdata(pdev); + //int id = pdev->id; + + bmi_del_slot(slot); + platform_set_drvdata(pdev, NULL); + kfree(slot); + return 0; +} + + +static struct platform_driver mxcbmi_slot_driver = { + .driver = { + .name = "mxc_bmi_slot", + .owner = THIS_MODULE, + }, + .probe = mxcbmi_slot_probe, + .remove = mxcbmi_slot_remove, + .suspend = mxcbmi_slot_suspend, + .resume = mxcbmi_slot_resume, +}; + +static int __init mxc_bmi_slot_init(void) +{ + /* Register the device driver structure. */ + return platform_driver_register(&mxcbmi_slot_driver); +} + +/*! + * This function is used to cleanup all resources before the driver exits. + */ +static void __exit mxc_bmi_slot_exit(void) +{ + platform_driver_unregister(&mxcbmi_slot_driver); +} + +module_init(mxc_bmi_slot_init); +module_exit(mxc_bmi_slot_exit); + +MODULE_AUTHOR("Matt Isaacs"); +MODULE_DESCRIPTION("MXC BMI Slot Driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ git/include/linux/bmi-ids.h @@ -0,0 +1,30 @@ +/* + * BMI Vendor and Product IDs + * + * Please keep sorted. + */ + +/* BMI vendors */ + +#define BMI_VENDOR_ILLEGAL_0 0x0 +#define BMI_VENDOR_BUG_LABS 0x1 +#define BMI_VENDOR_ILLEGAL_F 0xFFFF + + +/* BMI products */ + +#define BMI_PRODUCT_ILLEGAL_0 0x0000 +#define BMI_PRODUCT_GPS_J32 0x0001 +#define BMI_PRODUCT_MOT_ACCEL 0x0002 +#define BMI_PRODUCT_LCD_SHARP_320X240 0x0003 +#define BMI_PRODUCT_CAMERA_VS6624 0x0004 +#define BMI_PRODUCT_CAMERA_OV2640 0x0005 +#define BMI_PRODUCT_FACTORY_TEST 0x0006 +#define BMI_PRODUCT_VON_HIPPEL 0x0007 +#define BMI_PRODUCT_WIFI 0x0008 +#define BMI_PRODUCT_ZIGBEE 0x0009 +#define BMI_PRODUCT_AUDIO 0x000A +#define BMI_PRODUCT_GSM 0x000B +#define BMI_PRODUCT_PROJECTOR 0x000C +#define BMI_PRODUCT_ILLEGAL_F 0xFFFF + --- /dev/null +++ git/include/linux/bmi.h @@ -0,0 +1,142 @@ +#ifndef __LINUX_BMI_H +#define __LINUX_BMI_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* BMI bus device table constants */ +#define BMI_ANY 0x0 + +#define RED_LED 8 +#define GREEN_LED 4 +#define GPIO_1 2 +#define GPIO_0 1 + +struct bmi_slot; + +struct slot_actions { + int (*present)(struct bmi_slot*); + void (*power_on)(struct bmi_slot*); + void (*power_off)(struct bmi_slot*); + void (*gpio_config)(struct bmi_slot*, int mask); /*Configure gpios as inputs/ouputs*/ + int (*gpio_get)(struct bmi_slot*); + void (*gpio_set)(struct bmi_slot*, int mask); + void (*uart_enable)(struct bmi_slot*); + void (*uart_disable)(struct bmi_slot*); + void (*spi_enable)(struct bmi_slot*); + void (*spi_disable)(struct bmi_slot*); + void (*audio_enable)(struct bmi_slot*); + void (*audio_disable)(struct bmi_slot*); + void (*batt_enable)(struct bmi_slot*); + void (*batt_disable)(struct bmi_slot*); +}; + +struct bmi_slot { + int slotnum; + char* name; + struct bmi_device *bdev; + struct module *owner; + struct device slotdev; + struct kref kref; + struct mutex pres_mutex; + struct list_head event_list; + unsigned int event_bits[1]; + + int present; + struct i2c_adapter *adap; + struct i2c_client *eeprom; + + + // struct spi_device spi; + int spi_bus_num; + int spi_cs; + + int present_irq; + int status_irq; + struct delayed_work work; + struct slot_actions* actions; + + void* slot_data; + +}; + + +/* BMI Device */ + +struct bmi_device { + int devnum; + + struct device dev; + + struct mutex lock; + + + int present_irq_cnt; + int state; /* Make this an enum */ + + struct bmi_slot *slot; + + struct bmi_eeprom_data ident; + unsigned short vendor; + unsigned short product; + unsigned short revision; + + struct bmi_driver *driver; /* which driver has allocated this device */ + +}; + +#define to_bmi_device(n) container_of(n, struct bmi_device, dev); +#define work_to_slot(w) container_of(container_of(w, \ + struct delayed_work, \ + work), \ + struct bmi_slot, \ + work) + + +static inline void *bmi_device_get_drvdata (struct bmi_device *bdev) +{ + return dev_get_drvdata (&bdev->dev); +} + +static inline void bmi_device_set_drvdata (struct bmi_device *bdev, void *data) +{ + dev_set_drvdata(&bdev->dev, data); +} + + +/* BMI Driver */ + +struct bmi_driver { + + char *name; + struct bmi_device_id *id_table; + struct device_driver driver; + int (*probe)(struct bmi_device *dev); + void (*remove)(struct bmi_device *dev); +}; + +extern struct bus_type bmi_bus_type; + +#define to_bmi_driver(drv) container_of(drv,struct bmi_driver, driver) + +int __must_check __bmi_register_driver(struct bmi_driver *, struct module *); +static inline int __must_check bmi_register_driver(struct bmi_driver *driver) +{ + return __bmi_register_driver(driver, THIS_MODULE); +} + +void bmi_unregister_driver(struct bmi_driver *drv); + +struct bmi_device *bmi_alloc_dev(struct bmi_slot *slot); +struct class* bmi_get_class (void); +int bmi_add_slot(struct bmi_slot *slot); +int bmi_del_slot(struct bmi_slot *slot); + +#endif --- /dev/null +++ git/include/linux/bmi/at24c02.h @@ -0,0 +1,26 @@ +#include +#include + +/*-------------------------------- + * + * AT24C02 I2C Eeprom Device + * + *-------------------------------- + */ + + +struct at24c02 { + + unsigned char addr; + struct i2c_adapter *adap; +}; + +void at24c02_init (struct at24c02 *dev, u8 addr, struct i2c_adapter *adap); + +int at24c02_read_byte ( struct at24c02 *dev, u8 offset, u8 *data); +int at24c02_write_byte ( struct at24c02 *dev, u8 offset, u8 data); +int at24c02_read ( struct at24c02 *dev, u8 offset, u8 *data, int size); +int at24c02_write_page ( struct at24c02 *dev, u8 offset, u8 *data, int size); + + + --- /dev/null +++ git/include/linux/bmi/bmi-bus.h @@ -0,0 +1,21 @@ +#ifndef BMI_BUS_H +#define BMI_BUS_H + +#include + +#define BMI_MAX_SLOTS 1 + +struct bmi_bus { + + struct bmi_device slot[BMI_MAX_SLOTS]; +}; + + + +extern struct bus_type bmi_bus_type; + +struct bmi_device* bmi_get_bmi_device (int slot_num); + + +#endif /* BMI_BUS_H */ + --- /dev/null +++ git/include/linux/bmi/bmi-control.h @@ -0,0 +1,303 @@ +/* + * Copyright 2007 EnCADIS Design, Inc. All Rights Reserved. + * Copyright 2007 Bug-Labs, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef _BMI_CONTROL_H +#define _BMI_CONTROL_H + +#define BMI_M1 (0x0) +#define BMI_M2 (0x1) +#define BMI_M3 (0x2) +#define BMI_M4 (0x3) +#define BMI_GPIO_IN (0x0) +#define BMI_GPIO_OUT (0x1) +#define BMI_GPIO_ON (0x1) +#define BMI_GPIO_OFF (0x0) + +/*! + * This function configures the UART function for the IOMUX pins. + * + * @param port a UART port number (0-5) + * @param no_irda configure UART port for IRDA + */ +void bmi_gpio_uart_active(int port, int no_irda); + +/*! + * This function configures the UART function in the BMI. + * + * @param port a UART port number (0-5) + */ +void bmi_uart_active(int port); + +/*! + * This function configures the UART function in the BMI. + * + * @param port a UART port number (0-5) + */ +void bmi_uart_inactive(int port); + +/*! + * Setup GPIO for a CSPI device to be active + * + * @param cspi_mod an CSPI device + */ +void bmi_gpio_spi_active(int cspi_mod); + +/*! + * Setup BMI for a CSPI device to be active + * + * @param cspi_mod an CSPI device + */ +void bmi_spi_active(int cspi_mod); + +/*! + * Setup BMI for a CSPI device to be inactive + * + * @param cspi_mod an CSPI device + */ +void bmi_spi_inactive(int cspi_mod); + +/*! + * Setup GPIO for an I2C device to be active + * + * @param i2c_num an I2C device + */ +void bmi_gpio_i2c_active(int i2c_num); + +/*! + * Setup BMI for an I2C device to be active + */ +void bmi_i2c_active(void); + +/*! + * Setup BMI for an I2C device to be inactive + */ +void bmi_i2c_inactive(void); + +/* + * Setup GPIO for an I2S device to be active + */ +void bmi_gpio_activate_audio_ports(void); + +/*! + * Setup CPLD for an I2S device to be active + */ +void bmi_activate_audio_ports(void); + +/*! + * Setup CPLD for an I2S device to be inactive + */ +void bmi_inactivate_audio_ports(void); + +/*! + * Setup GPIO for the plug-in module LCD interface to be active + */ +void bmi_gpio_lcd_active(void); + +/*! + * Setup BMI for plug-in module LCD to be active + * + * @param port LCD serializer (0 or 1) + * @param pllc LCD serializer PLL divisor (0-7) + * @param mode LCD serializer bus mode (LCD_MODE_I80 or LCD_MODE_M68) + * + */ +void bmi_lcd_active(int port, int pllc, int mode); + +/*! + * Setup BMI for plug-in module LCD chip select to be active + * + * @param cs LCD chip select (LCD_MxCS x = 1,2,3,4) + * + */ +void bmi_lcd_cs_active(int cs); + +/*! + * Setup BMI for plug-in module LCD to be inactive + * + * @param port LCD serializer (0 or 1) + * + */ +void bmi_lcd_inactive(int port); + +/*! + * Setup BMI for plug-in module LCD chip select to be inactive + * + * @param cs LCD chip select (LCD_MxCS x = 1,2,3,4) + * + */ +void bmi_lcd_cs_inactive(int cs); + +/*! + * Setup pins for SLCD to be active + * + */ +void bmi_slcd_gpio_config(void); + +/*! + * Setup GPIO for sensor to be active + * + */ +void bmi_gpio_sensor_active(void); + +/*! + * Setup BMI for sensor to be active + * + * @param rclk_r pixclk edge (CAM_CLK_RISE or CAM_CLK_FALL) + */ +void bmi_sensor_active(int rclk_r); + +/*! + * Setup BMI for sensor to be inactive + */ +void bmi_sensor_inactive(void); + +/*! + * read BMI for sensor lock status + * + * @return camera serializer lock status (1 == locked) + */ +int bmi_sensor_lock_status(void); + +/* + * USB Host 2 GPIO config + * + * @return 0 + */ +int bmi_gpio_usbh2_active(void); + +/* + * USB Host 2 BMI config + * + * @param mtt - number of MTT's enabled in hub (USB_HUB_1_MTT or USB_HUB_4_MTT) + */ +void bmi_usbh2_active(int mtt); + +/* + * USB Host 2 BMI config inactive + */ +void bmi_usbh2_inactive(void); + +/* + * configure BMI Module GPIO direction + * + * @param module plug-in module (BMI, x= 1,2,3,4) + * @param bit GPIO bit (0-3) + * @param dir GPIO bit (BMI_GPIO_IN or BMI_GPIO_OUT) + */ +void bmi_set_module_gpio_dir(int module, int bit, int dir); + +/* + * read BMI GPIO Direction register + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @return module GPIO direction (4 LSB) + */ +int bmi_read_gpio_direction_reg(int module); + +/* + * set BMI Module GPIO data + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @param bit GPIO bit (0-3) + * @param value GPIO bit (0x0 or 0x1) + */ +void bmi_set_module_gpio_data(int module, int bit, int value); + +/* + * read BMI GPIO Data register + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @return module GPIO data (4 LSB) + */ +int bmi_read_gpio_data_reg(int module); + +/* + * set BMI Module battery enable + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + */ +void bmi_set_module_battery_enable(int module); + +/* + * set BMI Module battery disable + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + */ +void bmi_set_module_battery_disable(int module); + +/* + * read BMI module battery status + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @return state of module battery status bit + */ +int bmi_read_module_battery_status(int module); + +/* + * set BMI interrupt enable + * + * @param interrupt interrupt (INT_BUGRTC .. INT_M4_PRES) (defined in mx31bug.h) + */ +void bmi_interrupt_enable(int interrupt); + +/* + * set BMI interrupt disable + * + * @param interrupt interrupt (INT_BUGRTC .. INT_M4_PRES) (defined in mx31bug.h) + */ +void bmi_interrupt_disable(int interrupt); + +/* + * get BMI interrupt status + * + * @param interrupt interrupt (INT_BUGRTC .. INT_M4_PRES) (defined in mx31bug.h) + * @return 1 if set, 0 otherwise + */ +int bmi_interrupt_status(int interrupt); + +/* + * clear BMI module present interrupt bit + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + */ +void bmi_clear_module_present_interrupt(int module); + +/* + * enable I2C switches in BMI + */ +void bmi_i2c_sw_enable(void); + +/* + * disable I2C switches in BMI + */ +void bmi_i2c_sw_disable(void); + +/* + * read BMI module present status + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @return module present status (3 LSB = (OUT, IN, STATE_CHANGED)) + */ +int bmi_read_module_present_status(int module); + +/* + * BMI module present + * + * @param module plug-in module (BMI_Mx, x= 1,2,3,4) + * @return module present (1 = present, 0 = not present ) + */ +int bmi_module_present (struct bmi_device *bdev); + + +#endif // _BMI_CONTROL_H + --- /dev/null +++ git/include/linux/bmi/bmi-eeprom-data.h @@ -0,0 +1,83 @@ +#ifndef BMI_EEPROM_DATA_H +#define BMI_EEPROM_DATA_H + +#include + +union _bmi_vendor { + __u16 vendor; + __u8 vendor_msb; + __u8 vendor_lsb; +}; + +union _bmi_product { + __u16 product; + __u8 product_msb; + __u8 product_lsb; +}; + +union _bmi_revision { + __u16 revision; + __u8 revision_msb; + __u8 revision_lsb; +}; + +struct bmi_eeprom_raw +{ + __u8 format; /* byte 0x00 */ + __u8 vendor_msb; /* byte 0x01 */ + __u8 vendor_lsb; /* byte 0x02 */ + __u8 product_msb; /* byte 0x03 */ + __u8 product_lsb; /* byte 0x04 */ + __u8 revision_msb; /* byte 0x05 */ + __u8 revision_lsb; /* byte 0x06 */ + __u8 bus_usage; /* byte 0x07 */ + __u8 gpio_usage; /* byte 0x08 */ + __u8 power_use; /* byte 0x09 */ + __u8 power_charging; /* byte 0x0A */ + __u8 memory_size_msb; /* byte 0x0B */ + __u8 memory_size_lsb; /* byte 0x0C */ + __u8 serial_num_loc; /* byte 0x0D */ + __u8 serial_num_year; /* byte 0x0E */ + __u8 serial_num_week; /* byte 0x0F */ + __u8 serial_num_seq_msb; /* byte 0x10 */ + __u8 serial_num_seq_mid; /* byte 0x11 */ + __u8 serial_num_seq_lsb; /* byte 0x12 */ + __s8 description[108]; /* byte 0x13-0x7E */ + __u8 checksum; /* byte 0x7F */ +}; + + +#ifdef __KERNEL__ + +struct bmi_eeprom_id +{ + __u16 vendor; + __u16 product; + __u16 revision; +}; + + +enum { + BMI_EPSTATE_UNKNOWN = 0, + BMI_EPSTATE_I2C_READ_ERROR, + BMI_EPSTATE_CHECKSUM_ERROR, + BMI_EPSTATE_VALID, +}; + +extern void bmi_eeprom_get_id (struct bmi_eeprom_raw *raw, struct bmi_eeprom_id *epid); +extern int bmi_eeprom_checksum_validate ( struct bmi_eeprom_raw *raw ); +#endif /* __KERNEL__ */ + +static inline __u8 bmi_eeprom_checksum ( struct bmi_eeprom_raw *raw ) +{ + int i; + __u8 sum = 0; + __u8 *buf = (__u8*)raw; + + for (i = 0; i < (sizeof (struct bmi_eeprom_raw) - 1); i++) { + sum ^= *buf++; + } + return sum; +} + +#endif /* BMI_EEPROM_DATA_H */ --- /dev/null +++ git/include/linux/bmi/bmi-eeprom-driver.h @@ -0,0 +1,113 @@ +#ifndef BMI_EEPROM_DRIVER_H +#define BMI_EEPROM_DRIVER_H + +/******************************************************************************* + * Driver description: + * + * This driver provides operations that allow an application program to + * read and write the inventory eeprom on Bug Labs Bug PlugIn peripheral + * hardware modules. + * + * This driver is a character driver. + * + * Supported system calls + * + * This driver supports the following system calls: + * + * open() + * + * During the open() call, only driver initialization and house keeping + * are performed. The hardware is not touched. + * + * close() + * + * During the close() system call, only driver house keeping is performed. + * The hardware is not touched. + * + * ioctl() + * + * All of the ioctl() calls for this driver take 2 or 3 parameters. + * They are: + * file descriptor - obtained from open() call. + * ioctl command number - described below. + * void pointer to struct - ioctl command specific. + + * ioctl() return values: + * + * On success, all ioctl() calls return zero. + * + * On error, all ioctl() calls return -1 and errno is set appropriatly. + * Additional error information may be returned in the ioctl command + * structure. See the ioctl command structure declarations for more + * information. + * + ******************************************************************************* + */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Include the standard type definitions. + * The file to include depends on whether or not we are doing a kernel or + * application build. + */ +#ifdef __KERNEL__ + #include +#else + #include + #include +#endif /* __KERNEL__ */ + + +#ifdef __cplusplus +} +#endif + +//REWORK: Add documentation. + +//REWORK: Where should this file live so that applications can #include it ? + + +/* bmi_eeprom_request + + offset: 0 - 255 + size: 1- 256 + offset + size must be <= 256 + + */ +struct bmi_eeprom_request { + int return_code; + int xfer_count; + int size; + int offset; + __u8 data[256]; +}; + + +/******************************************************************************* + * Ioctl type definition: + * + * The ioctl type (magic) number for this driver is BUG_EEPROM_IOC_TYPE + * + ******************************************************************************* + */ + +#define BUG_EEPROM_IOC_TYPE 0xFE + +/******************************************************************************* + * Ioctl command definitions: + * + * The ioctl calls supported by this driver are: + * + ******************************************************************************* + */ + +#define BUG_EEPROM_READ \ + _IOR (BUG_EEPROM_IOC_TYPE, 0, struct bmi_eeprom_request) + +#define BUG_EEPROM_WRITE \ + _IOW (BUG_EEPROM_IOC_TYPE, 0, struct bmi_eeprom_request) + +#endif --- /dev/null +++ git/include/linux/bmi/bmi-eeprom.h @@ -0,0 +1,75 @@ +#ifndef BMI_EEPROM_H +#define BMI_EEPROM_H + +#include + +union _bmi_vendor { + __u16 vendor; + __u8 vendor_msb; + __u8 vendor_lsb; +}; + +union _bmi_product { + __u16 product; + __u8 product_msb; + __u8 product_lsb; +}; + +union _bmi_revision { + __u16 revision; + __u8 revision_msb; + __u8 revision_lsb; +}; + +struct bmi_eeprom_data +{ + __u8 format; /* byte 0x00 */ + __u8 vendor_msb; /* byte 0x01 */ + __u8 vendor_lsb; /* byte 0x02 */ + __u8 product_msb; /* byte 0x03 */ + __u8 product_lsb; /* byte 0x04 */ + __u8 revision_msb; /* byte 0x05 */ + __u8 revision_lsb; /* byte 0x06 */ +/* __u16 vendor; */ +/* __u16 product; */ +/* __u16 revision; */ + __u8 bus_usage; /* byte 0x07 */ + __u8 gpio_usage; /* byte 0x08 */ + __u8 power_use; /* byte 0x09 */ + __u8 power_charging; /* byte 0x0A */ + __u8 memory_size_msb; /* byte 0x0B */ + __u8 memory_size_lsb; /* byte 0x0C */ + __u8 serial_num_loc; /* byte 0x0D */ + __u8 serial_num_year; /* byte 0x0E */ + __u8 serial_num_week; /* byte 0x0F */ + __u8 serial_num_seq_msb; /* byte 0x10 */ + __u8 serial_num_seq_mid; /* byte 0x11 */ + __u8 serial_num_seq_lsb; /* byte 0x12 */ + __s8 description[108]; /* byte 0x13-0x7E */ + __u8 checksum; /* byte 0x7F */ +}; + + +struct bmi_eeprom_id +{ + __u16 vendor; + __u16 product; + __u16 revision; +}; + + +enum { + BMI_EPSTATE_UNKNOWN = 0, + BMI_EPSTATE_I2C_READ_ERROR, + BMI_EPSTATE_CHECKSUM_ERROR, + BMI_EPSTATE_VALID, +}; + + +//__u8 bmi_eeprom_checksum ( struct bmi_eeprom_data *raw ); +int bmi_eeprom_checksum_validate ( struct bmi_eeprom_data *raw ); +//extern void bmi_eeprom_get_id (struct bmi_eeprom_data *raw, struct bmi_eeprom_id *epid); +//extern int bmi_eeprom_checksum_validate ( struct bmi_eeprom_data *raw ); + + +#endif /* BMI_EEPROM_H */ --- /dev/null +++ git/include/linux/bmi/bmi-slot.h @@ -0,0 +1,29 @@ +#ifndef BMI_SLOT_H +#define BMI_SLOT_H + +//void bmi_slot_resrc_init(void); + +void bmi_slot_power_on (int num); +void bmi_slot_power_off (int num); + +void bmi_slot_gpio_configure (int num, int gpio); +int bmi_slot_gpio_get (int num); + +void bmi_slot_gpio_set (int num, int data); +void bmi_slot_uart_enable (int num); +void bmi_slot_uart_disable (int num); + +void bmi_slot_spi_enable (int num); +void bmi_slot_spi_disable (int num); + +void bmi_slot_audio_enable (int num); +void bmi_slot_audio_disable (int num); + +void bmi_slot_battery_enable (int num); +void bmi_slot_battery_disable (int num); + +int bmi_slot_module_present (int num); +//int bmi_slot_status_irq_state (int num); + + +#endif --- /dev/null +++ git/include/linux/bmi/bmi_audio.h @@ -0,0 +1,449 @@ +/* + * File: include/linux/bmi/bmi_audio.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus audio plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_AUDIO_H +#define BMI_AUDIO_H + +#include + +// GPIO defines +typedef enum { + GPIO_SPARE, // unused + GPIO_RESET, // CODEC reset + GPIO_GREEN, // green LED + GPIO_RED, // red LED +} BMI_AUDIO_GPIO; + +// GETSTAT defines +typedef enum { + GETSTAT_AMP = 0x001, // IOX bit 0 - amplifier off (O - low active) + GETSTAT_ISPARE = 0x002, // IOX bit 1 - spare + GETSTAT_VOLP = 0x004, // IOX bit 2 - VOLP (I - interrupt) + GETSTAT_VOLD = 0x008, // IOX bit 3 - VOLD (I - interrupt) + GETSTAT_HP_INS = 0x010, // IOX bit 4 - HP_INS (I - interrupt) + GETSTAT_MIC_INS = 0x020, // IOX bit 5 - MIC_INS (I - interrupt) + GETSTAT_LI_INS = 0x040, // IOX bit 6 - LI_INS (I - interrupt) + GETSTAT_LO_INS = 0x080, // IOX bit 7 - LO_INS (I - interrupt) + GETSTAT_GSPARE = 0x100, // unused + GETSTAT_RESET = 0x200, // CODEC reset + GETSTAT_GREEN = 0x400, // green LED + GETSTAT_RED = 0x800, // red LED +} BMI_AUDIO_GETSTAT; + +// input event bit defintions +typedef enum { + HEADPHONE_INSERTED = 0x001, // Detected headphone insertion + MICROPHONE_INSERTED = 0x002, // Detected microphone insertion + LINEOUT_INSERTED = 0x004, // Detected line out insertion + LINEIN_INSERTED = 0x008, // Detected line in insertion + VOLUME_DOWN = 0x010, // volume down button pressed + VOLUME_UP = 0x020, // volume up button pressed +} BMI_AUDIO_EVENT; + +// module numbers +typedef enum { + BMI_AUDIO_M1, // PIM 1 + BMI_AUDIO_M2, // PIM 2 + BMI_AUDIO_M3, // PIM 3 + BMI_AUDIO_M4, // PIM 4 + BMI_AUDIO_PIM_NUM, // Number of PIMs +} BMI_MODULE_NUMBERS; + +// TI '3105 CODEC registers +// Page 0 +#define CODEC_PAGE_SEL 0x0 // page select +#define CODEC_RESET 0x1 // reset (self-clearing) + #define CODEC_RESET_RESET 0x1 // reset (self-clearing) +#define CODEC_SAMPLE_RATE 0x2 // ADC/DAC sample rate + #define CODEC_SR1 0x0 // ADC/DAC sample rate + #define CODEC_SR1_5 0x1 // ADC/DAC sample rate + #define CODEC_SR2 0x2 // ADC/DAC sample rate + #define CODEC_SR2_5 0x3 // ADC/DAC sample rate + #define CODEC_SR3 0x4 // ADC/DAC sample rate + #define CODEC_SR3_5 0x5 // ADC/DAC sample rate + #define CODEC_SR4 0x6 // ADC/DAC sample rate + #define CODEC_SR4_5 0x7 // ADC/DAC sample rate + #define CODEC_SR5 0x8 // ADC/DAC sample rate + #define CODEC_SR5_5 0x9 // ADC/DAC sample rate + #define CODEC_SR6 0xA // ADC/DAC sample rate + #define CODEC_SR_SHIFT (4) // ADC shift +#define CODEC_PLLA 0x3 // PLL Programming A + #define CODEC_PLLA_EN 0x80 // PLL enabled + #define CODEC_PLLA_DIS 0x00 // PLL disabled + #define CODEC_PLLA_Q(x) (((x) & 0xF) << 3) // PLL Q + #define CODEC_PLLA_P(x) ((x) & 0x7) // PLL P +#define CODEC_PLLB 0x4 // PLL Programming B + #define CODEC_PLLB_J(x) (((x) & 0x3F) << 2) // PLL J +#define CODEC_PLLC_DMSB 0x5 // PLL D MSB +#define CODEC_PLLD_DLSB 0x6 // PLL D LSB + #define CODEC_PLLD_D(x) (((x) & 0x3F) << 2) // PLL D LSB +#define CODEC_DATAPATH 0x7 // Datapath set up + #define CODEC_DP_48 (0x00) // 48 kHz + #define CODEC_DP_44 (0x80) // 44.1 kHz + #define CODEC_ADR_DIS (0x00) // ADC Dual Rate + #define CODEC_ADR_EN (0x40) // ADC Dual Rate + #define CODEC_DDR_DIS (0x00) // DAC Dual Rate + #define CODEC_DDR_EN (0x20) // DAC Dual Rate + #define CODEC_DP_MUTED (0x00) // DAC Data Path + #define CODEC_DP_NORMAL (0x01) // DAC Data Path + #define CODEC_DP_REVERSE (0x02) // DAC Data Path + #define CODEC_DP_MONO (0x03) // DAC Data Path + #define CODEC_DP_L(x) ((x) << 3) // DAC Data Path Left + #define CODEC_DP_R(x) ((x) << 1) // DAC Data Path Right +#define CODEC_AIFA 0x8 // Audio serial data IF control + #define CODEC_AIFA_BCLK_S 0x00 // BCLK is input + #define CODEC_AIFA_BCLK_M 0x80 // BCLK is output + #define CODEC_AIFA_WCLK_S 0x00 // WCLK is input + #define CODEC_AIFA_WCLK_M 0x40 // WCLK is output + #define CODEC_AIFA_DOUT_N 0x00 // Dout not tri-state + #define CODEC_AIFA_DOUT_TS 0x20 // Dout tri-states + #define CODEC_AIFA_CLK_G 0x00 // CLKs gated (Master mode only) + #define CODEC_AIFA_CLK_F 0x10 // CLKs free run (Master mode only) + #define CODEC_AIFA_FX_OFF 0x00 // disable 3-D EFX + #define CODEC_AIFA_FX_ON 0x04 // enable 3-D EFX +#define CODEC_AIFB 0x9 // Audio serial data IF control + #define CODEC_AIFB_I2S 0x00 // MODE = I2S + #define CODEC_AIFB_DSP 0x40 // MODE = DSP + #define CODEC_AIFB_RJ 0x80 // MODE = Right Justified + #define CODEC_AIFB_LJ 0xC0 // MODE = Left Justified + #define CODEC_AIFB_16 0x00 // World Length = 16 bits + #define CODEC_AIFB_20 0x10 // World Length = 20 bits + #define CODEC_AIFB_24 0x20 // World Length = 24 bits + #define CODEC_AIFB_32 0x30 // World Length = 32 bits + #define CODEC_AIFB_256S 0x08 // 256-clock transfer mode (TDM) + #define CODEC_AIFB_DSYNC 0x04 // DAC resync + #define CODEC_AIFB_ASYNC 0x02 // ADC resync + #define CODEC_AIFB_MSYNC 0x01 // resync with soft-mute +#define CODEC_AIF_WORD_OFFSET 0xA // data bit offset in frame +#define CODEC_OVERFLOW 0xB // Overflow flags + #define CODEC_OF_LADC 0x80 // Left ADC + #define CODEC_OF_RADC 0x40 // Right ADC + #define CODEC_OF_LDAC 0x20 // Left DAC + #define CODEC_OF_RDAC 0x10 // Right DAC + #define CODEC_OF_PLLR(x) ((x) & 0xF) // PLL R +#define CODEC_FILT_CONTROL 0xC // Filter Control + #define CODEC_FC_LADC_HP45 0x40 // Left ADC Filter Control + #define CODEC_FC_LADC_HP125 0x80 // Left ADC Filter Control + #define CODEC_FC_LADC_HP25 0xC0 // Left ADC Filter Control + #define CODEC_FC_RADC_HP45 0x10 // Right ADC Filter Control + #define CODEC_FC_RADC_HP125 0x20 // Right ADC Filter Control + #define CODEC_FC_RADC_HP25 0x30 // Right ADC Filter Control +#define CODEC_HS 0xE // Headset/Button + #define CODEC_HS_COUPLED 0x80 // HP outputs AC-Coupled + #define CODEC_HS_ADIFF 0x40 // Output A differential + #define CODEC_HS_HSDET 0x10 // headset detected + #define CODEC_HS_BDIFF 0x08 // Output B differential +#define CODEC_LADC_PGA 0xF // Left ADC PGA +#define CODEC_RADC_PGA 0x10 // Right ADC PGA + #define CODEC_ADC_PGA_MUTE 0x80 // muted + #define CODEC_ADC_PGA_G(x) ((x) & 0x7F) // gain (0 to 59.5 dB) +#define CODEC_M3_LPGA 0x11 // MIC3 -> LADC PGA +#define CODEC_M3_RPGA 0x12 // MIC3 -> RADC PGA + #define CODEC_M3_PGA_LOFF (0xF << 4) // L input off + #define CODEC_M3_PGA_ROFF (0xF ) // R input off + #define CODEC_M3_PGA_L(x) (((x) & 0xF) << 4) // L input level (0 to -12 dB) + #define CODEC_M3_PGA_R(x) ((x) & 0xF) // R input level (0 to -12 dB) +#define CODEC_L1L_LPGA 0x13 // L1 Left -> LADC PGA +#define CODEC_L2L_LPGA 0x14 // L2 Left -> LADC PGA +#define CODEC_L1R_LPGA 0x15 // L1 Right -> LADC PGA +#define CODEC_L1R_RPGA 0x16 // R1 Right -> RADC PGA +#define CODEC_L2R_RPGA 0x17 // L2 Right -> RADC PGA +#define CODEC_L1L_RPGA 0x18 // L1 Left -> RADC PGA + #define CODEC_L_PGA(x) (((x) & 0xF) << 3) // input level (0 to -12 dB) + #define CODEC_LX_PGA_PU 0x04 // L1 power up + #define CODEC_L1L_PGA_SS(x) ((x) & 0x3) // L1 soft stepping + #define CODEC_L2L_LPGA_BIASED 0x04 // L2 Left weak bias +#define CODEC_MIC_BIAS 0x19 // Mic Bias + #define CODEC_MIC_BIAS_PD 0x00 // powered down + #define CODEC_MIC_BIAS_2V 0x40 // 2V + #define CODEC_MIC_BIAS_2P5V 0x80 // 2.5V + #define CODEC_MIC_BIAS_AVDD 0xC0 // AVDD +#define CODEC_MIC_LAGC_A 0x1A // L AGC A +#define CODEC_MIC_RAGC_A 0x1D // R AGC A + #define CODEC_MIC_AGC_EN 0x80 // enable + #define CODEC_MIC_AGC_TL(x) (((x) & 0x7) << 4) // target level (-5.5 to -24 dB) + #define CODEC_MIC_AGC_AT(x) (((x) & 0x3) << 2) // attack time (8 to 20 ms) + #define CODEC_MIC_AGC_DT(x) ((x) & 0x3) // decay time (100 to 500 ms) +#define CODEC_MIC_LAGC_B 0x1B // L AGC B +#define CODEC_MIC_RAGC_B 0x1E // R AGC B + #define CODEC_MIC_AGC_MG(x) (((x) & 0x7F) << 1) // max gain (0 to 59.5 dB) +#define CODEC_MIC_LAGC_C 0x1C // L AGC C +#define CODEC_MIC_RAGC_C 0x1F // R AGC C + #define CODEC_MIC_AGC_H(x) (((x) & 0x3) << 6) // NG hysteresis (1 to 3 dB, off) + #define CODEC_MIC_AGC_T(x) (((x) & 0x3) << 6) // NG Threshold (off to -90 dB) + #define CODEC_MIC_AGC_SC 0x1 // clip stepping enable +#define CODEC_MIC_LAGC_GAIN 0x20 // L AGC gain (-12 tp 59.5 dB) +#define CODEC_MIC_RAGC_GAIN 0x21 // R AGC gain (-12 tp 59.5 dB) +#define CODEC_MIC_LAGC_NGD 0x22 // L AGC NG debounce +#define CODEC_MIC_RAGC_NGD 0x23 // R AGC NG debounce + #define CODEC_MIC_AGC_NGD_D(x) (((x) & 0x1F) << 3) // detect(0 to 1536 ms) + #define CODEC_MIC_AGC_NGD_C(x) ((x) & 0x7) // control (0 to 32 ms) +#define CODEC_ADC_FLAG 0x24 // ADC flag + #define CODEC_ADC_FLAG_LPGA_S 0x80 // L ADC PGA gain equal + #define CODEC_ADC_FLAG_LPWR_S 0x40 // L ADC powered-up + #define CODEC_ADC_FLAG_LSIGD 0x20 // L AGC signal detected + #define CODEC_ADC_FLAG_LSAT 0x10 // L AGC saturation detected + #define CODEC_ADC_FLAG_RPGA_S 0x08 // R ADC PGA gain equal + #define CODEC_ADC_FLAG_RPWR_S 0x04 // R ADC powered-up + #define CODEC_ADC_FLAG_RSIGD 0x02 // R AGC signal detected + #define CODEC_ADC_FLAG_RSAT 0x01 // R AGC saturation detected +#define CODEC_DAC_PWR 0x25 // DAC power and output driver +#define CODEC_DAC_HPWR 0x26 // high-power output driver + #define CODEC_DAC_PWR_L_EN 0x80 // L power up + #define CODEC_DAC_PWR_R_EN 0x40 // R power up + #define CODEC_DAC_PWR_HP_DIFF 0x00 // differential of HPLOUT + #define CODEC_DAC_PWR_HP_VCM 0x10 // constant VCM + #define CODEC_DAC_PWR_HP_ISE 0x20 // independant single ended + #define CODEC_DAC_HPWR_HPL_DIFF 0x18 // short circuit protection + #define CODEC_DAC_HPWR_SS 0x04 // short circuit protection + #define CODEC_DAC_HPWR_SS_C 0x00 // limit current + #define CODEC_DAC_HPWR_SS_P 0x02 // power down +#define CODEC_DAC_HPOS 0x28 // high-power output stage + #define CODEC_DAC_HPOS_CM1P35 0x00 // common mode = 1.35V + #define CODEC_DAC_HPOS_CM1P5 0x40 // common mode = 1.5V + #define CODEC_DAC_HPOS_CM1P65 0x80 // common mode = 1.65V + #define CODEC_DAC_HPOS_CM1P8 0xC0 // common mode = 1.8V + #define CODEC_DAC_HPOS_L2L_BYP 0x00 // L2 L bypass disabled + #define CODEC_DAC_HPOS_L2L_SE 0x10 // L2 L bypass = L2LP + #define CODEC_DAC_HPOS_L2L_BYP 0x00 // L2 R bypass disabled + #define CODEC_DAC_HPOS_L2R_SE 0x04 // L2 R bypass = L2RP + #define CODEC_DAC_HPOS_FS 0x00 // soft stepping: 1 / fs + #define CODEC_DAC_HPOS_2FS 0x01 // soft stepping: 1 / 2 fs + #define CODEC_DAC_HPOS_SS_DIS 0x02 // soft stepping disabled +#define CODEC_DAC_OS 0x29 // output switching + #define CODEC_DAC_OS_L1 0x00 // L = L1 + #define CODEC_DAC_OS_L3 0x40 // L = L3 + #define CODEC_DAC_OS_L2 0x80 // L = L2 + #define CODEC_DAC_OS_R1 0x00 // R = R1 + #define CODEC_DAC_OS_R3 0x10 // R = R3 + #define CODEC_DAC_OS_R2 0x20 // R = R2 + #define CODEC_DAC_OS_VOL_S 0x00 // volume separate + #define CODEC_DAC_OS_VOL_R 0x01 // L follows R + #define CODEC_DAC_OS_VOL_L 0x02 // R follows L +#define CODEC_DAC_PR 0x2A // pop reduction + #define CODEC_DAC_PR_DEL(x) (((x) & 0xF) << 4) // delay (0 us to 4 s) + #define CODEC_DAC_PR_RU(x) (((x) & 0x3) << 2) // ramp up (0 to 4 ms) + #define CODEC_DAC_CM_AVDD 0x00 // common mode from AVDD + #define CODEC_DAC_CM_BG 0x02 // common mode from band gap +#define CODEC_DAC_LVOL 0x2B // Left volume +#define CODEC_DAC_RVOL 0x2C // Right volume + #define CODEC_DAC_VOL_MUTE 0x80 // muted + #define CODEC_DAC_VOL(x) ((x) & 0x7F) // volume (0 to -63.5 dB) +#define CODEC_L2L_HPL 0x2D // L2L -> HPLOUT +#define CODEC_PGAL_HPL 0x2E // PGAL -> HPLOUT +#define CODEC_DACL1_HPL 0x2F // DACL1 -> HPLOUT +#define CODEC_L2R_HPL 0x30 // L2R -> HPLOUT +#define CODEC_PGAR_HPL 0x31 // PGAR -> HPLOUT +#define CODEC_DACR1_HPL 0x32 // DACLR -> HPLOUT +#define CODEC_L2L_HPLCOM 0x34 // L2L -> HPLCOM +#define CODEC_PGAL_HPLCOM 0x35 // PGAL -> HPLCOM +#define CODEC_DACL1_HPLCOM 0x36 // DACL1 -> HPLCOM +#define CODEC_L2R_HPLCOM 0x37 // L2R -> HPLCOM +#define CODEC_PGAR_HPLCOM 0x38 // PGAR -> HPLCOM +#define CODEC_DACR1_HPLCOM 0x39 // DACR1 -> HPLCOM +#define CODEC_L2L_HPR 0x3B // L2L -> HPROUT +#define CODEC_PGAL_HPR 0x3C // PGAL -> HPROUT +#define CODEC_DACL1_HPR 0x3D // DACL1 -> HPROUT +#define CODEC_L2R_HPR 0x3E // L2R -> HPROUT +#define CODEC_PGAR_HPR 0x3F // PGAR -> HPROUT +#define CODEC_DACR1_HPR 0x40 // DACLR -> HPROUT +#define CODEC_L2L_HPRCOM 0x42 // L2L -> HPRCOM +#define CODEC_PGAL_HPRCOM 0x43 // PGAL -> HPRCOM +#define CODEC_DACL1_HPRCOM 0x44 // DACL1 -> HPRCOM +#define CODEC_L2R_HPRCOM 0x45 // L2R -> HPRCOM +#define CODEC_PGAR_HPRCOM 0x46 // PGAR -> HPRCOM +#define CODEC_DACR1_HPRCOM 0x47 // DACLR -> HPRCOM +#define CODEC_L2L_LLOPM 0x50 +#define CODEC_PGAL_LLOPM 0x51 +#define CODEC_DACL1_LLOPM 0x52 +#define CODEC_L2R_LLOPM 0x53 +#define CODEC_PGAR_LLOPM 0x54 +#define CODEC_DACR1_LLOPM 0x55 +#define CODEC_L2L_RLOPM 0x57 +#define CODEC_PGA_RLOPM 0x58 +#define CODEC_DACL1_RLOPM 0x59 +#define CODEC_L2R_RLOPM 0x5A +#define CODEC_PGAR_RLOPM 0x5B +#define CODEC_DACR1_RLOPM 0x5C + #define CODEC_HP_EN 0x80 // enabled + #define CODEC_HP_VOL(x) ((x) & 0x7F) // see datasheet Table 6 +#define CODEC_HPLOUT 0x33 // HPLOUT output level +#define CODEC_HPLCOM 0x3A // HPLCOM output level +#define CODEC_HPROUT 0x41 // HPROUT output level +#define CODEC_HPRCOM 0x48 // HPRCOM output level +#define CODEC_LLOPM 0x56 // LLOPM output level +#define CODEC_RLOPM 0x5D // RLOPM output level + #define CODEC_HPX_LC(x) (((x) & 0xF) << 4) // output level + #define CODEC_HPX_EN 0x08 // not muted + #define CODEC_HPX_PD 0x04 // power down enable + #define CODEC_HPX_STAT 0x02 // gain not applied + #define CODEC_HPX_PC 0x01 // fully powered up +#define CODEC_PSR 0x5E // Power Status + #define CODEC_PSR_LDPS 0x80 // L DAC + #define CODEC_PSR_DDPS 0x40 // R DAC + #define CODEC_PSR_LLOPM 0x10 // L LOPM + #define CODEC_PSR_RLOPM 0x08 // R LOPM + #define CODEC_PSR_HPLOUT 0x04 // HPLOUT + #define CODEC_PSR_HPLCOM 0x02 // HPLCOM +#define CODEC_SS 0x5F // driver short circuit + #define CODEC_SS_HPLOUT 0x80 + #define CODEC_SS_HPROUT 0x40 + #define CODEC_SS_HPLCOM 0x20 + #define CODEC_SS_HPRCOM 0x10 + #define CODEC_SS_HPLCOM_PS 0x08 + #define CODEC_SS_HPRCOM_PS 0x04 +#define CODEC_S_INT 0x60 // sticky interrupt +#define CODEC_RT_INT 0x61 // real-time interrupt + #define CODEC_INT_HPLOUT_SS 0x80 + #define CODEC_INT_HPROUT_SS 0x40 + #define CODEC_INT_HPLCOM_SS 0x20 + #define CODEC_INT_HPRCOM_SS 0x10 + #define CODEC_INT_HS_DET 0x04 + #define CODEC_INT_LAGC_NG 0x02 + #define CODEC_INT_RAGC_NG 0x01 +#define CODEC_CLK 0x65 // clock source + #define CODEC_CLK_PLLDIV 0x00 + #define CODEC_CLK_CLKDIV 0x01 +#define CODEC_CLKGEN 0x66 // clock generation + #define CODEC_CLKGEN_C_M 0x02 // MCLK -> CLK + #define CODEC_CLKGEN_C_G 0x42 // GPIO2 -> CLK + #define CODEC_CLKGEN_C_B 0x82 // BCLK -> CLK + #define CODEC_CLKGEN_P_M 0x02 // MCLK -> PLL + #define CODEC_CLKGEN_P_G 0x12 // GPIO2 -> PLL + #define CODEC_CLKGEN_P_B 0x22 // BCLK -> PLL +#define CODEC_LAGC_ATT 0x67 // L AGC Attack +#define CODEC_RAGC_ATT 0x69 // R AGC Attack + #define CODEC_AGC_ATT_R26 0x00 // source reg 36 + #define CODEC_AGC_ATT_R103 0x80 // source reg 103 + #define CODEC_AGC_ATT_T(x) (((x) & 0x3) << 5) // time + #define CODEC_AGC_ATT_M(x) (((x) & 0xF) << 2) // multiplication +#define CODEC_LAGC_DEC 0x68 // L AGC Decay +#define CODEC_RAGC_DEC 0x6A // R AGC Decay + #define CODEC_AGC_DEC_R26 0x00 // source reg 36 + #define CODEC_AGC_DEC_R104 0x80 // source reg 104 + #define CODEC_AGC_DEC_T(x) (((x) & 0x3) << 5) // time + #define CODEC_AGC_DEC_M(x) (((x) & 0xF) << 2) // multiplication +#define CODEC_DP_I2C 0x6B // digital path and I2C + #define CODEC_DP_I2C_LHPF_EN 0x80 + #define CODEC_DP_I2C_RHPF_EN 0x40 + #define CODEC_ADC_DFLDRD 0x00 + #define CODEC_ADC_DFLDRA 0x10 + #define CODEC_ADC_DFLARD 0x20 + #define CODEC_ADC_DFLARA 0x30 + #define CODEC_ADC_F_EN 0x08 + #define CODEC_I2C_ERR_DIS 0x04 + #define CODEC_I2C_HANG 0x01 +#define CODEC_PASB 0x6C // passive analog bypass + #define CODEC_PASB_L2RP_RLOP 0x40 + #define CODEC_PASB_L1RP_RLOP 0x10 + #define CODEC_PASB_L2LP_LLOP 0x04 + #define CODEC_PASB_L1LP_LLOP 0x01 +#define CODEC_DAC_QCA 0x6D // DAC current adjust +#define CODEC_DAC_QCA_50 0x40 +#define CODEC_DAC_QCA_100 0xC0 +// Page 1 +#define CODEC_EF_LN0M 0x1 +#define CODEC_EF_LN0L 0x2 +#define CODEC_EF_LN1M 0x3 +#define CODEC_EF_LN1L 0x4 +#define CODEC_EF_LN2M 0x5 +#define CODEC_EF_LN2L 0x6 +#define CODEC_EF_LN3M 0x7 +#define CODEC_EF_LN3L 0x8 +#define CODEC_EF_LN4M 0x9 +#define CODEC_EF_LN4L 0xA +#define CODEC_EF_LN5M 0xB +#define CODEC_EF_LN5L 0xC + +#define CODEC_EF_LD1M 0xD +#define CODEC_EF_LD1L 0xE +#define CODEC_EF_LD2M 0xF +#define CODEC_EF_LD2L 0x10 +#define CODEC_EF_LD4M 0x11 +#define CODEC_EF_LD4L 0x12 +#define CODEC_EF_LD5M 0x13 +#define CODEC_EF_LD5L 0x14 + +#define CODEC_DF_LN0M 0x15 +#define CODEC_DF_LN0L 0x16 +#define CODEC_DF_LN1M 0x17 +#define CODEC_DF_LN1L 0x18 + +#define CODEC_DF_LD1M 0x19 +#define CODEC_DF_LD1L 0x1A + +#define CODEC_EF_RN0M 0x1B +#define CODEC_EF_RN0L 0x1C +#define CODEC_EF_RN1M 0x1D +#define CODEC_EF_RN1L 0x1E +#define CODEC_EF_RN2M 0x1F +#define CODEC_EF_RN2L 0x20 +#define CODEC_EF_RN3M 0x21 +#define CODEC_EF_RN3L 0x22 +#define CODEC_EF_RN4M 0x23 +#define CODEC_EF_RN4L 0x24 +#define CODEC_EF_RN5M 0x25 +#define CODEC_EF_RN5L 0x26 + +#define CODEC_EF_RD1M 0x27 +#define CODEC_EF_RD1L 0x28 +#define CODEC_EF_RD2M 0x29 +#define CODEC_EF_RD2L 0x2A +#define CODEC_EF_RD4M 0x2B +#define CODEC_EF_RD4L 0x2C +#define CODEC_EF_RD5M 0x2D +#define CODEC_EF_RD5L 0x2E + +#define CODEC_DF_RN0M 0x2F +#define CODEC_DF_RN0L 0x30 +#define CODEC_DF_RN1M 0x31 +#define CODEC_DF_RN1L 0x32 + +#define CODEC_DF_RD1M 0x33 +#define CODEC_DF_RD1L 0x34 + +#define CODEC_3DAM 0x35 +#define CODEC_3DAL 0x36 + +#define CODEC_LHPN0M 0x41 +#define CODEC_LHPN0L 0x42 +#define CODEC_LHPN1M 0x43 +#define CODEC_LHPN1L 0x44 +#define CODEC_LHPD1M 0x45 +#define CODEC_LHPD1L 0x46 + +#define CODEC_RHPN0M 0x47 +#define CODEC_RHPN0L 0x48 +#define CODEC_RHPN1M 0x49 +#define CODEC_RHPN1L 0x4A +#define CODEC_RHPD1M 0x4B +#define CODEC_RHPD1L 0x4C + +struct codec_xfer { + unsigned char page; + unsigned char reg; + unsigned char data; +} codec_xfer; + +// IOCTL commands for BMI AUDIO driver +#define BMI_AUDIO_RLEDOFF _IO(BMI_AUDIO_IOCTL, 0x1) // Turn off red LED +#define BMI_AUDIO_RLEDON _IO(BMI_AUDIO_IOCTL, 0x2) // Turn on red LED +#define BMI_AUDIO_GLEDOFF _IO(BMI_AUDIO_IOCTL, 0x3) // Turn off green LED +#define BMI_AUDIO_GLEDON _IO(BMI_AUDIO_IOCTL, 0x4) // Turn on green LED +#define BMI_AUDIO_SPKOFF _IO(BMI_AUDIO_IOCTL, 0x5) // Turn off speaker +#define BMI_AUDIO_SPKON _IO(BMI_AUDIO_IOCTL, 0x6) // Turn on speaker +#define BMI_AUDIO_GETSTAT _IOR(BMI_AUDIO_IOCTL, 0x9, unsigned int *) // READ IOX register +#define BMI_AUDIO_SETRST _IO(BMI_AUDIO_IOCTL, 0xA) // Set RESET to '0' +#define BMI_AUDIO_CLRRST _IO(BMI_AUDIO_IOCTL, 0xB) // Set RESET to '1' +#define BMI_AUDIO_ACTIVATE _IO(BMI_AUDIO_IOCTL, 0xC) // Activate a module for audio capture +#define BMI_AUDIO_DEACTIVATE _IO(BMI_AUDIO_IOCTL, 0xD) // Deactivate a module for audio capture +#define BMI_AUDIO_WCODEC _IOW(BMI_AUDIO_IOCTL, 0xE, struct codec_xfer *) // write CODEC register +#define BMI_AUDIO_RCODEC _IOR(BMI_AUDIO_IOCTL, 0xF, struct codec_xfer *) // read CODEC register + +#endif /* BMI_AUDIO_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_camera.h @@ -0,0 +1,36 @@ +/* + * File: include/linux/bmi/bmi_camera.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus camera plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_CAMERA_A_H +#define BMI_CAMERA_A_H + +#include +#include + + // IOCTL commands for BMI Camera driver + +#define BMI_CAM_FLASH_HIGH_BEAM _IOW(BMI_CAMERA_IOCTL, 0x1, __u32) +#define BMI_CAM_FLASH_LOW_BEAM _IOW(BMI_CAMERA_IOCTL, 0x2, __u32) +#define BMI_CAM_FLASH_LED_OFF _IOW(BMI_CAMERA_IOCTL, 0x3, __u32) +#define BMI_CAM_FLASH_LED_ON _IOW(BMI_CAMERA_IOCTL, 0x4, __u32) + +#define BMI_CAM_RED_LED_OFF _IOW(BMI_CAMERA_IOCTL, 0x5, __u32) // Turn off red LED +#define BMI_CAM_RED_LED_ON _IOW(BMI_CAMERA_IOCTL, 0x6, __u32) // Turn on red LED +#define BMI_CAM_GREEN_LED_OFF _IOW(BMI_CAMERA_IOCTL, 0x7, __u32) // Turn off green LED +#define BMI_CAM_GREEN_LED_ON _IOW(BMI_CAMERA_IOCTL, 0x8, __u32) // Turn on green LED + +#define BMI_CAM_SELECT _IOW(BMI_CAMERA_IOCTL, 0x9, __u32) // Select camera module +#define BMI_CAM_GET_SELECTED _IOR(BMI_CAMERA_IOCTL, 0xA, __u32) // return selected camera module + + // input event definitions +#define BN_SHUTTER BTN_0 +#define BN_ZOOMIN BTN_1 +#define BN_ZOOMOUT BTN_2 + +#endif /* BMI_CAMERA_A_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_gps.h @@ -0,0 +1,30 @@ +/* + * File: include/linux/bmi/bmi_gps.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus gps plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_GPS_H +#define BMI_GPS_H + +#include + + // IOCTL commands for BMI GPS driver +#define BMI_GPS_RLEDOFF _IOW(BMI_GPS_IOCTL, 0x1, unsigned int) // Turn off red LED +#define BMI_GPS_RLEDON _IOW(BMI_GPS_IOCTL, 0x2, unsigned int) // Turn on red LED +#define BMI_GPS_GLEDOFF _IOW(BMI_GPS_IOCTL, 0x3, unsigned int) // Turn off green LED +#define BMI_GPS_GLEDON _IOW(BMI_GPS_IOCTL, 0x4, unsigned int) // Turn on green LED +#define BMI_GPS_SETBOOT _IOW(BMI_GPS_IOCTL, 0x5, unsigned int) // Set BOOT to '1' +#define BMI_GPS_CLRBOOT _IOW(BMI_GPS_IOCTL, 0x6, unsigned int) // Set BOOT to '0' +#define BMI_GPS_SETWAKE _IOW(BMI_GPS_IOCTL, 0x7, unsigned int) // Set WAKE to '1' +#define BMI_GPS_CLRWAKE _IOW(BMI_GPS_IOCTL, 0x8, unsigned int) // Set WAKE to '0' +#define BMI_GPS_GETSTAT _IOR(BMI_GPS_IOCTL, 0x9, unsigned int *) // READ IOX register +#define BMI_GPS_SETRST _IOW(BMI_GPS_IOCTL, 0xA, unsigned int) // Set RESET to '0' +#define BMI_GPS_CLRRST _IOW(BMI_GPS_IOCTL, 0xB, unsigned int) // Set RESET to '1' +#define BMI_GPS_ACTIVE_ANT _IOW(BMI_GPS_IOCTL, 0xC, unsigned int) // Select Active Antenna +#define BMI_GPS_PASSIVE_ANT _IOW(BMI_GPS_IOCTL, 0xD, unsigned int) // Select Passive Antenna + +#endif /* BMI_GPS_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_gsm.h @@ -0,0 +1,33 @@ +/* + * File: include/linux/bmi/bmi_gsm.h + * Author: Matt Isaacs + * + * This is the application header file for the BMI bus GSM/UMTS plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_GSM_H +#define BMI_GSM_H + +#include + +// GPIO +#define GSM_GPIO_RED_LED 3 // default to input +#define GSM_GPIO_GREEN_LED 2 // default to input +#define GSM_GPIO_1 1 // default to input +#define GSM_GPIO_0 0 // default to input + +#define GSM_GPIO_LED_ON 0 +#define GSM_GPIO_LED_OFF 1 + + +// von hippel driver ioctl definitions +#define BMI_GSM_RLEDOFF _IOW(BMI_GSM_IOCTL, 0x1, unsigned int) // Turn off red LED +#define BMI_GSM_RLEDON _IOW(BMI_GSM_IOCTL, 0x2, unsigned int) // Turn on red LED +#define BMI_GSM_GLEDOFF _IOW(BMI_GSM_IOCTL, 0x3, unsigned int) // Turn off green LED +#define BMI_GSM_GLEDON _IOW(BMI_GSM_IOCTL, 0x4, unsigned int) // Turn on green LED +#define BMI_GSM_GETSTAT _IOR(BMI_GSM_IOCTL, 0x5, unsigned int *) // READ IOX register + + +#endif /* BMI_GSM_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_ioctl.h @@ -0,0 +1,27 @@ +/* + * File: include/linux/bmi/bmi_ioctl.h + * Author: Peter Giacomini + * + * This is the header file for the BMI ioctl definitions + */ + +#ifndef BMI_IOCTL_H +#define BMI_IOCTL_H + + // IOCTL Magic Numbers +#define BMI_CAMERA_IOCTL ('c') +#define BMI_LCD_IOCTL ('l') +#define BMI_GPS_IOCTL ('g') +#define BMI_MDACC_IOCTL ('m') +#define BMI_AUDIO_IOCTL ('a') +#define BMI_VH_IOCTL ('v') +#define BMI_WIFI_IOCTL ('W') +#define BMI_ZIGBEE_IOCTL ('Z') +#define BMI_GSM_IOCTL ('G') +#define BMI_PROJECTOR_IOCTL ('p') +#define BMI_SENSOR_IOCTL ('s') +#define BMI_LCD2X_IOCTL ('x') +#define BMI_RFID_IOCTL ('r') + +#endif /* BMI_IOCTL_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_lcd.h @@ -0,0 +1,71 @@ +/* + * File: include/linux/bmi/bmi_lcd.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus lcd plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_LCD_H +#define BMI_LCD_H + +#include +#include + + // IOCTL commands for BMI LCD driver +#define BMI_LCD_RLEDOFF _IOW(BMI_LCD_IOCTL, 0x1, __u32) // turn off Red LED +#define BMI_LCD_RLEDON _IOW(BMI_LCD_IOCTL, 0x2, __u32) // turn on Red LED +#define BMI_LCD_GLEDOFF _IOW(BMI_LCD_IOCTL, 0x3, __u32) // turn off Green LED +#define BMI_LCD_GLEDON _IOW(BMI_LCD_IOCTL, 0x4, __u32) // turn on Green LED +#define BMI_LCD_VSYNC_DIS _IOW(BMI_LCD_IOCTL, 0x5, __u32) // Enable VSYNC output buffer +#define BMI_LCD_VSYNC_EN _IOW(BMI_LCD_IOCTL, 0x6, __u32) // Disable VSYNC output buffer +#define BMI_LCD_EN _IOW(BMI_LCD_IOCTL, 0x7, __u32) // Enable LCD component +#define BMI_LCD_DIS _IOW(BMI_LCD_IOCTL, 0x8, __u32) // Disable LCD component +#define BMI_LCD_SER_EN _IOW(BMI_LCD_IOCTL, 0x9, __u32) // Enable Seriallizer component +#define BMI_LCD_SER_DIS _IOW(BMI_LCD_IOCTL, 0xa, __u32) // Disable Seriallizer component +#define BMI_LCD_SETRST _IOW(BMI_LCD_IOCTL, 0xb, __u32) // Disable entire module +#define BMI_LCD_CLRRST _IOW(BMI_LCD_IOCTL, 0xc, __u32) // Enable entire module +#define BMI_LCD_SET_BL _IOW(BMI_LCD_IOCTL, 0xd, __u32) // Set IOX backlight bits [2:0] +#define BMI_LCD_GETSTAT _IOR(BMI_LCD_IOCTL, 0xe, __u32) // Get IOX state +#define BMI_LCD_ACTIVATE _IOW(BMI_LCD_IOCTL, 0xf, __u32) // Activate SER, TS, ACCEL +#define BMI_LCD_DEACTIVATE _IOW(BMI_LCD_IOCTL, 0x10, __u32) // Deactivate SER, TS, ACCEL +#define BMI_LCD_SUSPEND _IOW(BMI_LCD_IOCTL, 0x11, __u32) // Power down module +#define BMI_LCD_RESUME _IOW(BMI_LCD_IOCTL, 0x12, __u32) // Power up module + +/*Izzy Additions*/ +#define BMI_LCD_MIN_XC 0 +#define BMI_LCD_MAX_XC 0x3fff +#define BMI_LCD_MIN_YC 0 +#define BMI_LCD_MAX_YC 0x3fff + +/*struct lcd_ctl +{ + int slot; + struct cdev cdev; + struct device *class_dev; +}; +*/ + +// +// Orientation - location of module 1-3 shorter edge (when facing LCD side) +// when not FACEUP or FACEDOWN +// +// Note that orientation is only reported through bmi_lcd_ts[0-3] +// +#define ACC_PITCH_MSK (0xFFFF0000) +#define ACC_ROLL_MSK (0xFFFF) + + // touch screen input devices +enum { + BMI_TS_M1, // bmi_lcd_ts0 - slot 0 + BMI_TS_M2, // bmi_lcd_ts1 - slot 1 + BMI_TS_M3, // bmi_lcd_ts2 - slot 2 + BMI_TS_M4, // bmi_lcd_ts3 - slot 3 + BMI_TS_M13, // bmi_lcd_ts4 - slot 0 and 2 + BMI_TS_M24, // bmi_lcd_ts5 - slot 1 and 3 + BMI_TS_M1234, // bmi_lcd_ts6 - slot 0-3 + BMI_TS_NUM, +} lcd_ts_t; + +#endif /* BMI_LCD_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_mdacc.h @@ -0,0 +1,518 @@ +/*----------------------------------------------------------------------------- + * + * File: include/linux/bmi/bug_mdacc.h + * + *----------------------------------------------------------------------------- + * This file contains information needed by application programs that use the + * Bug Motion Detector Accelerometer (MDACC) Plug-In Module. + * + * The Bug Motion Detector Accelerometer (MDACC) Plug-In Module is a circuit + * board that contains the following devices: + * + * a motion sensor, + * a 3-axis accelerometer + * a micro-controller. + * 1 Red LED + * 1 Green LED + * + * The micro-controller behaves as an SPI-slave device. The host controls + * the operation of the micro-controller by issuing sequences of SPI messages. + * The micro-controller periodically samples the motion sensor and the + * accelerometer. The micro-controller generates an interrupts to the host + * processor. The micro-controller provides data to the host in response + * to received SPI messages. + * + * Application software can the MDACC Plug-in modules using the following + * device drivers: + * + * BMI MDACC Control Driver + * BMI MDACC Motion Detector Driver + * BMI MDACC Accelerometer Driver + * + * These drivers allow for independent operation of MDACC peripheral devices. + * + * This file contains the interface definition for all 3 device drivers. + * + * --------------------------------------------------------------------------- + * + * Default Device Names: + * + * The following device nodes are created for each MDACC card present in the + * system. + * + * /dev/bmi_mdacc_ctl_mX where X = 1,2,3,4 (bmi connector number) + * /dev/bmi_mdacc_mot_mX where X = 1,2,3,4 (bmi connector number) + * /dev/bmi_mdacc_acc_mX where X = 1,2,3,4 (bmi connector number) + * + * If the MDACC is not present in a given slot, the corresponding device nodes + * are not created. + * + *---------------------------------------------------------------------------- + * + * BMI MDACC Control Driver + * + *---------------------------------------------------------------------------- + * + * This character driver provides access to the Red and Green LEDs via + * via the ioctl() system call. + * + * Supported system calls: open(), close(), ioctl(). + * + * The following IOCTL commands are defined for this driver. + * + * BMI_MDACC_CTL_RED_LED_OFF + * BMI_MDACC_CTL_RED_LED_ON + * BMI_MDACC_CTL_GREEN_LED_OFF + * BMI_MDACC_CTL_GREEN_LED_ON + * + * Note that the 3rd argument to the ioctl system call are not used by the + * ioctl commands listed above. + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * + * BMI MDACC Motion Detector Driver + * + *---------------------------------------------------------------------------- + * + * This character driver provides access to the motion sensor via the SPI + * interface. This driver enforces single-open and stop-on-close behaviors. + * + * + * Supported system calls: open(), close(), ioctl(). read(), select(). + * + * BMI MDACC Motion Detector ioctl() interface + *-------------------------------------------- + * + * The following IOCTL commands are defined for this driver. + * + * BMI_MDACC_MOTION_DETECTOR_GET_STATUS + * BMI_MDACC_MOTION_DETECTOR_RUN + * BMI_MDACC_MOTION_DETECTOR_STOP + * + * + * The BMI_MDACC_MOTION_DETECTOR_RUN command sends an SPI message to the + * microcontroller to enable sampling of the motion detector status pin. + * This command does not use the 3rd parameter to the ioctl system call. + * + * The BMI_MDACC_MOTION_DETECTOR_STOP command sends an SPI message to the + * microcontroller to halt sampling of the motion detector status pin. + * This command does not use the 3rd parameter to the ioctl system call. + * + * The BMI_MDACC_MOTION_DETECTOR_GET_STATUS command gets the motion detector + * status byte that is maintained by the motion detector driver. + * The third argument to the ioctl system call should be the address of a + * the receive buffer that is 1 byte in size. + * + * Motion Detect Status Bit Descriptions + * --------------------------------------- + * + * The Motion Detect Status byte is returned by the system calls to the + * MDACC Motion Detector driver: + * + * ioctl(BMI_MDACC_MOTION_GET_STATUS) + * read() + * + * The following bits are defined in the status byte. + * + * BMI_MOTION_DETECT_STATUS + * + * This bit is the present status of the the motion detector status pin. + * A value of 1 indicates that motion is being detected. + * A value of 0 indicates that motion is not being detected. + * + * + * BMI_MOTION_DETECT_LATCHED_STATUS + * + * This bit is the latched status of the motion sensor. This bit is set to 1 + * when the BMI_MOTION_DETECT_STATUS bit changes from 0 to 1. This bit will + * be cleared as the result of an "ioctl(BMI_MDACC_MOTION_GET_STATUS)" or a + * read() system call. + * + * BMI_MOTION_DETECT_DELTA + * + * This bit indicates that the motion detector status has changed from 1 to 0 + * or has changed from 0 to 1. This bit will be cleared as the result of an + * "ioctl(BMI_MDACC_MOTION_GET_STATUS)" or read() system calls. + * + * + * BMI_MOTION_DETECT_ENABLED + * + * This bits is the state of the motion detector sampling and status reporting + * mechanism. A value of 1 indicates that the motion detector is enabled. A + * value of 0 indicates that the motion detector is disabled. + * + * + * Motion Detect read() system call + * -------------------------------- + * + * The read() call for this driver allows the application program to read the + * motion detector status only when the status has changed. + * + * Prior to issuing a read() to this driver, the application must enabled the + * motion detector using the "ioctl(BMI_MDACC_MOTION_DETECTOR_RUN)" command. + * + * read parameters: + * + * buffer: status byte destination address. + * size: 1 + * + * Motion Detect blocking read() behavior + * -------------------------------------- + * + * If the motion detector status HAS NOT changed, then the driver will sleep + * waiting for the motion detect status to change. + * + * If the driver is awoken by a Motion Detect status change interrupt, the + * underlying hardware will be accessed (for a second time) and the motion + * detect status will be updated. + * + * The status data byte will be copied to the user-supplied buffer. The + * following bits will then be cleared in the motion detect status byte: + * + * BMI_MOTION_DETECT_LATCHED_STATUS + * BMI_MOTION_DETECT_DELTA + * + * The driver will then be marked as "not-ready-to-read". + * + * If the the driver is awoken by a signal, the driver will return failure (-1) + * and errno will be set to ERESTARTSYS. + * + * + * Motion Detect non-blocking read() behavior + * ------------------------------------------ + * If the motion detector status HAS NOT changed prior to the non-blocking + * read() system call, then the driver will return failure (-1) and errno will + * be set to EAGAIN. + * + * If the motion detector status HAS changed prior to the non-blocking read() + * system call, the underlying hardware will be accessed and the motion detect + * status will be updated. The status data byte will be copied to the user + * supplied buffer. The following bits will then be cleared in the motion + * detect status byte: + * + * BMI_MOTION_DETECT_LATCHED_STATUS + * BMI_MOTION_DETECT_DELTA + * + * The driver will then be marked as "not-ready-to-read". + * + * + * Motion Detect select() system call + * ----------------------------------- + * + * This driver supports select() for read only. Select for write and + * exception is not supported. + * + * When a Motion Detect interrupt occurs, the file descriptor corresponding + * to the Motion Detector driver will be marked as "ready for read". + * + * --------------------------------------------------------------------------- + */ + + +/*---------------------------------------------------------------------------- + * + * BMI MDACC Accelerometer Driver + * + *---------------------------------------------------------------------------- + * + * This character driver provides access to the accelerometer data via an SPI + * interface. This driver enforces single-open and stop-on-close behaviors. + * + * Supported system calls: open(), close(), ioctl(). read(), select(). + * + * + * MDACC Accelerometer Driver Configuration + * ----------------------------------------- + * + * The micro-controller on the MDACC Plug-In module uses an internal 10 bit A/D + * converter to sample the 3 analog output channels of the accelerometer + * device. The analog channels are sampled periodically at a rate that can be + * configured by an application program. When a set of 3 channel samples has + * been acquired, the micro-controller generates an interrupt to the host + * processor. The host processor then issues SPI messages to the + * micro-controller to obtain the 3 channel sample set. + * + * The MDACC Accelerometer Driver provides a read queue to store sample data + * until it can be read by the application program. The size of the read queue + * and a read-queue "ready" threshold can both be specified by the application + * program. + * + * + * MDACC Accelerometer Driver ioctl() interface + * -------------------------------------------- + + * The following IOCTL commands are defined for this driver. + * + * BMI_MDACC_ACCELEROMETER_SET_CONFIG + * BMI_MDACC_ACCELEROMETER_GET_CONFIG + * BMI_MDACC_ACCELEROMETER_RUN + * BMI_MDACC_ACCELEROMETER_STOP + * + * + * BMI_MDACC_ACCELEROMETER_SET_CONFIG + * + * This ioctl command transfers an mdacc_accel_config structure from the + * application program to the MDACC Accelerometer Driver. + * The third argument to this ioctl system call is the address of the an + * mdacc_accel_config structure. + * + * In the mdacc_accel_config structure, if the delay_mode field is 0, + * the values of the delay and delay resolution fields are ignored and + * and a default delay value of 4 milliseconds is used. + * + * BMI_MDACC_ACCELEROMETER_GET_CONFIG + * + * This ioctl command transfers an mdacc_accel_config structure from the + * MDACC Accelerometer Drive to the application program. + * The third argument to this ioctl system call is the address of the an + * mdacc_accel_config structure. + * + * + * BMI_MDACC_ACCELEROMETER_RUN + * + * This ioctl command will enable the accelerometer data aquistion in the MDACC + * Accelerometer Driver and in the MDACC micro-controller. The behavior of this + * ioctl command can also be invoked by an + + * "ioctl(BMI_MDACC_ACCELEROMETER_SET_CONFIG)" system call with + * "mdacc_accel_config.run = 1". + * + * BMI_MDACC_ACCELEROMETER_STOP + * + * This ioctl command will disable the accelerometer data aquistion in the MDACC + * Accelerometer Driver and in the MDACC micro-controller. The behavior of this + * ioctl command can also be invoked by an + * + * "ioctl(BMI_MDACC_ACCELEROMETER_SET_CONFIG)" system call with + * "mdacc_accel_config.run = 0". + * + * Note that this behavior is also invoked in the close() system call if the + * accelerometer had previously been enabled. + * + * + * MDACC Accelerometer Driver read() interface + * ------------------------------------------- + * + * The read() call for this driver allows the application program to read + * motion detector status only when the status has changed. + * + * Prior to issuing a read() to this driver, the application must enabled the + * motion detector using the "ioctl(BMI_MDACC_MOTION_DETECTOR_RUN)" command. + * + * read parameters: + * + * buffer: address of an array of mdacc_accel_sample structures. + * size: size of the mdacc_accel_sample array in bytes. + * + * Accelerometer blocking read() behavior + * -------------------------------------- + * + * If the accelerometer read queue DOES NOT contain at least "read-threshold" + * number of sample set entries, then the driver will sleep. + * + * If the the driver is awoken by a signal, the driver will return failure (-1) + * and errno will be set to ERESTARTSYS. + + * Otherwise, the requested number of sample sets are removed from the driver + * read queue and copied to user space. The number of bytes transfers will be + * returned to the application. + * + * At the end of the transfer, if the number of read queue entries is below the + * read-threshold, the the driver will then be marked as "not-ready-to-read". + * + * + * Accelerometer non-blocking read() behavior + * ------------------------------------------ + * If the accelerometer read queue DOES NOT contain at least "read-threshold" + * number of sample set entries, then the driver will return failure (-1) and + * errno will be set to EAGAIN. + * + * Otherwise, the requested number of sample sets are removed from the driver + * read queue and copied to user space. The number of bytes transfers will be + * returned to the application. + * + * At the end of the transfer, if the number of read queue entries is below the + * read-threshold, the the driver will then be marked as "not-ready-to-read". + * + * + * Accelerometer select() system call + * ----------------------------------- + * + * This driver supports select() for read only. Select for write and + * exception is not supported. + * + * When a data arrives and is inserted into the read queue and the number of + * queue entries meets or exceeds the read-threshold, the file descriptor + * corresponding to the Accelerometer driver will be marked as + * "ready for read". + * --------------------------------------------------------------------------- + * + * Accelerometer Data Samples + * --------------------------- + * + * The accelerometer analog outputs are sampled with a 10 bit A/D converter + * using 2.9V as a reference. + * + * An accelerometer output of 1.45V corresponds to "0g". + * + * The accelerometer outputs are scaled by the sensitivity settings. + * + * sensitivity scale factor + * --------------------------- + * 0 = 2.5G, 421 mV/G + * 1 = 3.3G, 316 mV/G + * 2 = 6.7G, 158 mV/G + * 3 = 10G, 105 mV/G + * + * The following equation converts an A/D sample to a G-Force value. + * + * G-force = ( ((digital sample) * (X mV/bit)) - 1450 mV) / (scale factor ) + * + * --------------------------------------------------------------------------- + * + * Accelerometer Coordinate System. + * + * + * z axis: perpendicular to PCB. + * y axis: parallel to the long edge of the connector. + * x axis: perpendicular to the short edge of the connector. + * + * + * Top View Side View + * + * +--------------------------------------+ +-+ + * | +x LEDS | | | + * | +------------------------+ | | +-----+ + * | -y | connector underneath | +y | | | + * | +------------------------+ | | +-----+ + * | ____ | | | + * | / \ | | | + * | | | | -z | | +z + * | \ ____ / | | | + * | | | | + * | motion sensor on top | | | + * | | | | + * | -x | | | + * +--------------------------------------+ +-+ + * + * --------------------------------------------------------------------------- + */ +#ifndef LINUX_BMI_BMI_MDACC_H +#define LINUX_BMI_BMI_MDACC_H + +#include + +/* ------------------------- + * + * MDACC Control Driver + * + *-------------------------- + */ +#define BMI_MDACC_CTL_RED_LED_OFF \ + _IOW(BMI_MDACC_IOCTL, 0, char) // Turn off red LED + +#define BMI_MDACC_CTL_RED_LED_ON \ + _IOW(BMI_MDACC_IOCTL, 1, char) // Turn on red LED + +#define BMI_MDACC_CTL_GREEN_LED_OFF \ + _IOW(BMI_MDACC_IOCTL, 2, char) // Turn off green LED + +#define BMI_MDACC_CTL_GREEN_LED_ON \ + _IOW(BMI_MDACC_IOCTL, 3, char) // Turn on green LED + + +/* ------------------------------- + * + * MDACC Motion Detector Driver + * + *-------------------------------- + */ + +/* Status Byte Bit Definitions */ + +#define BMI_MOTION_DETECT_STATUS (1<<3) +#define BMI_MOTION_DETECT_LATCHED_STATUS (1<<2) +#define BMI_MOTION_DETECT_DELTA (1<<1) +#define BMI_MOTION_DETECT_ENABLED (1<<0) + +/* Ioctl Commands */ + +#define BMI_MDACC_MOTION_DETECTOR_GET_STATUS \ + _IOR (BMI_MDACC_IOCTL, 4, char) + +#define BMI_MDACC_MOTION_DETECTOR_RUN \ + _IOW (BMI_MDACC_IOCTL, 5, char) + +#define BMI_MDACC_MOTION_DETECTOR_STOP \ + _IOW (BMI_MDACC_IOCTL, 6, char) + + +/* ------------------------------- + * + * MDACC Accelerometer Driver + * + *-------------------------------- + */ +struct mdacc_accel_sample { + + unsigned short adc_0; //accelerometer channel Z, 10 bit, left justified + //referenced to VCC = 2.9V + + unsigned short adc_1; //accelerometer channel Y, 10 bit, left justified. + //referenced to VCC = 2.9V. + + unsigned short adc_2; //accelerometer channel X, 10 bit, left justified. + //referenced to VCC = 2.9 V. +}; + + +struct mdacc_accel_config { + + int read_queue_size; // number of 6-byte sample sets. + + int read_queue_threshold; // number of 6-byte sample sets to queue + // before ready. + + unsigned short delay; // timer ticks between the start of 2 + // sucessive sample sets. + + unsigned char delay_resolution; // timer tick resolution + // 1 = 1 usec, + // 2 = 8 usec, + // 3 = 64 usec, + // 4 = 256 usec, + // 5 = 1024 usec + + unsigned char delay_mode; //0 = default delay = 5 millisecond, + // ignore delay and delay_resolution + //1 = configured delay + + unsigned char run; //0 = sampling disabled + //1 = sampling enabled + + unsigned char sensitivity; // 0 = 2.5G, 421 mV/G + // 1 = 3.3G, 316 mV/G + // 2 = 6.7G, 158 mV/G + // 3 = 10G, 105 mV/G + +}; + + + +#define BMI_MDACC_ACCELEROMETER_SET_CONFIG \ + _IOW (BMI_MDACC_IOCTL, 7, struct mdacc_accel_config) + +#define BMI_MDACC_ACCELEROMETER_GET_CONFIG \ + _IOR (BMI_MDACC_IOCTL, 8, struct mdacc_accel_config) + + +#define BMI_MDACC_ACCELEROMETER_RUN \ + _IOW (BMI_MDACC_IOCTL, 9, char) + +#define BMI_MDACC_ACCELEROMETER_STOP \ + _IOW (BMI_MDACC_IOCTL, 10, char) + +#define BMI_MDACC_LAST_USED (10) +#endif --- /dev/null +++ git/include/linux/bmi/bmi_projector.h @@ -0,0 +1,33 @@ +/* + * File: include/linux/bmi/bmi_projector.h + * Author: Suresh Rao + * + * This is the application header file for the BMI bus projector plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_PROJECTOR_H +#define BMI_PROJECTOR_H + +#include +#include + +// IOCTL commands for BMI PROJECTOR driver +#define BMI_PROJECTOR_ON _IOW(BMI_PROJECTOR_IOCTL, 0x1, __u32) // turn on projector +#define BMI_PROJECTOR_MODE _IOW(BMI_PROJECTOR_IOCTL, 0x2, __u32) // turn on projector +#define BMI_PROJECTOR_OFF _IOW(BMI_PROJECTOR_IOCTL, 0x3, __u32) // turn off projector +#define BMI_PROJECTOR_BATTERY _IOW(BMI_PROJECTOR_IOCTL, 0x4, __u32) // Battery charger on to bug from projector + +// IOCTL commands for Encoder control +#define BMI_PROJECTOR_HUE _IOW(BMI_PROJECTOR_IOCTL, 0x5, __u32) // Hue control in Encoder +#define BMI_PROJECTOR_SATURATION _IOW(BMI_PROJECTOR_IOCTL, 0x6, __u32) // Saturation control in Encoder +#define BMI_PROJECTOR_BRIGHTNESS _IOW(BMI_PROJECTOR_IOCTL, 0x7, __u32) // Brightness control in Encoder +#define BMI_PROJECTOR_SHARPNESS _IOW(BMI_PROJECTOR_IOCTL, 0x8, __u32) // Sharpness control in Encoder +#define BMI_PROJECTOR_CONTRAST _IOW(BMI_PROJECTOR_IOCTL, 0x9, __u32) // Contrast control in Encoder + +/* BMI_PROJECTOR_MODE settings */ +#define PROJECTOR_ECONOMY_MODE 0x0 +#define PROJECTOR_BRIGHT_MODE 0x1 + +#endif /* BMI_PROJECTOR_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_sensor.h @@ -0,0 +1,673 @@ +/* + * File: include/linux/bmi/bmi_sensor.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus sensor plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_SENSOR_H +#define BMI_SENSOR_H + +#include +#include + +// GPIO +#define SENSOR_GPIO_RED_LED 3 // output +#define SENSOR_GPIO_GREEN_LED 2 // output +#define SENSOR_GPIO_PDOUT 1 // input - aproximity detector state +#define SENSOR_GPIO_MOT_DET 0 // input - motion detector state + +#define SENSOR_GPIO_LED_ON 0 +#define SENSOR_GPIO_LED_OFF 1 + +// I2C +// I2C Slave Addresses +#define BMI_MEE_I2C_ADDRESS 0x51 // 7-bit address - Module specific EEPROM +#define BMI_IOX_I2C_ADDRESS 0x74 // 7-bit address - 2 banks I2C IO expander +#define BMI_ADC_I2C_ADDRESS 0x48 // 7-bit address - ADC - humidity/acompass/sound/alight/aproximity +#define BMI_PL_I2C_ADDRESS 0x44 // 7-bit address - digital proximity/light +#define BMI_DLIGHT_I2C_ADDRESS 0x44 // 7-bit address - digital light +#define BMI_TEMP_I2C_ADDRESS 0x4C // 7-bit address - temperature +#define BMI_ACCEL_I2C_ADDRESS 0x1D // 7-bit address - accelerometer +#define BMI_DCOMP_I2C_ADDRESS 0x1C // 7-bit address - digital compass + +// I2C IOX register addresses +#define IOX_INPUT0_REG 0x0 +#define IOX_INPUT1_REG 0x1 +#define IOX_OUTPUT0_REG 0x2 +#define IOX_OUTPUT1_REG 0x3 +#define IOX_POLARITY0_REG 0x4 +#define IOX_POLARITY1_REG 0x5 +#define IOX_CONTROL0_REG 0x6 +#define IOX_CONTROL1_REG 0x7 + +// IOX bit definitions +// bank 0 +#define SENSOR_IOX_ACC_INT1 0 // Input - Accelerometer interrupt 1 +#define SENSOR_IOX_ACC_INT2 1 // Input - Accelerometer interrupt 2 +#define SENSOR_IOX_USB_FL_N 2 // Input - USB power interrupt +#define SENSOR_IOX_USB_EN 3 // Output - USB power enable +#define SENSOR_IOX_HUM_EN 4 // Output - Humidity sensor power enable +#define SENSOR_IOX_MOT_DET 5 // Input - Motion Detector interrupt +#define SENSOR_IOX_MOT_EN 6 // Output - Motion Detector interrupt enable +#define SENSOR_IOX_COMP_RS_N 7 // Output - A/D Compass Reset (see Honeywell AN213) +// bank 1 +#define SENSOR_IOX_PROX_RST_N 0 // Output - Analog Proximity sensor reset +#define SENSOR_IOX_PROX_EN_N 1 // Output - Analog Proximity sensor enable +#define SENSOR_IOX_PROX_OUT 2 // Input - Analog Proximity sensor output +#define SENSOR_IOX_S_PK_CLR_N 3 // Output - Sound peak detector clear +#define SENSOR_IOX_TEMP_INT 4 // Input - Termperature interrupt +#define SENSOR_IOX_PL_INT 5 // Input - Proximity/Light interrupt +#define SENSOR_IOX_MIC_EN 6 // Output - Sound power enanle +#define SENSOR_IOX_DCOMP_INT 7 // Input - Digital Compass Interrupt + +// EEPROM contents +struct sensor_eeprom_raw +{ + __u8 xsf_msb; /* byte 0x00 */ // analog and digital compass + __u8 xsf_lsb; /* byte 0x01 */ // analog and digital compass + __u8 ysf_msb; /* byte 0x02 */ // analog and digital compass + __u8 ysf_lsb; /* byte 0x03 */ // analog and digital compass + __u8 zsf_msb; /* byte 0x04 */ // analog and digital compass + __u8 zsf_lsb; /* byte 0x05 */ // analog and digital compass + __u8 xoff_msb; /* byte 0x06 */ // analog and digital compass + __u8 xoff_lsb; /* byte 0x07 */ // analog and digital compass + __u8 yoff_msb; /* byte 0x08 */ // analog and digital compass + __u8 yoff_lsb; /* byte 0x09 */ // analog and digital compass + __u8 zoff_msb; /* byte 0x0A */ // analog and digital compass + __u8 zoff_lsb; /* byte 0x0B */ // analog and digital compass + __u8 xdac; /* byte 0x0C */ // digital compass + __u8 ydac; /* byte 0x0D */ // digital compass + __u8 zdac; /* byte 0x0E */ // digital compass + __u8 adc_present; /* byte 0x0F - 0x1 == present */ // TI/Burr-Brown ADS7828 + __u8 humidity_present; /* byte 0x10 - 0x1 == present */ // Honeywell HIH3030 + __u8 acompass_present; /* byte 0x11 - 0x1 == present */ // Honeywell HMC6042/HMC1041Z + __u8 light_proximity_present; /* byte 0x12 - 0x1 == present */ // Intersil ISL29018 + __u8 sound_present; /* byte 0x13 - 0x1 == present */ // discrete components + __u8 temperature_present; /* byte 0x14 - 0x1 == present */ // National LM95235 + __u8 motion_present; /* byte 0x15 - 0x1 == present */ // Panasonic AMN44121 + __u8 accel_present; /* byte 0x16 - 0x1 == present */ // Analog Devices ADXL345 + __u8 dcompass_present; /* byte 0x17 - 0x1 == present */ // AsahiKASEI AK8973 + __u8 aproximity_present; /* byte 0x18 - 0x1 == present */ // Avago APDS-9700 + __u8 alight_present; /* byte 0x19 - 0x1 == present */ // Avago APDS-9002 + __u8 dlight_present; /* byte 0x1A - 0x1 == present */ // Intersil ISL29003 + __u8 acc302_present; /* byte 0x1B - 0x1 == present */ // ST LIS302DL +}; +#define SENSOR_DEVICE_NOT_PRESENT (0x0) +#define SENSOR_DEVICE_PRESENT (0x1) +#define SENSOR_EE_SF_START (0x00) +#define SENSOR_EE_OFF_START (0x06) +#define SENSOR_EE_XDAC (0x0C) +#define SENSOR_EE_YDAC (0x0D) +#define SENSOR_EE_ZDAC (0x0E) +#define SENSOR_PRESENT_START (0x0F) +#define SENSOR_PRESENT_END (0x1B) + +struct sensor_comp_cal +{ + unsigned int xsf; + unsigned int ysf; + unsigned int zsf; + unsigned int xoff; + unsigned int yoff; + unsigned int zoff; +}; + +struct sensor_comp_dac +{ + unsigned char xdac; + unsigned char ydac; + unsigned char zdac; +}; + +// ADC (ADS7828) - humidity/acompass/sound/alight/aproximity +// command write +#define SENSOR_ADC_D10P (0x00 << 4) // positive diff - ch0 & ch1 +#define SENSOR_ADC_D23P (0x01 << 4) // positive diff - ch2 & ch3 +#define SENSOR_ADC_D45P (0x02 << 4) // positive diff - ch4 & ch5 +#define SENSOR_ADC_D67P (0x03 << 4) // positive diff - ch6 & ch7 +#define SENSOR_ADC_D10N (0x04 << 4) // negative diff - ch0 & ch1 +#define SENSOR_ADC_D23N (0x05 << 4) // negative diff - ch2 & ch3 +#define SENSOR_ADC_D45N (0x06 << 4) // negative diff - ch4 & ch5 +#define SENSOR_ADC_D67N (0x07 << 4) // negative diff - ch6 & ch7 +#define SENSOR_ADC_CH0 (0x08 << 4) // single ended ch0 +#define SENSOR_ADC_CH2 (0x09 << 4) // single ended ch2 +#define SENSOR_ADC_CH4 (0x0A << 4) // single ended ch4 +#define SENSOR_ADC_CH6 (0x0B << 4) // single ended ch6 +#define SENSOR_ADC_CH1 (0x0C << 4) // single ended ch1 +#define SENSOR_ADC_CH3 (0x0D << 4) // single ended ch3 +#define SENSOR_ADC_CH5 (0x0E << 4) // single ended ch5 +#define SENSOR_ADC_CH7 (0x0F << 4) // single ended ch7 +#define SENSOR_ADC_PD_OFF (0x00 << 2) // full power down +#define SENSOR_ADC_PD_IR (0x01 << 2) // power down internal reference +#define SENSOR_ADC_PD_ADC (0x02 << 2) // power down ADC +#define SENSOR_ADC_PD_ON (0x03 << 2) // power up ADC +// data read +#define SENSOR_ADC_DATA_MSB (0x0F) +#define SENSOR_ADC_DATA_LSB (0xFF) + +// ADC mapping +#define SENSOR_ADC_SOUND_PEAK SENSOR_ADC_CH7 +#define SENSOR_ADC_SOUND_AVG SENSOR_ADC_CH6 +#define SENSOR_ADC_APROXIMITY SENSOR_ADC_CH5 // Analog proximity +#define SENSOR_ADC_HUMIDITY SENSOR_ADC_CH4 +#define SENSOR_ADC_LIGHT SENSOR_ADC_CH3 // Analog light +#define SENSOR_ADC_ACOMPASS_Z SENSOR_ADC_CH2 // Analog compass +#define SENSOR_ADC_ACOMPASS_Y SENSOR_ADC_CH1 // Analog compass +#define SENSOR_ADC_ACOMPASS_X SENSOR_ADC_CH0 // Analog compass + +// Light/Proximity +#define SENSOR_PL_CMD1 (0x00) // command I + #define SENSOR_PL_CMD1_PD (0x00 << 5) // power down + #define SENSOR_PL_CMD1_ALS_1X (0x01 << 5) // ALS once + #define SENSOR_PL_CMD1_IR_1X (0x02 << 5) // IR once + #define SENSOR_PL_CMD1_PROX_1X (0x03 << 5) // Proximity once + #define SENSOR_PL_CMD1_ALS_CONT (0x05 << 5) // ALS continuous + #define SENSOR_PL_CMD1_IR_CONT (0x06 << 5) // IR continuous + #define SENSOR_PL_CMD1_PROX_CONT (0x07 << 5) // Proximity continuous + #define SENSOR_PL_CMD1_INT_TIMING (0x00) // Proximity continuous + #define SENSOR_PL_CMD1_EXT_TIMING (0x10) // Proximity continuous + #define SENSOR_PL_CMD1_DATA_ADC (0x00) // data is ADC value + #define SENSOR_PL_CMD1_DATA_TIMING (0x08) // data is ADC value + #define SENSOR_PL_CMD1_INT_STAT (0x04) // interrupt status + #define SENSOR_PL_CMD1_INT_1MS (0x00) // interrupt persist = 1 ms + #define SENSOR_PL_CMD1_INT_4MS (0x01) // interrupt persist = 4 ms + #define SENSOR_PL_CMD1_INT_8MS (0x02) // interrupt persist = 8 ms + #define SENSOR_PL_CMD1_INT_16MS (0x03) // interrupt persist = 16 ms +#define SENSOR_PL_CMD2 (0x01) // command II + #define SENSOR_PL_CMD2_IR_LED_A (0x00) // sense IR from LED and ambient + #define SENSOR_PL_CMD2_IR_LED (0x80) // sense IR from LED only + #define SENSOR_PL_CMD2_MOD_DC (0x00) // IR LED modulation = DC + #define SENSOR_PL_CMD2_MOD_327K (0x40) // IR LED modulation = 327.7 kHz + #define SENSOR_PL_CMD2_DRIVE_12M (0x00 << 4) // IR drive current = 12.5 mA + #define SENSOR_PL_CMD2_DRIVE_25M (0x01 << 4) // IR drive current = 25 mA + #define SENSOR_PL_CMD2_DRIVE_50M (0x02 << 4) // IR drive current = 50 mA + #define SENSOR_PL_CMD2_DRIVE_100M (0x03 << 4) // IR drive current = 100 mA + #define SENSOR_PL_CMD2_ADC_RES_16 (0x00 << 2) // ADC resolution = 16 bits + #define SENSOR_PL_CMD2_ADC_RES_12 (0x01 << 2) // ADC resolution = 12 bits + #define SENSOR_PL_CMD2_ADC_RES_8 (0x02 << 2) // ADC resolution = 8 bits + #define SENSOR_PL_CMD2_ADC_RES_4 (0x03 << 2) // ADC resolution = 4 bits + #define SENSOR_PL_CMD2_ALS_RNG_1 (0x00) // ALS sensing = 1000 LUX + #define SENSOR_PL_CMD2_ALS_RNG_4 (0x01) // ALS sensing = 4000 LUX + #define SENSOR_PL_CMD2_ALS_RNG_16 (0x02) // ALS sensing = 16000 LUX + #define SENSOR_PL_CMD2_ALS_RNG_64 (0x03) // ALS sensing = 64000 LUX +#define SENSOR_PL_DATA_LSB (0x02) // Data +#define SENSOR_PL_DATA_MSB (0x03) // Data +#define SENSOR_PL_INT_LT_LSB (0x04) // Low interrupt threshold LSB +#define SENSOR_PL_INT_LT_MSB (0x05) // Low interrupt threshold MSB +#define SENSOR_PL_INT_HT_LSB (0x06) // High interrupt threshold LSB +#define SENSOR_PL_INT_HT_MSB (0x07) // High interrupt threshold MSB +#define SENSOR_PL_EXT_SYNC (0x80) // write address to restart ADC integration + +struct sensor_pl_rw { // see the datasheet + unsigned char cmd1; + unsigned char cmd2; + unsigned char dl; + unsigned char dm; + unsigned char int_lt_lsb; + unsigned char int_lt_msb; + unsigned char int_ht_lsb; + unsigned char int_ht_msb; +}; + +// Digital Light +#define SENSOR_DL_CMD (0x00) // command + #define SENSOR_DL_CMD_ADC_EN (0x80) // enable ADC core + #define SENSOR_DL_CMD_ADC_DIS (0x00) // disable ADC core + #define SENSOR_DL_CMD_PD (0x40) // power down + #define SENSOR_DL_CMD_EXT_SYNC (0x20) // external sync + #define SENSOR_DL_CMD_MODE_D1 (0x00) // ADC work mode = Diode 1, 16 bits + #define SENSOR_DL_CMD_MODE_D2 (0x04) // ADC work mode = Diode 2, 16 bits + #define SENSOR_DL_CMD_MODE_DIFF (0x08) // ADC work mode = I1-I2, 15 bits + #define SENSOR_DL_CMD_W16 (0x00) // 2^16 cycles + #define SENSOR_DL_CMD_W12 (0x01) // 2^12 cycles + #define SENSOR_DL_CMD_W8 (0x02) // 2^8 cycles + #define SENSOR_DL_CMD_W4 (0x03) // 2^4 cycles +#define SENSOR_DL_CONT (0x01) // control + #define SENSOR_DL_CONT_INT (0x20) // interrupt status + #define SENSOR_DL_G1 (0x00) // gain < 1000 LUX + #define SENSOR_DL_G4 (0x04) // gain < 4000 LUX + #define SENSOR_DL_G16 (0x08) // gain < 16000 LUX + #define SENSOR_DL_G64 (0x0C) // gain < 64000 LUX + #define SENSOR_DL_IP1 (0x00) // interrupt persistence = 1 cycle + #define SENSOR_DL_IP4 (0x01) // interrupt persistence = 4 cycle + #define SENSOR_DL_IP8 (0x02) // interrupt persistence = 8 cycle + #define SENSOR_DL_IP16 (0x03) // interrupt persistence = 16 cycle +#define SENSOR_DL_INT_THI (0x02) // +#define SENSOR_DL_INT_TLO (0x03) // +#define SENSOR_DL_SENSOR_LSB (0x04) // +#define SENSOR_DL_SENSOR_MSB (0x05) // +#define SENSOR_DL_TIMER_LSB (0x06) // +#define SENSOR_DL_TIMER_MSB (0x07) // +#define SENSOR_DL_EXT_SYNC (0x80) // +#define SENSOR_DL_INT_CLR (0x40) // + +struct sensor_dl_rw { // see the datasheet + unsigned char cmd; + unsigned char control; + unsigned char int_thi; + unsigned char int_tlo; + unsigned int sensor_data; +}; + +// Temperature +#define SENSOR_TEMP_LOC_MSB (0x00) // Local temperature MSB +#define SENSOR_TEMP_ROFF_HIGH (0x11) // Remote offset high + // 10-bit plus sign format + #define SENSOR_TEMP_LOC_MSB_10B_SIGN (0x80) // Sign + #define SENSOR_TEMP_LOC_MSB_10B_64 (0x40) + #define SENSOR_TEMP_LOC_MSB_10B_32 (0x20) + #define SENSOR_TEMP_LOC_MSB_10B_16 (0x10) + #define SENSOR_TEMP_LOC_MSB_10B_8 (0x08) + #define SENSOR_TEMP_LOC_MSB_10B_4 (0x04) + #define SENSOR_TEMP_LOC_MSB_10B_2 (0x02) + #define SENSOR_TEMP_LOC_MSB_10B_1 (0x01) +#define SENSOR_TEMP_LOC_LSB (0x30) // Local temperature LSB +#define SENSOR_TEMP_ROFF_LOW (0x12) // Remote offset low + // 10-bit plus sign format + #define SENSOR_TEMP_LOC_LSB_10B_P5 (0x80) + #define SENSOR_TEMP_LOC_LSB_10B_P25 (0x40) + #define SENSOR_TEMP_LOC_LSB_10B_P125 (0x20) +#define SENSOR_TEMP_REM_MSB (0x01) // Remote temperature MSB + // 12-bit plus sign format + #define SENSOR_TEMP_REM_MSB_12B_SIGN (0x80) // Sign + #define SENSOR_TEMP_REM_MSB_12B_64 (0x40) + #define SENSOR_TEMP_REM_MSB_12B_32 (0x20) + #define SENSOR_TEMP_REM_MSB_12B_16 (0x10) + #define SENSOR_TEMP_REM_MSB_12B_8 (0x08) + #define SENSOR_TEMP_REM_MSB_12B_4 (0x04) + #define SENSOR_TEMP_REM_MSB_12B_2 (0x02) + #define SENSOR_TEMP_REM_MSB_12B_1 (0x01) +#define SENSOR_TEMP_REM_LSB (0x10) // Remote temperature LSB + // 12-bit plus sign format with filter off + #define SENSOR_TEMP_REM_LSB_12B_P5 (0x80) + #define SENSOR_TEMP_REM_LSB_12B_P25 (0x40) + #define SENSOR_TEMP_REM_LSB_12B_P125 (0x20) + // 12-bit plus sign format with filter on + #define SENSOR_TEMP_REM_LSB_12B_P0625 (0x10) + #define SENSOR_TEMP_REM_LSB_12B_P03125 (0x04) +#define SENSOR_TEMP_UREM_MSB (0x31) // Remote unsigned temperature MSB + // 13-bit usigned format + #define SENSOR_TEMP_UREM_MSB_12B_128 (0x80) + #define SENSOR_TEMP_UREM_MSB_12B_64 (0x40) + #define SENSOR_TEMP_UREM_MSB_12B_32 (0x20) + #define SENSOR_TEMP_UREM_MSB_12B_16 (0x10) + #define SENSOR_TEMP_UREM_MSB_12B_8 (0x08) + #define SENSOR_TEMP_UREM_MSB_12B_4 (0x04) + #define SENSOR_TEMP_UREM_MSB_12B_2 (0x02) + #define SENSOR_TEMP_UREM_MSB_12B_1 (0x01) +#define SENSOR_TEMP_UREM_LSB (0x32) // Remote unsigned temperature LSB + // 13-bit usigned format with filter off + #define SENSOR_TEMP_UREM_LSB_12B_P5 (0x80) + #define SENSOR_TEMP_UREM_LSB_12B_P25 (0x40) + #define SENSOR_TEMP_UREM_LSB_12B_P125 (0x20) + // 13-bit usigned format with filter on + #define SENSOR_TEMP_UREM_LSB_12B_P0625 (0x10) + #define SENSOR_TEMP_UREM_LSB_12B_P03125 (0x04) +#define SENSOR_TEMP_CONF2 (0xBF) // Diode configuration + #define SENSOR_TEMP_CONF2_A0 (0x40) // A0 pin function + #define SENSOR_TEMP_CONF2_OS (0x00) // A0 pin function + #define SENSOR_TEMP_CONF2_OS_ON (0x20) // OS fault mask on + #define SENSOR_TEMP_CONF2_OS_OFF (0x00) // OS fault mask off + #define SENSOR_TEMP_CONF2_TCRIT_ON (0x10) // TCRIT fault mask on + #define SENSOR_TEMP_CONF2_TCRIT_OFF (0x00) // TCRIT fault mask off + #define SENSOR_TEMP_CONF2_DMOD1 (0x00) // Diode model 1 + #define SENSOR_TEMP_CONF2_DMOD2 (0x08) // Diode model 1 + #define SENSOR_TEMP_CONF2_FILT_OFF (0x00 << 1) // Filter off + #define SENSOR_TEMP_CONF2_FILT_ON (0x03 << 1) // Filter on +#define SENSOR_TEMP_CONF1_RD (0x03) // General configuration +#define SENSOR_TEMP_CONF1_WR (0x09) // General configuration + #define SENSOR_TEMP_CONF1_RUN (0x00) // Active/Converting + #define SENSOR_TEMP_CONF1_STOP (0x40) // Standby + #define SENSOR_TEMP_R_TCRIT_MASK_OFF (0x00) // Remote TCRIT + #define SENSOR_TEMP_R_TCRIT_MASK_ON (0x10) // Remote TCRIT + #define SENSOR_TEMP_R_OS_MASK_OFF (0x00) // Remote OS + #define SENSOR_TEMP_R_OS_MASK_ON (0x08) // Remote OS + #define SENSOR_TEMP_L_TCRIT_MASK_OFF (0x00) // Local TCRIT + #define SENSOR_TEMP_L_TCRIT_MASK_ON (0x04) // Local TCRIT + #define SENSOR_TEMP_L_OS_MASK_OFF (0x00) // Local OS + #define SENSOR_TEMP_L_OS_MASK_ON (0x02) // Local OS +#define SENSOR_TEMP_CONV_RD (0x04) // Conversion rate +#define SENSOR_TEMP_CONV_WR (0x0A) // Conversion rate + #define SENSOR_TEMP_CONV_CONT (0x00) // Continuous + #define SENSOR_TEMP_CONV_P364 (0x01) // .364 seconds + #define SENSOR_TEMP_CONV_1 (0x02) // 1 second + #define SENSOR_TEMP_CONV_2P5 (0x03) // 2.5 seconds +#define SENSOR_TEMP_ONE_SHOT (0x0F) // Remote offset low +#define SENSOR_TEMP_STAT1 (0x02) // Status 1 + #define SENSOR_TEMP_STAT1_BUSY (0x80) // Converting + #define SENSOR_TEMP_STAT1_ROS (0x10) // Remote OS + #define SENSOR_TEMP_STAT1_DFAULT (0x04) // Diode Fault + #define SENSOR_TEMP_STAT1_RTCRIT (0x02) // Remote TCRIT + #define SENSOR_TEMP_STAT1_LOC (0x01) // Local OS & TCRIT +#define SENSOR_TEMP_STAT2 (0x33) // Status 2 + #define SENSOR_TEMP_STAT2_NR (0x80) // Not Ready - 30 ms power-up + #define SENSOR_TEMP_STAT2_TT (0x40) // TruTherm Diode detected +#define SENSOR_TEMP_REM_OS_LIM_RD (0x07) // Remote OS limit +#define SENSOR_TEMP_REM_OS_LIM_WR (0x0D) // Remote OS limit +#define SENSOR_TEMP_LOC_OS_LIM (0x20) // Local OS limit +#define SENSOR_TEMP_REM_TCRIT_LIM (0x19) // Remote T_Crit limit + #define SENSOR_TEMP_LIM_128 (0x80) + #define SENSOR_TEMP_LIM_64 (0x40) + #define SENSOR_TEMP_LIM_32 (0x20) + #define SENSOR_TEMP_LIM_16 (0x10) + #define SENSOR_TEMP_LIM_8 (0x08) + #define SENSOR_TEMP_LIM_4 (0x04) + #define SENSOR_TEMP_LIM_2 (0x02) + #define SENSOR_TEMP_LIM_1 (0x01) +#define SENSOR_TEMP_HYSTERESIS (0x21) // Common hysteresis + #define SENSOR_TEMP_HYS_16 (0x10) + #define SENSOR_TEMP_HYS_8 (0x08) + #define SENSOR_TEMP_HYS_4 (0x04) + #define SENSOR_TEMP_HYS_2 (0x02) + #define SENSOR_TEMP_HYS_1 (0x01) +#define SENSOR_TEMP_MAN_ID (0xFE) // Manufacture ID + #define SENSOR_TEMP_MAN_ID_DATA (0x01) // Manufacture ID +#define SENSOR_TEMP_REV_ID (0xFF) // Revision ID + #define SENSOR_TEMP_REV_ID_DATA (0xB1) // Revision ID + +struct sensor_temp_rw { // see the datasheet + unsigned char address; + unsigned char d1; +}; + +// accelerometer +// ADXL345 +#define SENSOR_ACC_ID (0x00) // Device ID + #define SENSOR_ACC_ID_DATA (0xE5) // Device ID +#define SENSOR_ACC_TT (0x1D) // Tap threshold (62.5 mg/LSB) +#define SENSOR_ACC_OFSX (0x1E) // X axis offset (15.6 mg/LSB) +#define SENSOR_ACC_OFSY (0x1F) // Y axis offset (15.6 mg/LSB) +#define SENSOR_ACC_OFSZ (0x20) // Z axis offset (15.6 mg/LSB) +#define SENSOR_ACC_DUR (0x21) // Tap duration (625 us/LSB) +#define SENSOR_ACC_LAT (0x22) // Tap latency (1.25 ms/LSB) +#define SENSOR_ACC_WIN (0x23) // Tap window (1.25 ms/LSB) +#define SENSOR_ACC_TAT (0x24) // Activity threshold (62.5 mg/LSB) +#define SENSOR_ACC_TINAT (0x25) // Inactivity threshold (62.5 mg/LSB) +#define SENSOR_ACC_TIM_INAT (0x26) // Inactivity time (1 s/LSB) +#define SENSOR_ACC_AT_CONTROL (0x27) // Activity/Inactivity control + #define SENSOR_ACC_ATC_DC (0x00) // Active DC coupled + #define SENSOR_ACC_ATC_AC (0x80) // Active AC coupled + #define SENSOR_ACC_ATC_XE (0x40) // Active X enable + #define SENSOR_ACC_YTC_XE (0x20) // Active X enable + #define SENSOR_ACC_ZTC_XE (0x10) // Active X enable + #define SENSOR_ACC_ITC_DC (0x00) // Inactive DC coupled + #define SENSOR_ACC_ITC_AC (0x08) // Inactive AC coupled + #define SENSOR_ACC_ITC_XE (0x04) // Inactive X enable + #define SENSOR_ACC_ITC_YE (0x02) // Inactive Y enable + #define SENSOR_ACC_ITC_ZE (0x01) // Inactive Z enable +#define SENSOR_ACC_T_FF (0x28) // Freefall Threshold (62.5 mg/LSB) +#define SENSOR_ACC_TIM_FF (0x29) // Freefall Time (5 ms/LSB) +#define SENSOR_ACC_TAP_AXES (0x2A) // Tap Axis control + #define SENSOR_ACC_TA_SUPRESS (0x08) // Supress Double Tap + #define SENSOR_ACC_TA_XYZE (0x07) // X,Y,Z Tap Enable + #define SENSOR_ACC_TA_XE (0x04) // X Tap Enable + #define SENSOR_ACC_TA_YE (0x02) // Y Tap Enable + #define SENSOR_ACC_TA_ZE (0x01) // Z Tap Enable +#define SENSOR_ACC_TAP_STAT (0x2B) // Tap Status + #define SENSOR_ACC_TS_XA (0x40) // X Activity + #define SENSOR_ACC_TS_YA (0x20) // Y Activity + #define SENSOR_ACC_TS_ZA (0x10) // Z Activity + #define SENSOR_ACC_TS_XT (0x04) // X Tap + #define SENSOR_ACC_TS_YT (0x02) // Y Tap + #define SENSOR_ACC_TS_ZT (0x01) // Z Tap +#define SENSOR_ACC_RATE (0x2C) // Data Rate control + #define SENSOR_ACC_RATE_LP (0x10) // Low Power Mode + #define SENSOR_ACC_RC_3200_1600 (0x0F) // _OUTPUT-DATA_BANDWIDTH + #define SENSOR_ACC_RC_1600_800 (0x0E) + #define SENSOR_ACC_RC_800_400 (0x0D) + #define SENSOR_ACC_RC_400_200 (0x0C) + #define SENSOR_ACC_RC_200_100 (0x0B) + #define SENSOR_ACC_RC_100_50 (0x0A) + #define SENSOR_ACC_RC_50_25 (0x09) + #define SENSOR_ACC_RC_25_12P5 (0x08) + #define SENSOR_ACC_RC_12P5_6P25 (0x07) + #define SENSOR_ACC_RC_6P25_3P125 (0x06) + #define SENSOR_ACC_RC_3P125_1P563 (0x05) + #define SENSOR_ACC_RC_1P563_P782 (0x04) + #define SENSOR_ACC_RC_P782_P39 (0x03) + #define SENSOR_ACC_RC_P39_P195 (0x02) + #define SENSOR_ACC_RC_P195_P098 (0x01) + #define SENSOR_ACC_RC_P098_P048 (0x00) +#define SENSOR_ACC_POWER (0x2D) // Power control + #define SENSOR_ACC_P_LINK (0x20) // Activity/Inactivity Link mode + #define SENSOR_ACC_P_APM (0x10) // Auto Low Power + #define SENSOR_ACC_P_SM (0x00) // Standby + #define SENSOR_ACC_P_NORM (0x08) // Powered Up + #define SENSOR_ACC_P_SLEEP_NORM (0x00) // Not Sleep + #define SENSOR_ACC_P_SLEEP (0x04) // Sleep + #define SENSOR_ACC_P_W8 (0x00) // wakeup Frequency = 8 Hz + #define SENSOR_ACC_P_W4 (0x01) // wakeup Frequency = 4 Hz + #define SENSOR_ACC_P_W2 (0x02) // wakeup Frequency = 2 Hz + #define SENSOR_ACC_P_W1 (0x03) // wakeup Frequency = 1 Hz +#define SENSOR_ACC_IE (0x2E) // Interrupt Enable +#define SENSOR_ACC_IM (0x2F) // Interrupt Map +#define SENSOR_ACC_IS (0x30) // Interrupt Source + #define SENSOR_ACC_I_DR (0x80) // Data Ready + #define SENSOR_ACC_I_ST (0x40) // Single Tap + #define SENSOR_ACC_I_DT (0x20) // Double Tap + #define SENSOR_ACC_I_A (0x10) // Activity + #define SENSOR_ACC_I_I (0x08) // Inactivity + #define SENSOR_ACC_I_FF (0x04) // Freefall + #define SENSOR_ACC_I_WM (0x02) // Watermark + #define SENSOR_ACC_I_OR (0x01) // Overrun +#define SENSOR_ACC_DF (0x31) // Data Format + #define SENSOR_ACC_DF_SELF_TEST (0x80) // Self Test + #define SENSOR_ACC_DF_SPI_MODE4 (0x00) // SPI 4-Wire + #define SENSOR_ACC_DF_SPI_MODE3 (0x40) // SPI 3-Wire + #define SENSOR_ACC_DF_INT_INVERT (0x20) // Interrupt Active Low + #define SENSOR_ACC_DF_LENGTH (0x08) // 13-bit, 16g Enable + #define SENSOR_ACC_DF_POS (0x04) // MSB Left Justified + #define SENSOR_ACC_DF_R2 (0x00) // 2g + #define SENSOR_ACC_DF_R4 (0x01) // 4g + #define SENSOR_ACC_DF_R8 (0x02) // 8g + #define SENSOR_ACC_DF_R16 (0x03) // 16g +#define SENSOR_ACC_DX0 (0x32) // Data X axis 0 (LSB) +#define SENSOR_ACC_DX1 (0x33) // Data X axis 1 (MSB) +#define SENSOR_ACC_DY0 (0x34) // Data Y axis 0 (LSB) +#define SENSOR_ACC_DY1 (0x35) // Data Y axis 1 (MSB) +#define SENSOR_ACC_DZ0 (0x36) // Data Z axis 0 (LSB) +#define SENSOR_ACC_DZ1 (0x37) // Data Z axis 1 (MSB) +#define SENSOR_ACC_FC (0x38) // FIFO Control + #define SENSOR_ACC_FC_BYP (0x00 << 6) // Bypass + #define SENSOR_ACC_FC_HOLD (0x01 << 6) // Hold after 32 + #define SENSOR_ACC_FC_OF (0x02 << 6) // Discard after 32 + #define SENSOR_ACC_FC_TRIG (0x03 << 6) // Hold on TRIGGER + #define SENSOR_ACC_FC_TRIG1 (0x00) // TRIGGER = INT1 + #define SENSOR_ACC_FC_TRIG2 (0x20) // TRIGGER = INT2 + #define SENSOR_ACC_FC_SAMP(x) (x) // See ADXL345 datasheet +#define SENSOR_ACC_FS (0x39) // FIFO Status + #define SENSOR_ACC_FS_TRIG (0x80) // TRIGGER occurred + #define SENSOR_ACC_FS_ENTRIES_MSK (0x1F) // See ADXL345 datasheet + +// ST LIS302DL +#define SENSOR_A3_WAI (0x0F) // Device ID + #define SENSOR_A3_WAI_ID (0x3B) // Device ID +#define SENSOR_A3_CTRL1 (0x20) // Control Register + #define SENSOR_A3_CTRL1_DR100 (0x00) // sample data rate = 100 Hz + #define SENSOR_A3_CTRL1_DR400 (0x80) // sample data rate = 400 Hz + #define SENSOR_A3_CTRL1_PD (0x00) // power down + #define SENSOR_A3_CTRL1_PU (0x40) // power up + #define SENSOR_A3_CTRL1_FS (0x20) // See data sheet + #define SENSOR_A3_CTRL1_STP (0x10) // See data sheet + #define SENSOR_A3_CTRL1_STM (0x08) // See data sheet + #define SENSOR_A3_CTRL1_XYZEN (0x07) // X,Y,Z axis enable + #define SENSOR_A3_CTRL1_ZEN (0x04) // Z axis enable + #define SENSOR_A3_CTRL1_YEN (0x02) // Y axis enable + #define SENSOR_A3_CTRL1_XEN (0x01) // X axis enable +#define SENSOR_A3_CTRL2 (0x21) // Control Register + #define SENSOR_A3_CTRL2_SIM (0x80) // SPI mode + #define SENSOR_A3_CTRL2_BOOT (0x40) // copy calibration from FLASH + #define SENSOR_A3_CTRL2_FILT_OFF (0x00) // internal filter bypassed + #define SENSOR_A3_CTRL2_FILT_ON (0x10) // internal filter enabled + #define SENSOR_A3_CTRL2_F2 (0x08) // WU2 filter enable + #define SENSOR_A3_CTRL2_F1 (0x04) // WU1 filter enable + #define SENSOR_A3_CTRL2_COEFF(x) (x & 0x3) // See data sheet +#define SENSOR_A3_CTRL3 (0x22) // Control Register + #define SENSOR_A3_CTRL3_IH (0x00) // Interrupt active high + #define SENSOR_A3_CTRL3_IL (0x80) // Interrupt active low + #define SENSOR_A3_CTRL3_IPP (0x00) // Interrupt push/pull + #define SENSOR_A3_CTRL3_IOD (0x40) // Interrupt open drain + #define SENSOR_A3_CTRL3_I2C(x) ((x&0x7) << 3) // I2 config - See data sheet + #define SENSOR_A3_CTRL3_I1C(x) ((x&0x7)) // I1 config - See data sheet +#define SENSOR_A3_HPF_RST (0x23) // High Pass Filter Reset - See data sheet +#define SENSOR_A3_STAT (0x27) // Status + #define SENSOR_A3_STAT_ZYXOR (0x80) // ZYX overrun + #define SENSOR_A3_STAT_ZOR (0x40) // Z overrun + #define SENSOR_A3_STAT_YOR (0x20) // Y overrun + #define SENSOR_A3_STAT_XOR (0x10) // X overrun + #define SENSOR_A3_STAT_ZYXDA (0x08) // ZYX data available + #define SENSOR_A3_STAT_ZDA (0x04) // Z data available + #define SENSOR_A3_STAT_YDA (0x02) // Y data available + #define SENSOR_A3_STAT_XDA (0x01) // X data available +#define SENSOR_A3_OUTX (0x29) // X Output +#define SENSOR_A3_OUTY (0x2B) // Y Output +#define SENSOR_A3_OUTZ (0x2D) // Z Output +#define SENSOR_A3_CFG1 (0x30) // Configuration +#define SENSOR_A3_CFG2 (0x34) // Configuration + #define SENSOR_A3_CFG_AOI (0x80) // AND/OR interrupts + #define SENSOR_A3_CFG_LIR (0x40) // latch interrupts into SRC + #define SENSOR_A3_CFG_ZHIE (0x20) // Z high enable + #define SENSOR_A3_CFG_ZLIE (0x10) // Z low enable + #define SENSOR_A3_CFG_YHIE (0x08) // Y high enable + #define SENSOR_A3_CFG_YLIE (0x04) // Y low enable + #define SENSOR_A3_CFG_XHIE (0x02) // X high enable + #define SENSOR_A3_CFG_XLIE (0x01) // X low enable +#define SENSOR_A3_SRC1 (0x31) // Source +#define SENSOR_A3_SRC2 (0x35) // Source + #define SENSOR_A3_SRC_IA (0x40) // interrupt active + #define SENSOR_A3_SRC_ZH (0x20) // Z high + #define SENSOR_A3_SRC_ZL (0x10) // Z low + #define SENSOR_A3_SRC_YH (0x08) // Y high + #define SENSOR_A3_SRC_YL (0x04) // Y low + #define SENSOR_A3_SRC_XH (0x02) // X high + #define SENSOR_A3_SRC_XL (0x01) // X low +#define SENSOR_A3_THS1 (0x32) // Threshold +#define SENSOR_A3_THS2 (0x36) // Threshold + #define SENSOR_A3_THS_DCRM (0x80) // Resetting mode - See data sheet + #define SENSOR_A3_THS_THS(x) (x & 0x7F) // FF/wakeup threshold +#define SENSOR_A3_DUR1 (0x33) // Duration - See data sheet +#define SENSOR_A3_DUR2 (0x37) // Duration - See data sheet +#define SENSOR_A3_CCFG (0x38) // Click Configuration +#define SENSOR_A3_CSRC (0x39) // Click Source + #define SENSOR_A3_CCS_LIR (0x40) // latch interrupt into SRC + #define SENSOR_A3_CCS_DZ (0x20) // double Z enable + #define SENSOR_A3_CCS_SZ (0x10) // single Z enable + #define SENSOR_A3_CCS_DY (0x08) // double Y enable + #define SENSOR_A3_CCS_SY (0x04) // single Y enable + #define SENSOR_A3_CCS_DX (0x02) // double X enable + #define SENSOR_A3_CCS_SX (0x01) // single X enable +#define SENSOR_A3_CTHXY (0x3B) // Click X, Y Threshold + #define SENSOR_A3_CTHYX_Y(x) ((x&0xF) << 4) // Y Threshold + #define SENSOR_A3_CTHYX_X(x) (x&0xF) // X Threshold +#define SENSOR_A3_CTHZ (0x3C) // Click Z Threshold + #define SENSOR_A3_CTHYX_Z(x) (x&0xF) // Z Threshold +#define SENSOR_A3_CTL (0x3D) // Click Time Limit +#define SENSOR_A3_CLAT (0x3E) // Click Latency +#define SENSOR_A3_CWIN (0x3F) // Click Window + +// count always = 1 for LIS302DL +struct sensor_acc_rw { // see the datasheets + unsigned char address; + unsigned int count; // number of bytes to read (1 or 2) + unsigned char data[2]; +}; + +// digital compass +#define SENSOR_DCOMP_ST (0xC0) // Status (RO) + #define SENSOR_DCOMP_ST_INT (0x01) // Interrupt + #define SENSOR_DCOMP_ST_EERW (0x02) // EEPROM R/W +#define SENSOR_DCOMP_TMPS (0xC1) // Temperature(C) = 35+(120-TMPS)/1.6 +#define SENSOR_DCOMP_H1X (0xC2) // X Heading +#define SENSOR_DCOMP_H1Y (0xC3) // Y Heading +#define SENSOR_DCOMP_H1Z (0xC4) // Z Heading +#define SENSOR_DCOMP_MS1 (0xE0) // Mode + #define SENSOR_DCOMP_MS1_SENSOR (0x0) // sensor mode + #define SENSOR_DCOMP_MS1_EEPROM (0x2) // EEPROM R/W + #define SENSOR_DCOMP_MS1_PD (0x3) // power down + #define SENSOR_DCOMP_MS1_EEWEN (0xA8) // EEPROM Write Enable +#define SENSOR_DCOMP_HXDA (0xE1) // X DAC offset - see table 3 in datasheet +#define SENSOR_DCOMP_HYDA (0xE2) // Y DAC offset - see table 3 in datasheet +#define SENSOR_DCOMP_HZDA (0xE3) // Z DAC offset - see table 3 in datasheet +#define SENSOR_DCOMP_HXGA (0xE4) // X gain - see table 4 in datasheet +#define SENSOR_DCOMP_HYGA (0xE5) // Y gain - see table 4 in datasheet +#define SENSOR_DCOMP_HZGA (0xE6) // Z gain - see table 4 in datasheet +#define SENSOR_DCOMP_TS1 (0x5D) // FACTORY TEST - DO NOT USE +#define SENSOR_DCOMP_EE_WRAL1 (0x60) // EE - batch write adress +#define SENSOR_DCOMP_EE_ETS (0x62) // EE - temperature offset +#define SENSOR_DCOMP_EE_EVIR (0x63) // EE - VREF/IREF adjustment +#define SENSOR_DCOMP_EE_EIHE (0x64) // EE - HE drive/OSC +#define SENSOR_DCOMP_EE_ETST (0x65) // EE - test +#define SENSOR_DCOMP_EE_EHXGA (0x66) // EE - X gain adjustment +#define SENSOR_DCOMP_EE_EHYGA (0x67) // EE - Y gain adjustment +#define SENSOR_DCOMP_EE_EHZGA (0x68) // EE - Z gain adjustment + +// generic address/data byte R/W +struct sensor_rw { + unsigned char address; + unsigned char data; +}; + +// Sensor driver ioctl definitions +#define BMI_SENSOR_ON (1) +#define BMI_SENSOR_OFF (0) +#define BMI_SENSOR_RLEDOFF _IOW(BMI_SENSOR_IOCTL, 0x1, unsigned int) // Turn off red LED +#define BMI_SENSOR_RLEDON _IOW(BMI_SENSOR_IOCTL, 0x2, unsigned int) // Turn on red LED +#define BMI_SENSOR_GLEDOFF _IOW(BMI_SENSOR_IOCTL, 0x3, unsigned int) // Turn off green LED +#define BMI_SENSOR_GLEDON _IOW(BMI_SENSOR_IOCTL, 0x4, unsigned int) // Turn on green LED +#define BMI_SENSOR_GETSTAT _IOR(BMI_SENSOR_IOCTL, 0x5, unsigned int *) // Read IOX/GPIO (== GPIO<<16 | IOX1<<8 | IOX0) +#define BMI_SENSOR_ADCWR _IOW(BMI_SENSOR_IOCTL, 0x6, unsigned int) // write ADC +#define BMI_SENSOR_ADCRD _IOR(BMI_SENSOR_IOCTL, 0x7, unsigned int *) // read ADC +#define BMI_SENSOR_HUMRD _IOR(BMI_SENSOR_IOCTL, 0x8, unsigned int *) // read ADC - Humidity sensor +#define BMI_SENSOR_ACOMPRST _IO(BMI_SENSOR_IOCTL, 0x9) // analog Compass reset (toggle off/on) +#define BMI_SENSOR_ACOMPXRD _IOR(BMI_SENSOR_IOCTL, 0xa, unsigned int *) // read ADC - Compass X axis +#define BMI_SENSOR_ACOMPYRD _IOR(BMI_SENSOR_IOCTL, 0xb, unsigned int *) // read ADC - Compass Y axis +#define BMI_SENSOR_ACOMPZRD _IOR(BMI_SENSOR_IOCTL, 0xc, unsigned int *) // read ADC - Compass Z axis +#define BMI_SENSOR_PLWR _IOW(BMI_SENSOR_IOCTL, 0xd, struct sensor_pl_rw *) // write Proximity/Light sensor +#define BMI_SENSOR_PLRD _IOR(BMI_SENSOR_IOCTL, 0xe, struct sensor_pl_rw *) // read Proximity/Light sensor +#define BMI_SENSOR_PL_SYNC _IO(BMI_SENSOR_IOCTL, 0xf) // generate external SYNC for Proximity/Light or Digital Light +#define BMI_SENSOR_PL_IWAIT _IOR(BMI_SENSOR_IOCTL, 0x10, struct sensor_pl_rw *) // wait for Proximity/Light interrupt - application sets up INT configuration +#define BMI_SENSOR_SNDARD _IOR(BMI_SENSOR_IOCTL, 0x11, unsigned int *) // read ADC - Sound Average level +#define BMI_SENSOR_SNDPRD _IOR(BMI_SENSOR_IOCTL, 0x12, unsigned int *) // read ADC - Sound Peak level read/clear +#define BMI_SENSOR_SNDIRD _IOR(BMI_SENSOR_IOCTL, 0x13, unsigned int *) // read ADC - Instantaneous level read/clear +#define BMI_SENSOR_TEMPWR _IOW(BMI_SENSOR_IOCTL, 0x14, struct sensor_temp_rw *) // write Temperature sensor +#define BMI_SENSOR_TEMPRD _IOR(BMI_SENSOR_IOCTL, 0x15, struct sensor_temp_rw *) // read Temperature sensor +#define BMI_SENSOR_TEMPRD_SL _IOR(BMI_SENSOR_IOCTL, 0x16, unsigned int *) // Read signed local +#define BMI_SENSOR_TEMPRD_SR _IOR(BMI_SENSOR_IOCTL, 0x17, unsigned int *) // Read signed remote +#define BMI_SENSOR_TEMPRD_UR _IOR(BMI_SENSOR_IOCTL, 0x18, unsigned int *) // Read unsigned remote +#define BMI_SENSOR_TEMP_IWAIT _IO(BMI_SENSOR_IOCTL, 0x19) // wait for Temperature interrupt - application sets up INT configuration +#define BMI_SENSOR_MOTRD _IOR(BMI_SENSOR_IOCTL, 0x1a, unsigned int *) // read real-time Motion state +#define BMI_SENSOR_MOT_IWAIT _IOR(BMI_SENSOR_IOCTL, 0x1b, unsigned int *) // wait for Motion interrupt +#define BMI_SENSOR_ACCWR _IOW(BMI_SENSOR_IOCTL, 0x1c, struct sensor_acc_rw *) // write Accelerometer +#define BMI_SENSOR_ACCRD _IOR(BMI_SENSOR_IOCTL, 0x1d, struct sensor_acc_rw *) // read Accelerometer +#define BMI_SENSOR_ACCXRD _IOR(BMI_SENSOR_IOCTL, 0x1e, unsigned int *) // read Accelerometer X +#define BMI_SENSOR_ACCYRD _IOR(BMI_SENSOR_IOCTL, 0x1f, unsigned int *) // read Accelerometer Y +#define BMI_SENSOR_ACCZRD _IOR(BMI_SENSOR_IOCTL, 0x20, unsigned int *) // read Accelerometer Z +#define BMI_SENSOR_ACC_I1WAIT _IO(BMI_SENSOR_IOCTL, 0x21) // wait for Accelerometer interrupt 1 - application sets up INT configuration +#define BMI_SENSOR_ACC_I2WAIT _IO(BMI_SENSOR_IOCTL, 0x22) // wait for Accelerometer interrupt 2 - application sets up INT configuration +#define BMI_SENSOR_EEWR _IOW(BMI_SENSOR_IOCTL, 0x23, struct sensor_rw *) // write EEPROM +#define BMI_SENSOR_EERD _IOR(BMI_SENSOR_IOCTL, 0x24, struct sensor_rw *) // read EEPROM +#define BMI_SENSOR_MOT_IE _IOW(BMI_SENSOR_IOCTL, 0x25, unsigned int) // Motion interrupt enable (on = BMI_SENSOR_ON) +#define BMI_SENSOR_USB_IWAIT _IO(BMI_SENSOR_IOCTL, 0x26) // wait for USB power flag interrupt +#define BMI_SENSOR_USB_PWR_EN _IOW(BMI_SENSOR_IOCTL, 0x27, unsigned int) // USB power enable (on = BMI_SENSOR_ON) +#define BMI_SENSOR_HUM_PWR_EN _IOW(BMI_SENSOR_IOCTL, 0x28, unsigned int) // Humidity power enable (on = BMI_SENSOR_ON) +#define BMI_SENSOR_DCOM_RST _IOW(BMI_SENSOR_IOCTL, 0x29, unsigned int) // Digital Compass Reset (on = BMI_SENSOR_ON) +#define BMI_SENSOR_COM_GCAL _IOR(BMI_SENSOR_IOCTL, 0x2a, struct sensor_comp_cal *) // Get compass calibation +#define BMI_SENSOR_COM_SCAL _IOW(BMI_SENSOR_IOCTL, 0x2b, struct sensor_comp_cal *) // Set compass calibation +#define BMI_SENSOR_DCWR _IOW(BMI_SENSOR_IOCTL, 0x2c, struct sensor_rw *) // write digital compass +#define BMI_SENSOR_DCRD _IOR(BMI_SENSOR_IOCTL, 0x2d, struct sensor_rw *) // read digital compass +#define BMI_SENSOR_DC_GDAC _IOR(BMI_SENSOR_IOCTL, 0x2e, struct sensor_comp_dac *) // Get digital compass DAC settings +#define BMI_SENSOR_DC_SDAC _IOW(BMI_SENSOR_IOCTL, 0x2f, struct sensor_comp_dac *) // Set digital compass DAC settings +#define BMI_SENSOR_DC_IWAIT _IO(BMI_SENSOR_IOCTL, 0x30) // wait for digital compass interrupt - application sets up INT configuration +#define BMI_SENSOR_APROX_DUR _IOW(BMI_SENSOR_IOCTL, 0x31, unsigned int) // Analog Proximity LED burst time (in ms 2 <= arg <= 100) +#define BMI_SENSOR_APROXRD _IOR(BMI_SENSOR_IOCTL, 0x32, unsigned int *) // read Analog proximity = (PDOUT << 16) | ADC_DATA +#define BMI_SENSOR_ALIGHTRD _IOR(BMI_SENSOR_IOCTL, 0x33, unsigned int *) // read Analog Light +#define BMI_SENSOR_DLIGHTWR _IOW(BMI_SENSOR_IOCTL, 0x34, struct sensor_dl_rw *) // write Digital Light sensor +#define BMI_SENSOR_DLIGHTRD _IOR(BMI_SENSOR_IOCTL, 0x35, unsigned int) // read Digital Light sensor +#define BMI_SENSOR_DLIGHT_IC _IO(BMI_SENSOR_IOCTL, 0x36) // Digital Light interrupt clear +#define BMI_SENSOR_DLIGHT_IWAIT _IOR(BMI_SENSOR_IOCTL, 0x37, struct sensor_dl_rw *) // wait for Digital Light interrupt - application sets up INT configuration +#define BMI_SENSOR_MIC_EN _IOW(BMI_SENSOR_IOCTL, 0x38, unsigned int) // Sound power enable (on = BMI_SENSOR_ON) + +#endif /* BMI_SENSOR_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_vh.h @@ -0,0 +1,135 @@ +/* + * File: include/linux/bmi/bmi_vh.h + * Author: Peter Giacomini + * + * This is the application header file for the BMI bus voh Hippel plug-in + * module on the MX31 BUG platform. + */ + +#ifndef BMI_VH_H +#define BMI_VH_H + +#include + +// GPIO +#define VH_GPIO_RED_LED 3 // default to input +#define VH_GPIO_GREEN_LED 2 // default to input +#define VH_GPIO_1 1 // default to input +#define VH_GPIO_0 0 // default to input + +#define VH_GPIO_LED_ON 0 +#define VH_GPIO_LED_OFF 1 + +// I2C +// I2C Slave Addresses +#define BMI_IOX_I2C_ADDRESS 0x71 // 7-bit address +#define VH_RDAC_I2C_ADDRESS 0x18 // 7-bit address +#define VH_ADC_I2C_ADDRESS 0x24 // 7-bit address +#define VH_DAC_I2C_ADDRESS 0x58 // 7-bit address + +// I2C IOX register addresses +#define IOX_INPUT_REG 0x0 +#define IOX_OUTPUT_REG 0x1 +#define IOX_POLARITY_REG 0x2 +#define IOX_CONTROL 0x3 + +#define VH_IOX_USB_FLG_N 7 // Input - H=normal, L=fault +#define VH_IOX_USB_VEN 6 // output - H=power on, L=power off +#define VH_IOX_B5 5 // set to output driven high to prevent interrupts +#define VH_IOX_B4 4 // set to output driven high to prevent interrupts +#define VH_IOX_B3 3 // set to output driven high to prevent interrupts +#define VH_IOX_B2 2 // set to output driven high to prevent interrupts +#define VH_IOX_B1 1 // set to output driven high to prevent interrupts +#define VH_IOX_B0 0 // set to output driven high to prevent interrupts + +// programmable LDO digital resistor +#define VH_RD_CMD_RDAC 0x00 // RDAC interface +#define VH_RD_CMD_EE 0x20 // EEPROM interface + #define VH_TOL_HA 0x1E // Tolerance MSB + #define VH_TOL_LA 0x1F // Tolerance LSB +#define VH_RD_CMD_WP 0x40 // EEPROM write protect +#define VH_RD_CMD_NOP 0x80 // NOP +#define VH_RD_CMD_ETOR 0xA0 // EEPROM -> RDAC +#define VH_RD_CMD_RTOE 0xC0 // RDAC -> EEPROM + +// ADC +#define VH_ADC_W1_EN 0xA0 // Word 1 Enable +#define VH_ADC_W1_CH01 0x00 // diff - 0, 1 +#define VH_ADC_W1_CH23 0x01 // diff - 2, 3 +#define VH_ADC_W1_CH10 0x08 // diff - 1, 0 +#define VH_ADC_W1_CH32 0x09 // diff - 3, 2 +#define VH_ADC_W1_CH0 0x10 // single-ended - 0 +#define VH_ADC_W1_CH1 0x18 // single-ended - 1 +#define VH_ADC_W1_CH2 0x11 // single-ended - 2 +#define VH_ADC_W1_CH3 0x19 // single-ended - 3 +#define VH_ADC_W2_EN 0x80 // Word 2 Enable +#define VH_ADC_W2_IM 0x40 // internal temp +#define VH_ADC_W2_F(x) (((x) % 0x3) << 4) // rejection mode +#define VH_ADC_W2_SPD 0x08 // speed 2X +#define VH_ADC_W2_G(x) ((x) % 0x7) // gain + +struct vh_adc_wr { // see the datasheet + unsigned char w1; // VH_ADC_W1_* + unsigned char w2; // VH_ADC_W2_* +}; + +// DAC +#define VH_DAC_W1_UA 0x00 // update DAC A output +#define VH_DAC_W1_UB 0x10 // update DAC B output +#define VH_DAC_W1_LA 0x40 // load DAC A input +#define VH_DAC_W1_LB 0x50 // load DAC B input +#define VH_DAC_W1_ALLA 0x80 // load DAC A input, update all outputs +#define VH_DAC_W1_ALLB 0x90 // load DAC B input, update all outputs +#define VH_DAC_W1_ALL 0xC0 // load all inputs, update all outputs +#define VH_DAC_W1_ALLI 0xD0 // load all inputs +#define VH_DAC_W1_UALL 0xE0 // update all - don't send data +#define VH_DAC_W1_EC 0xF0 // Extended command + #define VH_DAC_BCH 0x0C // both channel A & B + #define VH_DAC_CHB 0x08 // channel B + #define VH_DAC_CHA 0x04 // channel A + #define VH_DAC_PD100K 0x03 // power down - 100K pull down + #define VH_DAC_PD1K 0x02 // power down - 1K pull down + #define VH_DAC_PDF 0x01 // power down - float + #define VH_DAC_PU 0x00 // power up +#define VH_DAC_W1_RDA 0xF1 // Read A +#define VH_DAC_W1_RDB 0xF2 // Read B + +struct vh_dac_wr { + unsigned char w1; // cmd | d[7:3] + unsigned char w2; // (d[3:0] << 4) || (VH_DAC_CH* | VH_DAC_P*) +}; + +// SPI +#define BUF_MAX_SIZE (20) + +// SPI transfer structure +struct spi_xfer { + unsigned char addr; + unsigned char data[2]; +} spi_xfer; + +// von hippel driver ioctl definitions +#define BMI_VH_RLEDOFF _IOW(BMI_VH_IOCTL, 0x1, unsigned int) // Turn off red LED +#define BMI_VH_RLEDON _IOW(BMI_VH_IOCTL, 0x2, unsigned int) // Turn on red LED +#define BMI_VH_GLEDOFF _IOW(BMI_VH_IOCTL, 0x3, unsigned int) // Turn off green LED +#define BMI_VH_GLEDON _IOW(BMI_VH_IOCTL, 0x4, unsigned int) // Turn on green LED +#define BMI_VH_GETSTAT _IOR(BMI_VH_IOCTL, 0x5, unsigned int *) // READ IOX register +#define BMI_VH_MKGPIO_OUT _IOW(BMI_VH_IOCTL, 0x6, unsigned int) // make a GPIO bit an output +#define BMI_VH_MKGPIO_IN _IOW(BMI_VH_IOCTL, 0x7, unsigned int) // make a GPIO bit an input +#define BMI_VH_SETGPIO _IOW(BMI_VH_IOCTL, 0x8, unsigned int) // set a GPIO output to 1 +#define BMI_VH_CLRGPIO _IOW(BMI_VH_IOCTL, 0x9, unsigned int) // set a GPIO output to 0 +#define BMI_VH_MKIOX_OUT _IOW(BMI_VH_IOCTL, 0xa, unsigned int) // make a IOX bit an output +#define BMI_VH_MKIOX_IN _IOW(BMI_VH_IOCTL, 0xb, unsigned int) // make a IOX bit an input +#define BMI_VH_SETIOX _IOW(BMI_VH_IOCTL, 0xc, unsigned int) // set a IOX output to 1 +#define BMI_VH_CLRIOX _IOW(BMI_VH_IOCTL, 0xd, unsigned int) // set a IOX output to 0 +#define BMI_VH_SETRDAC _IOW(BMI_VH_IOCTL, 0xe, unsigned int) // set LDO RDAC resistance +#define BMI_VH_RDRDAC _IOW(BMI_VH_IOCTL, 0xf, unsigned int *) // read LDO RDAC resistance +#define BMI_VH_ADCWR _IOW(BMI_VH_IOCTL, 0x10, struct vh_adc_wr *) // write ADC +#define BMI_VH_ADCRD _IOW(BMI_VH_IOCTL, 0x11, unsigned int *) // read ADC +#define BMI_VH_DACWR _IOW(BMI_VH_IOCTL, 0x12, struct vh_dac_wr *) // write DAC +#define BMI_VH_DACRD _IOW(BMI_VH_IOCTL, 0x13, unsigned int *) // read DAC +#define BMI_VH_READ_SPI _IOR(BMI_VH_IOCTL, 0x14, struct spi_xfer *) // read SPI - requires SPI EEPROM +#define BMI_VH_WRITE_SPI _IOR(BMI_VH_IOCTL, 0x15, struct spi_xfer *) // write SPI - requires SPI EEPROM + +#endif /* BMI_VH_H */ + --- /dev/null +++ git/include/linux/bmi/bmi_zb.h @@ -0,0 +1,83 @@ +/* + * File: include/linux/bmi/bmi_gps.h + * Author: V. Thavisri +#include + +/* IOCTL commands for BMI ZB driver - char device portion */ + +#define BMI_ZB_RLEDOFF _IO(BMI_ZIGBEE_IOCTL, 0x1) +#define BMI_ZB_RLEDON _IO(BMI_ZIGBEE_IOCTL, 0x2) +#define BMI_ZB_GLEDOFF _IO(BMI_ZIGBEE_IOCTL, 0x3) +#define BMI_ZB_GLEDON _IO(BMI_ZIGBEE_IOCTL, 0x4) +#define BMI_ZB_RESET _IO(BMI_ZIGBEE_IOCTL, 0x5) +#define BMI_ZB_SPI_SIG _IO(BMI_ZIGBEE_IOCTL, 0x6) +#define BMI_ZB_LOOPBACK _IO(BMI_ZIGBEE_IOCTL, 0x7) +#define BMI_ZB_STARTREQ _IO(BMI_ZIGBEE_IOCTL, 0x8) +#define BMI_ZB_UPDATE_STATE _IO(BMI_ZIGBEE_IOCTL, 0x9) + + +/* IOCTL commands for BMI ZB driver - network device portion */ + +#define SIOCSAPPREGISTER (SIOCDEVPRIVATE + 1) +#define SIOCSALLOWBIND (SIOCDEVPRIVATE + 2) +#define SIOCSPERMITJOINING (SIOCDEVPRIVATE + 3) +#define SIOCGDEVICEINFO (SIOCDEVPRIVATE + 4) +#define SIOCSRESET (SIOCDEVPRIVATE + 5) +#define SIOCSBIND (SIOCDEVPRIVATE + 6) +#define SIOCSZCOMMAND (SIOCDEVPRIVATE + 7) +#define SIOCSSTARTREQ (SIOCDEVPRIVATE + 8) +#define SIOCSFINDDEVICE (SIOCDEVPRIVATE + 9) +#define SIOCSAFREGISTER (SIOCDEVPRIVATE + 10) +#define SIOCSPOWERAMP (SIOCDEVPRIVATE + 11) +#define SIOCDEBUG (SIOCDEVPRIVATE + 15) + +struct sockaddr_zb +{ + unsigned short z_family; + int z_ifindex; + unsigned char z_name[15]; + unsigned short z_protocol; +}; + +/* move this #define to include/linux/socket.h */ +#define SOL_ZACCEL 275 + +#define Z_PACKET_SOCK 0 +#define Z_CONTROL_SOCK 1 +#define Z_NUM_SOCK 2 +#define Z_NO_SOCK 0xFF + +/* Device-Specification Configuration Parameters */ +#define ZCD_NV_STARTUP_OPTION 0x03 +#define ZCD_NV_LOGICAL_TYPE 0x87 +#define ZCD_NV_POLL_RATE 0x24 +#define ZCD_NV_QUEUED_POLL_RATE 0x25 +#define ZCD_NV_RESPONSE_POLL_RATE 0x26 +#define ZCD_NV_POLL_FAILURE_RETRIES 0x29 +#define ZCD_NV_INDIRECT_MSG_TIMEOUT 0x2B +#define ZCD_NV_APS_FRAME_RETRIES 0x43 +#define ZCD_NV_APS_ACK_WAIT_DURATION 0x44 +#define ZCD_NV_BINDING_TIME 0x46 +#define ZCD_NV_USERDESC 0x81 + +// Network-Specification Configuration Parameters +#define ZCD_NV_PANID 0x83 +#define ZCD_NV_CHANLIST 0x84 +#define ZCD_NV_PRECFGKEY 0x62 +#define ZCD_NV_PRECFGKEYS_ENABLE 0x63 +#define ZCD_NV_SECURITY_MODE 0x64 +#define ZCD_NV_BCAST_RETRIES 0x2E +#define ZCD_NV_PASSIVE_ACK_TIMEOUT 0x2F +#define ZCD_NV_BCAST_DELIVERY_TIME 0x30 +#define ZCD_NV_ROUTE_EXPIRY_TIME 0x2C + +#endif /* BMI_ZBCNTL_H */ --- git.orig/include/linux/mod_devicetable.h +++ git/include/linux/mod_devicetable.h @@ -341,10 +341,23 @@ struct eisa_device_id { kernel_ulong_t driver_data; }; #define EISA_DEVICE_MODALIAS_FMT "eisa:s%s" +/* Bug Labs BeagleBug */ + +struct bmi_device_id { + __u16 match_flags; + __u16 vendor; + __u16 product; + __u16 revision; +}; + +#define BMI_DEVICE_ID_MATCH_VENDOR (1) +#define BMI_DEVICE_ID_MATCH_PRODUCT (2) +#define BMI_DEVICE_ID_MATCH_REVISION (4) + struct parisc_device_id { __u8 hw_type; /* 5 bits used */ __u8 hversion_rev; /* 4 bits */ __u16 hversion; /* 12 bits */ __u32 sversion; /* 20 bits */ --- git.orig/scripts/mod/file2alias.c +++ git/scripts/mod/file2alias.c @@ -286,10 +286,26 @@ static int do_pci_entry(const char *file ADD(alias, "i", interface_mask == 0xFF, interface); add_wildcard(alias); return 1; } +/* Looks like: bmi:vNpNrN. */ +static int do_bmi_entry(const char *filename, + struct bmi_device_id *id, char *alias) +{ + id->match_flags = TO_NATIVE(id->match_flags); + id->vendor = TO_NATIVE(id->vendor); + id->product = TO_NATIVE(id->product); + id->revision = TO_NATIVE(id->revision); + + strcpy(alias, "bmi:"); + ADD(alias, "v", id->match_flags & BMI_DEVICE_ID_MATCH_VENDOR, id->vendor); + ADD(alias, "p", id->match_flags & BMI_DEVICE_ID_MATCH_PRODUCT, id->product); + ADD(alias, "r", id->match_flags & BMI_DEVICE_ID_MATCH_REVISION, id->revision); + return 1; +} + /* looks like: "ccw:tNmNdtNdmN" */ static int do_ccw_entry(const char *filename, struct ccw_device_id *id, char *alias) { id->match_flags = TO_NATIVE(id->match_flags); @@ -779,10 +795,14 @@ void handle_moddevtable(struct module *m do_hid_entry, mod); else if (sym_is(symname, "__mod_ieee1394_device_table")) do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id), "ieee1394", do_ieee1394_entry, mod); + else if (sym_is(symname, "__mod_bmi_device_table")) + do_table(symval, sym->st_size, + sizeof(struct bmi_device_id), "bmi", + do_bmi_entry, mod); else if (sym_is(symname, "__mod_ccw_device_table")) do_table(symval, sym->st_size, sizeof(struct ccw_device_id), "ccw", do_ccw_entry, mod); else if (sym_is(symname, "__mod_ap_device_table"))