/*----------------------------------------------------------------------------*/ /* copyright STMicroelectronics, 2007. */ /* */ /* 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.1 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 . */ /*----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include /*******************************************************************************/ static DEFINE_MUTEX(core_lock); static void i2sdev_release(struct device *dev) { struct i2s_device *i2s = to_i2s_device(dev); if (i2s->controller) put_device(&(i2s->controller->dev)); kfree(dev); } static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { const struct i2s_device *i2s = to_i2s_device(dev); return sprintf(buf, "%s\n", i2s->modalias); } static struct device_attribute i2s_dev_attrs[] = { __ATTR_RO(modalias), __ATTR_NULL, }; /* modalias support makes "modprobe $MODALIAS" new-style hotplug work, * and the sysfs version makes coldplug work too. */ static const struct i2s_device_id *i2s_match_id(const struct i2s_device_id *id, const struct i2s_device *device) { while (id->name[0]) { if (strcmp(device->modalias, id->name) == 0) return id; id++; } return NULL; } static int i2s_match_device(struct device *dev, struct device_driver *drv) { const struct i2s_device *device = to_i2s_device(dev); struct i2s_driver *driver = to_i2s_driver(drv); if (driver->id_table) return i2s_match_id(driver->id_table, device) != NULL; return 0; } static int i2s_uevent(struct device *dev, struct kobj_uevent_env *env) { const struct i2s_device *i2s = to_i2s_device(dev); add_uevent_var(env, "MODALIAS=%s", i2s->modalias); return 0; } #ifdef CONFIG_PM static int i2s_suspend(struct device *dev, pm_message_t message) { int value = 0; struct i2s_driver *drv = to_i2s_driver(dev->driver); /* suspend will stop irqs and dma; no more i/o */ if (drv) { if (drv->suspend) value = drv->suspend(to_i2s_device(dev), message); else dev_dbg(dev, "... can't suspend\n"); } return value; } static int i2s_resume(struct device *dev) { int value = 0; struct i2s_driver *drv = to_i2s_driver(dev->driver); /* resume may restart the i/o queue */ if (drv) { if (drv->resume) value = drv->resume(to_i2s_device(dev)); else dev_dbg(dev, "... can't resume\n"); } return value; } #else #define i2s_suspend NULL #define i2s_resume NULL #endif /*This bus is designed to handle various protocols supported by the MSP- ARM Primecell IP * such as * I2s, PCM, AC97, TDM .... (refer to the data sheet for the complete list. * Current MSP driver has the above ones coded. * */ struct bus_type i2s_bus_type = { .name = "i2s", .dev_attrs = i2s_dev_attrs, .match = i2s_match_device, .uevent = i2s_uevent, .suspend = i2s_suspend, .resume = i2s_resume, }; EXPORT_SYMBOL_GPL(i2s_bus_type); static int i2s_drv_probe(struct device *dev) { const struct i2s_driver *sdrv = to_i2s_driver(dev->driver); return sdrv->probe(to_i2s_device(dev)); } static int i2s_drv_remove(struct device *dev) { const struct i2s_driver *sdrv = to_i2s_driver(dev->driver); return sdrv->remove(to_i2s_device(dev)); } static void i2s_drv_shutdown(struct device *dev) { const struct i2s_driver *sdrv = to_i2s_driver(dev->driver); sdrv->shutdown(to_i2s_device(dev)); } /** * i2s_register_driver - register a I2S driver * @sdrv: the driver to register * Context: can sleep */ int i2s_register_driver(struct i2s_driver *sdrv) { sdrv->driver.bus = &i2s_bus_type; if (sdrv->probe) sdrv->driver.probe = i2s_drv_probe; if (sdrv->remove) sdrv->driver.remove = i2s_drv_remove; if (sdrv->shutdown) sdrv->driver.shutdown = i2s_drv_shutdown; return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(i2s_register_driver); /******************************************************************************/ struct board_i2s_combined_info { struct i2s_board_info board_info; struct i2s_device *i2s_dev_p; }; struct boardinfo { struct list_head list; unsigned n_board_info; struct board_i2s_combined_info board_i2s_info[0]; }; static LIST_HEAD(board_list); static DEFINE_MUTEX(board_lock); /* * Get an i2s device. Used in MSP LTP tests. */ struct i2s_device *i2s_get_device_from_boardinfo(int chip_select) { struct boardinfo *bi; struct i2s_device *i2s_dev_p = NULL; mutex_lock(&board_lock); list_for_each_entry(bi, &board_list, list) { struct board_i2s_combined_info *chip = bi->board_i2s_info; unsigned n; for (n = bi->n_board_info; n > 0; n--, chip++) if (chip->board_info.chip_select == chip_select) { i2s_dev_p = chip->i2s_dev_p; break; } if (i2s_dev_p != NULL) break; } mutex_unlock(&board_lock); return i2s_dev_p; } EXPORT_SYMBOL_GPL(i2s_get_device_from_boardinfo); /* I2S devices should normally not be created by I2S device drivers; that * would make them board-specific. Similarly with I2S master drivers. * Device registration normally goes into like arch/.../mach.../board-YYY.c * with other readonly (flashable) information about mainboard devices. */ struct i2s_device *i2s_alloc_device(struct device *device) { struct i2s_device *i2s; struct device *dev = device->parent; get_device(device); i2s = kzalloc(sizeof *i2s, GFP_KERNEL); if (!i2s) { dev_err(dev, "cannot alloc i2s_device\n"); return NULL; } i2s->dev.parent = dev; i2s->dev.bus = &i2s_bus_type; i2s->dev.release = i2sdev_release; device_initialize(&i2s->dev); return i2s; } EXPORT_SYMBOL_GPL(i2s_alloc_device); /** * i2s_add_device - Add i2s_device allocated with i2s_alloc_device * @i2s: i2s_device to register * * Companion function to i2s_alloc_device. Devices allocated with * i2s_alloc_device can be added onto the i2s bus with this function. * * Returns 0 on success; negative errno on failure */ int i2s_add_device(struct i2s_device *i2s) { static DEFINE_MUTEX(i2s_add_lock); struct device *dev = i2s->dev.parent; int status; dev_set_name(&i2s->dev, "%s.%u", "i2s", i2s->chip_select); mutex_lock(&i2s_add_lock); if (bus_find_device_by_name(&i2s_bus_type, NULL, dev_name(&i2s->dev)) != NULL) { dev_err(dev, "chipselect %d already in use\n", i2s->chip_select); status = -EBUSY; goto done; } /* Device may be bound to an active driver when this returns */ status = device_add(&i2s->dev); if (status < 0) dev_err(dev, "can't %s %s, status %d\n", "add", dev_name(&i2s->dev), status); else dev_dbg(dev, "registered child %s\n", dev_name(&i2s->dev)); done: mutex_unlock(&i2s_add_lock); return status; } EXPORT_SYMBOL_GPL(i2s_add_device); /** * i2s_new_device - instantiate one new I2S device * @i2s_cont: Controller to which device is connected * @chip: Describes the I2S device * Context: can sleep * * On typical mainboards, this is purely internal; and it's not needed * after board init creates the hard-wired devices. Some development * platforms may not be able to use i2s_register_board_info though, and * this is exported so that driver could add devices (which it would * learn about out-of-band). * * Returns the new device, or NULL. */ struct i2s_device *i2s_new_device(struct i2s_controller *i2s_cont, struct i2s_board_info *chip) { struct i2s_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ proxy = i2s_alloc_device(&i2s_cont->dev); if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); proxy->chip_select = chip->chip_select; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *)chip->platform_data; proxy->controller = i2s_cont; status = i2s_add_device(proxy); if (status < 0) { kfree(proxy); return NULL; } return proxy; } EXPORT_SYMBOL_GPL(i2s_new_device); /** * i2s_register_board_info - register I2S devices for a given board * @info: array of chip descriptors * @n: how many descriptors are provided * Context: can sleep * * Board-specific early init code calls this (probably during arch_initcall) * with segments of the I2S device table. Any device nodes are created later, * after the relevant parent I2S controller (id) is defined. We keep * this table of devices forever, so that reloading a controller driver will * not make Linux forget about these hard-wired devices. * */ int __init i2s_register_board_info(struct i2s_board_info const *info, unsigned n) { int i; struct boardinfo *bi; bi = kmalloc(sizeof(*bi) + (n * sizeof(struct board_i2s_combined_info)), GFP_KERNEL); if (!bi) return -ENOMEM; bi->n_board_info = n; for (i = 0; i < n; i++) memcpy(&bi->board_i2s_info[i].board_info, &info[i], sizeof *info); mutex_lock(&board_lock); list_add_tail(&bi->list, &board_list); mutex_unlock(&board_lock); return 0; } /** * scan_boardinfo - Scan, creates and registered new i2s device structure. * @i2s_cont: i2s controller structure * Context: process * * It will scan the device list that may be registered statically using * register_board_info func in arch specific directory and call * i2s_new_device to create and registered i2s device over i2s bus. It is * called by i2s_add_controller function. * * Returns void. */ static void scan_boardinfo(struct i2s_controller *i2s_cont) { struct boardinfo *bi; mutex_lock(&board_lock); list_for_each_entry(bi, &board_list, list) { struct board_i2s_combined_info *chip = bi->board_i2s_info; unsigned n; for (n = bi->n_board_info; n > 0; n--, chip++) { if (chip->board_info.chip_select != i2s_cont->id) continue; /* NOTE: this relies on i2s_new_device to * issue diagnostics when given bogus inputs */ chip->i2s_dev_p = i2s_new_device(i2s_cont, &chip->board_info); } } mutex_unlock(&board_lock); } /******************************************************************************/ /**I2S Controller inittialization*/ static void i2s_controller_dev_release(struct device *dev) { struct i2s_controller *i2s_cont; i2s_cont = container_of(dev, struct i2s_controller, dev); kfree(i2s_cont); } static ssize_t show_controller_name(struct device *dev, struct device_attribute *attr, char *buf) { struct i2s_controller *cont = to_i2s_controller(dev); return sprintf(buf, "%s\n", cont->name); } static struct device_attribute i2s_controller_attrs[] = { __ATTR(name, S_IRUGO, show_controller_name, NULL), {}, }; static struct class i2s_controller_class = { .owner = THIS_MODULE, .name = "i2s-controller", .dev_attrs = i2s_controller_attrs, }; static int i2s_register_controller(struct i2s_controller *cont) { int res = 0; mutex_init(&cont->bus_lock); mutex_lock(&core_lock); /* Add the controller to the driver core. * If the parent pointer is not set up, * we add this controller to the host bus. */ if (cont->dev.parent == NULL) { cont->dev.parent = &platform_bus; pr_debug("I2S controller driver [%s] forgot to specify " "physical device\n", cont->name); } dev_set_name(&cont->dev, "I2Scrlr-%d", cont->id); cont->dev.release = &i2s_controller_dev_release; cont->dev.class = &i2s_controller_class; res = device_register(&cont->dev); if (res) goto out_unlock; dev_dbg(&cont->dev, "controller [%s] registered\n", cont->name); scan_boardinfo(cont); out_unlock: mutex_unlock(&core_lock); return res; } /** * i2s_add_controller - declare i2s controller, use dynamic bus number * @controller: the controller to add * Context: can sleep * */ int i2s_add_controller(struct i2s_controller *controller) { return i2s_register_controller(controller); } EXPORT_SYMBOL(i2s_add_controller); static int __unregister(struct device *dev, void *controller_dev) { /* note: before about 2.6.14-rc1 this would corrupt memory: */ if (dev != controller_dev) i2s_unregister_device(to_i2s_device(dev)); return 0; } /** * i2s_del_controller - unregister I2S controller * @cont: the controller being unregistered * Context: can sleep * * This unregisters an I2S controller which was previously registered * by @i2s_add_controller. */ int i2s_del_controller(struct i2s_controller *cont) { int res = 0; int dummy; mutex_lock(&core_lock); dummy = device_for_each_child(cont->dev.parent, &cont->dev, __unregister); device_unregister(&cont->dev); mutex_unlock(&core_lock); return res; } EXPORT_SYMBOL(i2s_del_controller); /******************************************************************************/ /*I2S interface apis*/ /** * i2s_transfer - Main i2s transfer function. * @i2s_cont: i2s controller structure passed by client driver. * @message: i2s message structure contains transceive info. * Context: process or interrupt. * * This API is called by client i2s driver as i2s_xfer funtion. It will handle * main i2s transfer over i2s bus. The controller should registered its own * functions using i2s algorithm structure. * * Returns error(-1) in case of failure or success(0). */ int i2s_transfer(struct i2s_controller *i2s_cont, struct i2s_message *message) { return i2s_cont->algo->cont_transfer(i2s_cont, message); } EXPORT_SYMBOL(i2s_transfer); /** * i2s_cleanup - Close the current i2s connection btw controller and client. * @i2s_cont: i2s controller structure * @flag: It indicates the functionality that needs to be disabled. * Context: process * * This API will disable and reset the controller's configuration. Reset the * controller so that i2s client driver can reconfigure with new configuration. * Controller should release all the necessary resources which was acquired * during setup. * * Returns error(-1) in case of failure or success(0). */ int i2s_cleanup(struct i2s_controller *i2s_cont, i2s_flag flag) { int status = 0; status = i2s_cont->algo->cont_cleanup(i2s_cont, flag); if (status) return -1; else return 0; } EXPORT_SYMBOL(i2s_cleanup); /** * i2s_setup - configures and enables the I2S controller. * @i2s_cont: i2s controller sent by i2s device. * @config: specifies the configuration parameters. * * This function configures the I2S controller with the client configuration. * Controller was already registered on I2S bus by some master controller * driver. * * Returns error(-1) in case of failure else success(0) */ int i2s_setup(struct i2s_controller *i2s_cont, void *config) { return i2s_cont->algo->cont_setup(i2s_cont, config); } EXPORT_SYMBOL(i2s_setup); /** * i2s_hw_status - Get the current hw status for the i2s controller. * @i2s_cont: i2s controller structure passed by client driver. * Context: process or interrupt. * * This API is called by client i2s driver to find out current hw status. * The controller should registered its own functions using i2s algorithm structure. * * Returns current hw status register. */ int i2s_hw_status(struct i2s_controller *i2s_cont) { return i2s_cont->algo->cont_hw_status(i2s_cont); } /** * i2s_get_pointer - Get the current dma_addr_t for the i2s controller. * @i2s_cont: i2s controller structure passed by client driver. * @i2s_direction: Specifies TX or RX direction. * Context: process or interrupt. * * This API is called by client i2s driver to return a dma_addr_t corresponding * to the position of the DMA-controller. * The controller should registered its own functions using i2s algorithm structure. * * Returns current hw status register. */ dma_addr_t i2s_get_pointer(struct i2s_controller *i2s_cont, enum i2s_direction_t i2s_direction) { return i2s_cont->algo->cont_get_pointer(i2s_cont, i2s_direction); } /******************************************************************************/ static int __init i2s_init(void) { int status; status = bus_register(&i2s_bus_type); if (status < 0) goto err0; status = class_register(&i2s_controller_class); if (status < 0) goto err1; return 0; err1: bus_unregister(&i2s_bus_type); err0: return status; } static void __exit i2s_exit(void) { class_unregister(&i2s_controller_class); bus_unregister(&i2s_bus_type); } subsys_initcall(i2s_init); module_exit(i2s_exit); MODULE_AUTHOR("Sandeep Kaushik, "); MODULE_LICENSE("GPL");