diff options
Diffstat (limited to 'drivers')
194 files changed, 9484 insertions, 1769 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 94c823b25138..7d0d62c8a4e6 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -273,9 +273,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) { - int result = 0; + int result = 0, nid; struct acpi_memory_info *info, *n; + nid = acpi_get_node(mem_device->device->handle); + list_for_each_entry_safe(info, n, &mem_device->res_list, list) { if (info->failed) /* The kernel does not use this memory block */ @@ -288,7 +290,9 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) */ return -EBUSY; - result = remove_memory(info->start_addr, info->length); + if (nid < 0) + nid = memory_add_physaddr_to_nid(info->start_addr); + result = remove_memory(nid, info->start_addr, info->length); if (result) return result; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 33e609f63585..59844ee149be 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -282,10 +282,10 @@ acpi_table_parse_srat(enum acpi_srat_type id, handler, max_entries); } -int __init acpi_numa_init(void) -{ - int cnt = 0; +static int srat_mem_cnt; +void __init early_parse_srat(void) +{ /* * Should not limit number with cpu num that is from NR_CPUS or nr_cpus= * SRAT cpu entries could have different order with that in MADT. @@ -295,21 +295,24 @@ int __init acpi_numa_init(void) /* SRAT: Static Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY, - acpi_parse_x2apic_affinity, 0); + acpi_parse_x2apic_affinity, 0); acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY, - acpi_parse_processor_affinity, 0); - cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, - acpi_parse_memory_affinity, - NR_NODE_MEMBLKS); + acpi_parse_processor_affinity, 0); + srat_mem_cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, + acpi_parse_memory_affinity, + NR_NODE_MEMBLKS); } +} +int __init acpi_numa_init(void) +{ /* SLIT: System Locality Information Table */ acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); acpi_numa_arch_fixup(); - if (cnt < 0) - return cnt; + if (srat_mem_cnt < 0) + return srat_mem_cnt; else if (!parsed_numa_memblks) return -ENOENT; return 0; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index c5d2fd85dbe0..d57e32c5c5dc 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -45,6 +45,7 @@ #include <linux/cpuidle.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/memory_hotplug.h> #include <asm/io.h> #include <asm/cpu.h> @@ -641,6 +642,7 @@ static int acpi_processor_remove(struct acpi_device *device) per_cpu(processors, pr->id) = NULL; per_cpu(processor_device_array, pr->id) = NULL; + try_offline_node(cpu_to_node(pr->id)); free: free_cpumask_var(pr->throttling.shared_cpu_map); diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c index b22d71cac54c..738be42d6fc5 100644 --- a/drivers/atm/atmtcp.c +++ b/drivers/atm/atmtcp.c @@ -157,7 +157,6 @@ static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) { struct atm_cirange ci; struct atm_vcc *vcc; - struct hlist_node *node; struct sock *s; int i; @@ -171,7 +170,7 @@ static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) for(i = 0; i < VCC_HTABLE_SIZE; ++i) { struct hlist_head *head = &vcc_hash[i]; - sk_for_each(s, node, head) { + sk_for_each(s, head) { vcc = atm_sk(s); if (vcc->dev != dev) continue; @@ -264,12 +263,11 @@ static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) { struct hlist_head *head; struct atm_vcc *vcc; - struct hlist_node *node; struct sock *s; head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; - sk_for_each(s, node, head) { + sk_for_each(s, head) { vcc = atm_sk(s); if (vcc->dev == dev && vcc->vci == vci && vcc->vpi == vpi && diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index c1eb6fa8ac35..b1955ba40d63 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -2093,7 +2093,6 @@ static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) { - struct hlist_node *node; struct sock *s; static const char *signal[] = { "LOST","unknown","okay" }; struct eni_dev *eni_dev = ENI_DEV(dev); @@ -2171,7 +2170,7 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) for(i = 0; i < VCC_HTABLE_SIZE; ++i) { struct hlist_head *head = &vcc_hash[i]; - sk_for_each(s, node, head) { + sk_for_each(s, head) { struct eni_vcc *eni_vcc; int length; diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 72b6960fa95f..d6891267f5bb 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -329,7 +329,6 @@ __find_vcc(struct he_dev *he_dev, unsigned cid) { struct hlist_head *head; struct atm_vcc *vcc; - struct hlist_node *node; struct sock *s; short vpi; int vci; @@ -338,7 +337,7 @@ __find_vcc(struct he_dev *he_dev, unsigned cid) vci = cid & ((1 << he_dev->vcibits) - 1); head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; - sk_for_each(s, node, head) { + sk_for_each(s, head) { vcc = atm_sk(s); if (vcc->dev == he_dev->atm_dev && vcc->vci == vci && vcc->vpi == vpi && diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index ed1d2b7f923b..6587dc295eb0 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -251,7 +251,6 @@ static void nicstar_remove_one(struct pci_dev *pcidev) if (card->scd2vc[j] != NULL) free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc); } - idr_remove_all(&card->idr); idr_destroy(&card->idr); pci_free_consistent(card->pcidev, NS_RSQSIZE + NS_RSQ_ALIGNMENT, card->rsq.org, card->rsq.dma); @@ -950,11 +949,10 @@ static void free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc) static void push_rxbufs(ns_dev * card, struct sk_buff *skb) { struct sk_buff *handle1, *handle2; - u32 id1 = 0, id2 = 0; + int id1, id2; u32 addr1, addr2; u32 stat; unsigned long flags; - int err; /* *BARF* */ handle2 = NULL; @@ -1027,23 +1025,12 @@ static void push_rxbufs(ns_dev * card, struct sk_buff *skb) card->lbfqc += 2; } - do { - if (!idr_pre_get(&card->idr, GFP_ATOMIC)) { - printk(KERN_ERR - "nicstar%d: no free memory for idr\n", - card->index); - goto out; - } - - if (!id1) - err = idr_get_new_above(&card->idr, handle1, 0, &id1); - - if (!id2 && err == 0) - err = idr_get_new_above(&card->idr, handle2, 0, &id2); - - } while (err == -EAGAIN); + id1 = idr_alloc(&card->idr, handle1, 0, 0, GFP_ATOMIC); + if (id1 < 0) + goto out; - if (err) + id2 = idr_alloc(&card->idr, handle2, 0, 0, GFP_ATOMIC); + if (id2 < 0) goto out; spin_lock_irqsave(&card->res_lock, flags); diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 0474a89170b9..32784d18d1f7 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -896,12 +896,11 @@ static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) { struct hlist_head *head; struct atm_vcc *vcc = NULL; - struct hlist_node *node; struct sock *s; read_lock(&vcc_sklist_lock); head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; - sk_for_each(s, node, head) { + sk_for_each(s, head) { vcc = atm_sk(s); if (vcc->dev == dev && vcc->vci == vci && vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE && diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 987604d56c83..8300a1864c33 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -693,6 +693,12 @@ int offline_memory_block(struct memory_block *mem) return ret; } +/* return true if the memory block is offlined, otherwise, return false */ +bool is_memblock_offlined(struct memory_block *mem) +{ + return mem->state == MEM_OFFLINE; +} + /* * Initialize the sysfs support for memory devices... */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3148b10dc2e5..1244930e3d7a 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -124,6 +124,76 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); +static int dev_memalloc_noio(struct device *dev, void *data) +{ + return dev->power.memalloc_noio; +} + +/* + * pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag. + * @dev: Device to handle. + * @enable: True for setting the flag and False for clearing the flag. + * + * Set the flag for all devices in the path from the device to the + * root device in the device tree if @enable is true, otherwise clear + * the flag for devices in the path whose siblings don't set the flag. + * + * The function should only be called by block device, or network + * device driver for solving the deadlock problem during runtime + * resume/suspend: + * + * If memory allocation with GFP_KERNEL is called inside runtime + * resume/suspend callback of any one of its ancestors(or the + * block device itself), the deadlock may be triggered inside the + * memory allocation since it might not complete until the block + * device becomes active and the involed page I/O finishes. The + * situation is pointed out first by Alan Stern. Network device + * are involved in iSCSI kind of situation. + * + * The lock of dev_hotplug_mutex is held in the function for handling + * hotplug race because pm_runtime_set_memalloc_noio() may be called + * in async probe(). + * + * The function should be called between device_add() and device_del() + * on the affected device(block/network device). + */ +void pm_runtime_set_memalloc_noio(struct device *dev, bool enable) +{ + static DEFINE_MUTEX(dev_hotplug_mutex); + + mutex_lock(&dev_hotplug_mutex); + for (;;) { + bool enabled; + + /* hold power lock since bitfield is not SMP-safe. */ + spin_lock_irq(&dev->power.lock); + enabled = dev->power.memalloc_noio; + dev->power.memalloc_noio = enable; + spin_unlock_irq(&dev->power.lock); + + /* + * not need to enable ancestors any more if the device + * has been enabled. + */ + if (enabled && enable) + break; + + dev = dev->parent; + + /* + * clear flag of the parent device only if all the + * children don't set the flag because ancestor's + * flag was set by any one of the descendants. + */ + if (!dev || (!enable && + device_for_each_child(dev, NULL, + dev_memalloc_noio))) + break; + } + mutex_unlock(&dev_hotplug_mutex); +} +EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio); + /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. @@ -278,7 +348,24 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (!cb) return -ENOSYS; - retval = __rpm_callback(cb, dev); + if (dev->power.memalloc_noio) { + unsigned int noio_flag; + + /* + * Deadlock might be caused if memory allocation with + * GFP_KERNEL happens inside runtime_suspend and + * runtime_resume callbacks of one block device's + * ancestor or the block device itself. Network + * device might be thought as part of iSCSI block + * device, so network device and its ancestor should + * be marked as memalloc_noio too. + */ + noio_flag = memalloc_noio_save(); + retval = __rpm_callback(cb, dev); + memalloc_noio_restore(noio_flag); + } else { + retval = __rpm_callback(cb, dev); + } dev->power.runtime_error = retval; return retval != -EACCES ? retval : -EIO; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 8c13eeb83c53..e98da675f0c1 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2660,25 +2660,24 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, mdev->read_requests = RB_ROOT; mdev->write_requests = RB_ROOT; - if (!idr_pre_get(&minors, GFP_KERNEL)) - goto out_no_minor_idr; - if (idr_get_new_above(&minors, mdev, minor, &minor_got)) + minor_got = idr_alloc(&minors, mdev, minor, minor + 1, GFP_KERNEL); + if (minor_got < 0) { + if (minor_got == -ENOSPC) { + err = ERR_MINOR_EXISTS; + drbd_msg_put_info("requested minor exists already"); + } goto out_no_minor_idr; - if (minor_got != minor) { - err = ERR_MINOR_EXISTS; - drbd_msg_put_info("requested minor exists already"); - goto out_idr_remove_minor; } - if (!idr_pre_get(&tconn->volumes, GFP_KERNEL)) - goto out_idr_remove_minor; - if (idr_get_new_above(&tconn->volumes, mdev, vnr, &vnr_got)) + vnr_got = idr_alloc(&tconn->volumes, mdev, vnr, vnr + 1, GFP_KERNEL); + if (vnr_got < 0) { + if (vnr_got == -ENOSPC) { + err = ERR_INVALID_REQUEST; + drbd_msg_put_info("requested volume exists already"); + } goto out_idr_remove_minor; - if (vnr_got != vnr) { - err = ERR_INVALID_REQUEST; - drbd_msg_put_info("requested volume exists already"); - goto out_idr_remove_vol; } + add_disk(disk); kref_init(&mdev->kref); /* one ref for both idrs and the the add_disk */ @@ -2689,8 +2688,6 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, return NO_ERROR; -out_idr_remove_vol: - idr_remove(&tconn->volumes, vnr_got); out_idr_remove_minor: idr_remove(&minors, minor_got); synchronize_rcu(); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index ae1251270624..56a3963f8a57 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -162,12 +162,13 @@ static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = { static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file) { - loff_t size, loopsize; + loff_t loopsize; /* Compute loopsize in bytes */ - size = i_size_read(file->f_mapping->host); - loopsize = size - offset; - /* offset is beyond i_size, wierd but possible */ + loopsize = i_size_read(file->f_mapping->host); + if (offset > 0) + loopsize -= offset; + /* offset is beyond i_size, weird but possible */ if (loopsize < 0) return 0; @@ -190,6 +191,7 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) { loff_t size = get_size(offset, sizelimit, lo->lo_backing_file); sector_t x = (sector_t)size; + struct block_device *bdev = lo->lo_device; if (unlikely((loff_t)x != size)) return -EFBIG; @@ -198,6 +200,9 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) if (lo->lo_sizelimit != sizelimit) lo->lo_sizelimit = sizelimit; set_capacity(lo->lo_disk, x); + bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9); + /* let user-space know about the new size */ + kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); return 0; } @@ -1091,10 +1096,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) return err; if (lo->lo_offset != info->lo_offset || - lo->lo_sizelimit != info->lo_sizelimit) { + lo->lo_sizelimit != info->lo_sizelimit) if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) return -EFBIG; - } + loop_config_discard(lo); memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE); @@ -1271,28 +1276,10 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) { static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev) { - int err; - sector_t sec; - loff_t sz; - - err = -ENXIO; if (unlikely(lo->lo_state != Lo_bound)) - goto out; - err = figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); - if (unlikely(err)) - goto out; - sec = get_capacity(lo->lo_disk); - /* the width of sector_t may be narrow for bit-shift */ - sz = sec; - sz <<= 9; - mutex_lock(&bdev->bd_mutex); - bd_set_size(bdev, sz); - /* let user-space know about the new size */ - kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); - mutex_unlock(&bdev->bd_mutex); + return -ENXIO; - out: - return err; + return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); } static int lo_ioctl(struct block_device *bdev, fmode_t mode, @@ -1624,30 +1611,17 @@ static int loop_add(struct loop_device **l, int i) if (!lo) goto out; - if (!idr_pre_get(&loop_index_idr, GFP_KERNEL)) - goto out_free_dev; - + /* allocate id, if @id >= 0, we're requesting that specific id */ if (i >= 0) { - int m; - - /* create specific i in the index */ - err = idr_get_new_above(&loop_index_idr, lo, i, &m); - if (err >= 0 && i != m) { - idr_remove(&loop_index_idr, m); + err = idr_alloc(&loop_index_idr, lo, i, i + 1, GFP_KERNEL); + if (err == -ENOSPC) err = -EEXIST; - } - } else if (i == -1) { - int m; - - /* get next free nr */ - err = idr_get_new(&loop_index_idr, lo, &m); - if (err >= 0) - i = m; } else { - err = -EINVAL; + err = idr_alloc(&loop_index_idr, lo, 0, 0, GFP_KERNEL); } if (err < 0) goto out_free_dev; + i = err; lo->lo_queue = blk_alloc_queue(GFP_KERNEL); if (!lo->lo_queue) @@ -1858,11 +1832,15 @@ static int __init loop_init(void) max_part = (1UL << part_shift) - 1; } - if ((1UL << part_shift) > DISK_MAX_PARTS) - return -EINVAL; + if ((1UL << part_shift) > DISK_MAX_PARTS) { + err = -EINVAL; + goto misc_out; + } - if (max_loop > 1UL << (MINORBITS - part_shift)) - return -EINVAL; + if (max_loop > 1UL << (MINORBITS - part_shift)) { + err = -EINVAL; + goto misc_out; + } /* * If max_loop is specified, create that many devices upfront. @@ -1880,8 +1858,10 @@ static int __init loop_init(void) range = 1UL << MINORBITS; } - if (register_blkdev(LOOP_MAJOR, "loop")) - return -EIO; + if (register_blkdev(LOOP_MAJOR, "loop")) { + err = -EIO; + goto misc_out; + } blk_register_region(MKDEV(LOOP_MAJOR, 0), range, THIS_MODULE, loop_probe, NULL, NULL); @@ -1894,6 +1874,10 @@ static int __init loop_init(void) printk(KERN_INFO "loop: module loaded\n"); return 0; + +misc_out: + misc_deregister(&loop_misc); + return err; } static int loop_exit_cb(int id, void *ptr, void *data) @@ -1911,7 +1895,6 @@ static void __exit loop_exit(void) range = max_loop ? max_loop << part_shift : 1UL << MINORBITS; idr_for_each(&loop_index_idr, &loop_exit_cb, NULL); - idr_remove_all(&loop_index_idr); idr_destroy(&loop_index_idr); blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range); diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 11cc9522cdd4..8bedaf46792f 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -161,11 +161,9 @@ static void mtip_command_cleanup(struct driver_data *dd) command = &port->commands[commandindex]; if (atomic_read(&command->active) - && (command->async_callback)) { - command->async_callback(command->async_data, - -ENODEV); - command->async_callback = NULL; - command->async_data = NULL; + && (command->bio)) { + bio_endio(command->bio, -ENODEV); + command->bio = NULL; } dma_unmap_sg(&port->dd->pdev->dev, @@ -606,11 +604,9 @@ static void mtip_timeout_function(unsigned long int data) writel(1 << bit, port->completed[group]); /* Call the async completion callback. */ - if (likely(command->async_callback)) - command->async_callback(command->async_data, - -EIO); - command->async_callback = NULL; - command->comp_func = NULL; + if (likely(command->bio)) + bio_endio(command->bio, -EIO); + command->bio = NULL; /* Unmap the DMA scatter list entries */ dma_unmap_sg(&port->dd->pdev->dev, @@ -679,7 +675,8 @@ static void mtip_timeout_function(unsigned long int data) static void mtip_async_complete(struct mtip_port *port, int tag, void *data, - int status) + int status, + struct batch_complete *batch) { struct mtip_cmd *command; struct driver_data *dd = data; @@ -696,11 +693,10 @@ static void mtip_async_complete(struct mtip_port *port, } /* Upper layer callback */ - if (likely(command->async_callback)) - command->async_callback(command->async_data, cb_status); + if (likely(command->bio)) + bio_endio_batch(command->bio, cb_status, batch); - command->async_callback = NULL; - command->comp_func = NULL; + command->bio = NULL; /* Unmap the DMA scatter list entries */ dma_unmap_sg(&dd->pdev->dev, @@ -733,24 +729,22 @@ static void mtip_async_complete(struct mtip_port *port, static void mtip_completion(struct mtip_port *port, int tag, void *data, - int status) + int status, + struct batch_complete *batch) { - struct mtip_cmd *command = &port->commands[tag]; struct completion *waiting = data; if (unlikely(status == PORT_IRQ_TF_ERR)) dev_warn(&port->dd->pdev->dev, "Internal command %d completed with TFE\n", tag); - command->async_callback = NULL; - command->comp_func = NULL; - complete(waiting); } static void mtip_null_completion(struct mtip_port *port, int tag, void *data, - int status) + int status, + struct batch_complete *batch) { return; } @@ -796,7 +790,7 @@ static void mtip_handle_tfe(struct driver_data *dd) atomic_inc(&cmd->active); /* active > 1 indicates error */ if (cmd->comp_data && cmd->comp_func) { cmd->comp_func(port, MTIP_TAG_INTERNAL, - cmd->comp_data, PORT_IRQ_TF_ERR); + cmd->comp_data, PORT_IRQ_TF_ERR, NULL); } goto handle_tfe_exit; } @@ -829,7 +823,7 @@ static void mtip_handle_tfe(struct driver_data *dd) cmd->comp_func(port, tag, cmd->comp_data, - 0); + 0, NULL); } else { dev_err(&port->dd->pdev->dev, "Missing completion func for tag %d", @@ -916,7 +910,7 @@ static void mtip_handle_tfe(struct driver_data *dd) if (cmd->comp_func) { cmd->comp_func(port, tag, cmd->comp_data, - -ENODATA); + -ENODATA, NULL); } continue; } @@ -946,7 +940,7 @@ static void mtip_handle_tfe(struct driver_data *dd) port, tag, cmd->comp_data, - PORT_IRQ_TF_ERR); + PORT_IRQ_TF_ERR, NULL); else dev_warn(&port->dd->pdev->dev, "Bad completion for tag %d\n", @@ -973,6 +967,9 @@ static inline void mtip_workq_sdbfx(struct mtip_port *port, int group, struct driver_data *dd = port->dd; int tag, bit; struct mtip_cmd *command; + struct batch_complete batch; + + batch_complete_init(&batch); if (!completed) { WARN_ON_ONCE(!completed); @@ -997,7 +994,8 @@ static inline void mtip_workq_sdbfx(struct mtip_port *port, int group, port, tag, command->comp_data, - 0); + 0, + &batch); } else { dev_warn(&dd->pdev->dev, "Null completion " @@ -1034,7 +1032,7 @@ static inline void mtip_process_legacy(struct driver_data *dd, u32 port_stat) cmd->comp_func(port, MTIP_TAG_INTERNAL, cmd->comp_data, - 0); + 0, NULL); return; } } @@ -2554,8 +2552,8 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, * None */ static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector, - int nsect, int nents, int tag, void *callback, - void *data, int dir) + int nsect, int nents, int tag, + struct bio *bio, int dir) { struct host_to_dev_fis *fis; struct mtip_port *port = dd->port; @@ -2610,12 +2608,7 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector, command->comp_func = mtip_async_complete; command->direction = dma_dir; - /* - * Set the completion function and data for the command passed - * from the upper layer. - */ - command->async_data = data; - command->async_callback = callback; + command->bio = bio; /* * To prevent this command from being issued @@ -3795,7 +3788,6 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) bio_sectors(bio), nents, tag, - bio_endio, bio, bio_data_dir(bio)); } else diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index 3bffff5f670c..af8c6f79a8d8 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -325,11 +325,9 @@ struct mtip_cmd { void (*comp_func)(struct mtip_port *port, int tag, void *data, - int status); - /* Additional callback function that may be called by comp_func() */ - void (*async_callback)(void *data, int status); - - void *async_data; /* Addl. data passed to async_callback() */ + int status, + struct batch_complete *batch); + struct bio *bio; int scatter_ents; /* Number of scatter list entries used */ diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 57763c54363a..deb722d63d68 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -775,7 +775,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id) if (intr & ERROR_INTR) { n = fs->scount - 1 - resid / 512; if (n > 0) { - blk_update_request(req, 0, n << 9); + blk_update_request(req, 0, n << 9, NULL); fs->req_sector += n; } if (fs->retries < 5) { @@ -1090,10 +1090,13 @@ static const struct block_device_operations floppy_fops = { static void swim3_mb_event(struct macio_dev* mdev, int mb_state) { struct floppy_state *fs = macio_get_drvdata(mdev); - struct swim3 __iomem *sw = fs->swim3; + struct swim3 __iomem *sw; if (!fs) return; + + sw = fs->swim3; + if (mb_state != MB_FD) return; diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 8ad21a25bc0d..5a9e04a6a6d7 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -210,7 +210,8 @@ static void virtblk_bio_send_flush_work(struct work_struct *work) virtblk_bio_send_flush(vbr); } -static inline void virtblk_request_done(struct virtblk_req *vbr) +static inline void virtblk_request_done(struct virtblk_req *vbr, + struct batch_complete *batch) { struct virtio_blk *vblk = vbr->vblk; struct request *req = vbr->req; @@ -224,11 +225,12 @@ static inline void virtblk_request_done(struct virtblk_req *vbr) req->errors = (error != 0); } - __blk_end_request_all(req, error); + blk_end_request_all_batch(req, error, batch); mempool_free(vbr, vblk->pool); } -static inline void virtblk_bio_flush_done(struct virtblk_req *vbr) +static inline void virtblk_bio_flush_done(struct virtblk_req *vbr, + struct batch_complete *batch) { struct virtio_blk *vblk = vbr->vblk; @@ -237,12 +239,13 @@ static inline void virtblk_bio_flush_done(struct virtblk_req *vbr) INIT_WORK(&vbr->work, virtblk_bio_send_data_work); queue_work(virtblk_wq, &vbr->work); } else { - bio_endio(vbr->bio, virtblk_result(vbr)); + bio_endio_batch(vbr->bio, virtblk_result(vbr), batch); mempool_free(vbr, vblk->pool); } } -static inline void virtblk_bio_data_done(struct virtblk_req *vbr) +static inline void virtblk_bio_data_done(struct virtblk_req *vbr, + struct batch_complete *batch) { struct virtio_blk *vblk = vbr->vblk; @@ -252,17 +255,18 @@ static inline void virtblk_bio_data_done(struct virtblk_req *vbr) INIT_WORK(&vbr->work, virtblk_bio_send_flush_work); queue_work(virtblk_wq, &vbr->work); } else { - bio_endio(vbr->bio, virtblk_result(vbr)); + bio_endio_batch(vbr->bio, virtblk_result(vbr), batch); mempool_free(vbr, vblk->pool); } } -static inline void virtblk_bio_done(struct virtblk_req *vbr) +static inline void virtblk_bio_done(struct virtblk_req *vbr, + struct batch_complete *batch) { if (unlikely(vbr->flags & VBLK_IS_FLUSH)) - virtblk_bio_flush_done(vbr); + virtblk_bio_flush_done(vbr, batch); else - virtblk_bio_data_done(vbr); + virtblk_bio_data_done(vbr, batch); } static void virtblk_done(struct virtqueue *vq) @@ -272,16 +276,19 @@ static void virtblk_done(struct virtqueue *vq) struct virtblk_req *vbr; unsigned long flags; unsigned int len; + struct batch_complete batch; + + batch_complete_init(&batch); spin_lock_irqsave(vblk->disk->queue->queue_lock, flags); do { virtqueue_disable_cb(vq); while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { if (vbr->bio) { - virtblk_bio_done(vbr); + virtblk_bio_done(vbr, &batch); bio_done = true; } else { - virtblk_request_done(vbr); + virtblk_request_done(vbr, &batch); req_done = true; } } @@ -291,6 +298,8 @@ static void virtblk_done(struct virtqueue *vq) blk_start_queue(vblk->disk->queue); spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags); + batch_complete(&batch); + if (bio_done) wake_up(&vblk->queue_wait); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 1c7fdcd22a98..0ac9b45a585e 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1208,6 +1208,16 @@ static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSPACING 1 #define DEFAULT_REGSIZE 1 +#ifdef CONFIG_ACPI +static bool si_tryacpi = 1; +#endif +#ifdef CONFIG_DMI +static bool si_trydmi = 1; +#endif +static bool si_tryplatform = 1; +#ifdef CONFIG_PCI +static bool si_trypci = 1; +#endif static bool si_trydefaults = 1; static char *si_type[SI_MAX_PARMS]; #define MAX_SI_TYPE_STR 30 @@ -1238,6 +1248,25 @@ MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" " Documentation/IPMI.txt in the kernel sources for the" " gory details."); +#ifdef CONFIG_ACPI +module_param_named(tryacpi, si_tryacpi, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via ACPI"); +#endif +#ifdef CONFIG_DMI +module_param_named(trydmi, si_trydmi, bool, 0); +MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" + " default scan of the interfaces identified via DMI"); +#endif +module_param_named(tryplatform, si_tryplatform, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via platform" + " interfaces like openfirmware"); +#ifdef CONFIG_PCI +module_param_named(trypci, si_trypci, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via pci"); +#endif module_param_named(trydefaults, si_trydefaults, bool, 0); MODULE_PARM_DESC(trydefaults, "Setting this to 'false' will disable the" " default scan of the KCS and SMIC interface at the standard" @@ -3371,13 +3400,15 @@ static int init_ipmi_si(void) return 0; initialized = 1; - rv = platform_driver_register(&ipmi_driver); - if (rv) { - printk(KERN_ERR PFX "Unable to register driver: %d\n", rv); - return rv; + if (si_tryplatform) { + rv = platform_driver_register(&ipmi_driver); + if (rv) { + printk(KERN_ERR PFX "Unable to register " + "driver: %d\n", rv); + return rv; + } } - /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { @@ -3400,24 +3431,31 @@ static int init_ipmi_si(void) return 0; #ifdef CONFIG_PCI - rv = pci_register_driver(&ipmi_pci_driver); - if (rv) - printk(KERN_ERR PFX "Unable to register PCI driver: %d\n", rv); - else - pci_registered = 1; + if (si_trypci) { + rv = pci_register_driver(&ipmi_pci_driver); + if (rv) + printk(KERN_ERR PFX "Unable to register " + "PCI driver: %d\n", rv); + else + pci_registered = 1; + } #endif #ifdef CONFIG_ACPI - pnp_register_driver(&ipmi_pnp_driver); - pnp_registered = 1; + if (si_tryacpi) { + pnp_register_driver(&ipmi_pnp_driver); + pnp_registered = 1; + } #endif #ifdef CONFIG_DMI - dmi_find_bmc(); + if (si_trydmi) + dmi_find_bmc(); #endif #ifdef CONFIG_ACPI - spmi_find_bmc(); + if (si_tryacpi) + spmi_find_bmc(); #endif /* We prefer devices with interrupts, but in the case of a machine diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6f6e92a3102d..3b9f28961f78 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -28,6 +28,7 @@ #include <linux/pfn.h> #include <linux/export.h> #include <linux/io.h> +#include <linux/aio.h> #include <asm/uaccess.h> @@ -627,6 +628,18 @@ static ssize_t write_null(struct file *file, const char __user *buf, return count; } +static ssize_t aio_read_null(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + return 0; +} + +static ssize_t aio_write_null(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + return iov_length(iov, nr_segs); +} + static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf, struct splice_desc *sd) { @@ -670,6 +683,24 @@ static ssize_t read_zero(struct file *file, char __user *buf, return written ? written : -EFAULT; } +static ssize_t aio_read_zero(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + size_t written = 0; + unsigned long i; + ssize_t ret; + + for (i = 0; i < nr_segs; i++) { + ret = read_zero(iocb->ki_filp, iov[i].iov_base, iov[i].iov_len, + &pos); + if (ret < 0) + break; + written += ret; + } + + return written ? written : -EFAULT; +} + static int mmap_zero(struct file *file, struct vm_area_struct *vma) { #ifndef CONFIG_MMU @@ -738,6 +769,7 @@ static int open_port(struct inode *inode, struct file *filp) #define full_lseek null_lseek #define write_zero write_null #define read_full read_zero +#define aio_write_zero aio_write_null #define open_mem open_port #define open_kmem open_mem #define open_oldmem open_mem @@ -766,6 +798,8 @@ static const struct file_operations null_fops = { .llseek = null_lseek, .read = read_null, .write = write_null, + .aio_read = aio_read_null, + .aio_write = aio_write_null, .splice_write = splice_write_null, }; @@ -782,6 +816,8 @@ static const struct file_operations zero_fops = { .llseek = zero_lseek, .read = read_zero, .write = write_zero, + .aio_read = aio_read_zero, + .aio_write = aio_write_zero, .mmap = mmap_zero, }; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 522136d40843..190d4423653f 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -183,19 +183,12 @@ static const struct file_operations misc_fops = { int misc_register(struct miscdevice * misc) { - struct miscdevice *c; dev_t dev; int err = 0; INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); - list_for_each_entry(c, &misc_list, list) { - if (c->minor == misc->minor) { - mutex_unlock(&misc_mtx); - return -EBUSY; - } - } if (misc->minor == MISC_DYNAMIC_MINOR) { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); @@ -205,6 +198,15 @@ int misc_register(struct miscdevice * misc) } misc->minor = DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); + } else { + struct miscdevice *c; + + list_for_each_entry(c, &misc_list, list) { + if (c->minor == misc->minor) { + mutex_unlock(&misc_mtx); + return -EBUSY; + } + } } dev = MKDEV(MISC_MAJOR, misc->minor); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fabbfe1a9253..ed87b2405806 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -52,31 +52,29 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, int level) { struct clk *child; - struct hlist_node *tmp; if (!c) return; clk_summary_show_one(s, c, level); - hlist_for_each_entry(child, tmp, &c->children, child_node) + hlist_for_each_entry(child, &c->children, child_node) clk_summary_show_subtree(s, child, level + 1); } static int clk_summary_show(struct seq_file *s, void *data) { struct clk *c; - struct hlist_node *tmp; seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, "---------------------------------------------------------------------\n"); mutex_lock(&prepare_lock); - hlist_for_each_entry(c, tmp, &clk_root_list, child_node) + hlist_for_each_entry(c, &clk_root_list, child_node) clk_summary_show_subtree(s, c, 0); - hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node) + hlist_for_each_entry(c, &clk_orphan_list, child_node) clk_summary_show_subtree(s, c, 0); mutex_unlock(&prepare_lock); @@ -111,14 +109,13 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) { struct clk *child; - struct hlist_node *tmp; if (!c) return; clk_dump_one(s, c, level); - hlist_for_each_entry(child, tmp, &c->children, child_node) { + hlist_for_each_entry(child, &c->children, child_node) { seq_printf(s, ","); clk_dump_subtree(s, child, level + 1); } @@ -129,21 +126,20 @@ static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) static int clk_dump(struct seq_file *s, void *data) { struct clk *c; - struct hlist_node *tmp; bool first_node = true; seq_printf(s, "{"); mutex_lock(&prepare_lock); - hlist_for_each_entry(c, tmp, &clk_root_list, child_node) { + hlist_for_each_entry(c, &clk_root_list, child_node) { if (!first_node) seq_printf(s, ","); first_node = false; clk_dump_subtree(s, c, 0); } - hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node) { + hlist_for_each_entry(c, &clk_orphan_list, child_node) { seq_printf(s, ","); clk_dump_subtree(s, c, 0); } @@ -222,7 +218,6 @@ out: static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) { struct clk *child; - struct hlist_node *tmp; int ret = -EINVAL;; if (!clk || !pdentry) @@ -233,7 +228,7 @@ static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) if (ret) goto out; - hlist_for_each_entry(child, tmp, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) clk_debug_create_subtree(child, clk->dentry); ret = 0; @@ -299,7 +294,6 @@ out: static int __init clk_debug_init(void) { struct clk *clk; - struct hlist_node *tmp; struct dentry *d; rootdir = debugfs_create_dir("clk", NULL); @@ -324,10 +318,10 @@ static int __init clk_debug_init(void) mutex_lock(&prepare_lock); - hlist_for_each_entry(clk, tmp, &clk_root_list, child_node) + hlist_for_each_entry(clk, &clk_root_list, child_node) clk_debug_create_subtree(clk, rootdir); - hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node) + hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_debug_create_subtree(clk, orphandir); inited = 1; @@ -345,13 +339,12 @@ static inline int clk_debug_register(struct clk *clk) { return 0; } static void clk_disable_unused_subtree(struct clk *clk) { struct clk *child; - struct hlist_node *tmp; unsigned long flags; if (!clk) goto out; - hlist_for_each_entry(child, tmp, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) clk_disable_unused_subtree(child); spin_lock_irqsave(&enable_lock, flags); @@ -384,14 +377,13 @@ out: static int clk_disable_unused(void) { struct clk *clk; - struct hlist_node *tmp; mutex_lock(&prepare_lock); - hlist_for_each_entry(clk, tmp, &clk_root_list, child_node) + hlist_for_each_entry(clk, &clk_root_list, child_node) clk_disable_unused_subtree(clk); - hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node) + hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_disable_unused_subtree(clk); mutex_unlock(&prepare_lock); @@ -484,12 +476,11 @@ static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk) { struct clk *child; struct clk *ret; - struct hlist_node *tmp; if (!strcmp(clk->name, name)) return clk; - hlist_for_each_entry(child, tmp, &clk->children, child_node) { + hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_lookup_subtree(name, child); if (ret) return ret; @@ -502,20 +493,19 @@ struct clk *__clk_lookup(const char *name) { struct clk *root_clk; struct clk *ret; - struct hlist_node *tmp; if (!name) return NULL; /* search the 'proper' clk tree first */ - hlist_for_each_entry(root_clk, tmp, &clk_root_list, child_node) { + hlist_for_each_entry(root_clk, &clk_root_list, child_node) { ret = __clk_lookup_subtree(name, root_clk); if (ret) return ret; } /* if not found, then search the orphan tree */ - hlist_for_each_entry(root_clk, tmp, &clk_orphan_list, child_node) { + hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { ret = __clk_lookup_subtree(name, root_clk); if (ret) return ret; @@ -812,7 +802,6 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) { unsigned long old_rate; unsigned long parent_rate = 0; - struct hlist_node *tmp; struct clk *child; old_rate = clk->rate; @@ -832,7 +821,7 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) if (clk->notifier_count && msg) __clk_notify(clk, msg, old_rate, clk->rate); - hlist_for_each_entry(child, tmp, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) __clk_recalc_rates(child, msg); } @@ -878,7 +867,6 @@ EXPORT_SYMBOL_GPL(clk_get_rate); */ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) { - struct hlist_node *tmp; struct clk *child; unsigned long new_rate; int ret = NOTIFY_DONE; @@ -895,7 +883,7 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) if (ret == NOTIFY_BAD) goto out; - hlist_for_each_entry(child, tmp, &clk->children, child_node) { + hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_speculate_rates(child, new_rate); if (ret == NOTIFY_BAD) break; @@ -908,11 +896,10 @@ out: static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) { struct clk *child; - struct hlist_node *tmp; clk->new_rate = new_rate; - hlist_for_each_entry(child, tmp, &clk->children, child_node) { + hlist_for_each_entry(child, &clk->children, child_node) { if (child->ops->recalc_rate) child->new_rate = child->ops->recalc_rate(child->hw, new_rate); else @@ -983,7 +970,6 @@ out: */ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) { - struct hlist_node *tmp; struct clk *child, *fail_clk = NULL; int ret = NOTIFY_DONE; @@ -996,7 +982,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even fail_clk = clk; } - hlist_for_each_entry(child, tmp, &clk->children, child_node) { + hlist_for_each_entry(child, &clk->children, child_node) { clk = clk_propagate_rate_change(child, event); if (clk) fail_clk = clk; @@ -1014,7 +1000,6 @@ static void clk_change_rate(struct clk *clk) struct clk *child; unsigned long old_rate; unsigned long best_parent_rate = 0; - struct hlist_node *tmp; old_rate = clk->rate; @@ -1032,7 +1017,7 @@ static void clk_change_rate(struct clk *clk) if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); - hlist_for_each_entry(child, tmp, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) clk_change_rate(child); } @@ -1348,7 +1333,7 @@ int __clk_init(struct device *dev, struct clk *clk) { int i, ret = 0; struct clk *orphan; - struct hlist_node *tmp, *tmp2; + struct hlist_node *tmp2; if (!clk) return -EINVAL; @@ -1448,7 +1433,7 @@ int __clk_init(struct device *dev, struct clk *clk) * walk the list of orphan clocks and reparent any that are children of * this clock */ - hlist_for_each_entry_safe(orphan, tmp, tmp2, &clk_orphan_list, child_node) { + hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); if (!strcmp(clk->name, orphan->parent_names[i])) diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c index 591b6597c00a..126cf295b198 100644 --- a/drivers/dca/dca-sysfs.c +++ b/drivers/dca/dca-sysfs.c @@ -53,22 +53,19 @@ void dca_sysfs_remove_req(struct dca_provider *dca, int slot) int dca_sysfs_add_provider(struct dca_provider *dca, struct device *dev) { struct device *cd; - int err = 0; + int ret; -idr_try_again: - if (!idr_pre_get(&dca_idr, GFP_KERNEL)) - return -ENOMEM; + idr_preload(GFP_KERNEL); spin_lock(&dca_idr_lock); - err = idr_get_new(&dca_idr, dca, &dca->id); + + ret = idr_alloc(&dca_idr, dca, 0, 0, GFP_NOWAIT); + if (ret >= 0) + dca->id = ret; + spin_unlock(&dca_idr_lock); - switch (err) { - case 0: - break; - case -EAGAIN: - goto idr_try_again; - default: - return err; - } + idr_preload_end(); + if (ret < 0) + return ret; cd = device_create(dca_class, dev, MKDEV(0, 0), NULL, "dca%d", dca->id); if (IS_ERR(cd)) { diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 242b8c0a3de8..b2728d6ba2fd 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -686,18 +686,14 @@ static int get_dma_id(struct dma_device *device) { int rc; - idr_retry: - if (!idr_pre_get(&dma_idr, GFP_KERNEL)) - return -ENOMEM; mutex_lock(&dma_list_mutex); - rc = idr_get_new(&dma_idr, NULL, &device->dev_id); - mutex_unlock(&dma_list_mutex); - if (rc == -EAGAIN) - goto idr_retry; - else if (rc != 0) - return rc; - return 0; + rc = idr_alloc(&dma_idr, NULL, 0, 0, GFP_KERNEL); + if (rc >= 0) + device->dev_id = rc; + + mutex_unlock(&dma_list_mutex); + return rc < 0 ? rc : 0; } /** diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f8d22872d753..27ac423ab25e 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -487,27 +487,28 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) static int add_client_resource(struct client *client, struct client_resource *resource, gfp_t gfp_mask) { + bool preload = gfp_mask & __GFP_WAIT; unsigned long flags; int ret; - retry: - if (idr_pre_get(&client->resource_idr, gfp_mask) == 0) - return -ENOMEM; - + if (preload) + idr_preload(gfp_mask); spin_lock_irqsave(&client->lock, flags); + if (client->in_shutdown) ret = -ECANCELED; else - ret = idr_get_new(&client->resource_idr, resource, - &resource->handle); + ret = idr_alloc(&client->resource_idr, resource, 0, 0, + GFP_NOWAIT); if (ret >= 0) { + resource->handle = ret; client_get(client); schedule_if_iso_resource(resource); } - spin_unlock_irqrestore(&client->lock, flags); - if (ret == -EAGAIN) - goto retry; + spin_unlock_irqrestore(&client->lock, flags); + if (preload) + idr_preload_end(); return ret < 0 ? ret : 0; } @@ -1779,7 +1780,6 @@ static int fw_device_op_release(struct inode *inode, struct file *file) wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); idr_for_each(&client->resource_idr, shutdown_resource, client); - idr_remove_all(&client->resource_idr); idr_destroy(&client->resource_idr); list_for_each_entry_safe(event, next_event, &client->event_list, link) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 3873d535b28d..b946330b02c3 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1017,9 +1017,7 @@ static void fw_device_init(struct work_struct *work) fw_device_get(device); down_write(&fw_device_rwsem); - ret = idr_pre_get(&fw_device_idr, GFP_KERNEL) ? - idr_get_new(&fw_device_idr, device, &minor) : - -ENOMEM; + ret = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, GFP_KERNEL); up_write(&fw_device_rwsem); if (ret < 0) diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 90723e65b081..0b5b5f619c75 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -21,6 +21,7 @@ #include <linux/types.h> #include <linux/bootmem.h> #include <linux/slab.h> +#include <linux/mm.h> /* * Data types ------------------------------------------------------------------ @@ -52,6 +53,9 @@ static ssize_t start_show(struct firmware_map_entry *entry, char *buf); static ssize_t end_show(struct firmware_map_entry *entry, char *buf); static ssize_t type_show(struct firmware_map_entry *entry, char *buf); +static struct firmware_map_entry * __meminit +firmware_map_find_entry(u64 start, u64 end, const char *type); + /* * Static data ----------------------------------------------------------------- */ @@ -79,7 +83,52 @@ static const struct sysfs_ops memmap_attr_ops = { .show = memmap_attr_show, }; -static struct kobj_type memmap_ktype = { +/* Firmware memory map entries. */ +static LIST_HEAD(map_entries); +static DEFINE_SPINLOCK(map_entries_lock); + +/* + * For memory hotplug, there is no way to free memory map entries allocated + * by boot mem after the system is up. So when we hot-remove memory whose + * map entry is allocated by bootmem, we need to remember the storage and + * reuse it when the memory is hot-added again. + */ +static LIST_HEAD(map_entries_bootmem); +static DEFINE_SPINLOCK(map_entries_bootmem_lock); + + +static inline struct firmware_map_entry * +to_memmap_entry(struct kobject *kobj) +{ + return container_of(kobj, struct firmware_map_entry, kobj); +} + +static void __meminit release_firmware_map_entry(struct kobject *kobj) +{ + struct firmware_map_entry *entry = to_memmap_entry(kobj); + + if (PageReserved(virt_to_page(entry))) { + /* + * Remember the storage allocated by bootmem, and reuse it when + * the memory is hot-added again. The entry will be added to + * map_entries_bootmem here, and deleted from &map_entries in + * firmware_map_remove_entry(). + */ + if (firmware_map_find_entry(entry->start, entry->end, + entry->type)) { + spin_lock(&map_entries_bootmem_lock); + list_add(&entry->list, &map_entries_bootmem); + spin_unlock(&map_entries_bootmem_lock); + } + + return; + } + + kfree(entry); +} + +static struct kobj_type __refdata memmap_ktype = { + .release = release_firmware_map_entry, .sysfs_ops = &memmap_attr_ops, .default_attrs = def_attrs, }; @@ -88,13 +137,6 @@ static struct kobj_type memmap_ktype = { * Registration functions ------------------------------------------------------ */ -/* - * Firmware memory map entries. No locking is needed because the - * firmware_map_add() and firmware_map_add_early() functions are called - * in firmware initialisation code in one single thread of execution. - */ -static LIST_HEAD(map_entries); - /** * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. * @start: Start of the memory range. @@ -118,11 +160,25 @@ static int firmware_map_add_entry(u64 start, u64 end, INIT_LIST_HEAD(&entry->list); kobject_init(&entry->kobj, &memmap_ktype); + spin_lock(&map_entries_lock); list_add_tail(&entry->list, &map_entries); + spin_unlock(&map_entries_lock); return 0; } +/** + * firmware_map_remove_entry() - Does the real work to remove a firmware + * memmap entry. + * @entry: removed entry. + * + * The caller must hold map_entries_lock, and release it properly. + **/ +static inline void firmware_map_remove_entry(struct firmware_map_entry *entry) +{ + list_del(&entry->list); +} + /* * Add memmap entry on sysfs */ @@ -144,6 +200,78 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry) return 0; } +/* + * Remove memmap entry on sysfs + */ +static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry) +{ + kobject_put(&entry->kobj); +} + +/* + * firmware_map_find_entry_in_list() - Search memmap entry in a given list. + * @start: Start of the memory range. + * @end: End of the memory range (exclusive). + * @type: Type of the memory range. + * @list: In which to find the entry. + * + * This function is to find the memmap entey of a given memory range in a + * given list. The caller must hold map_entries_lock, and must not release + * the lock until the processing of the returned entry has completed. + * + * Return: Pointer to the entry to be found on success, or NULL on failure. + */ +static struct firmware_map_entry * __meminit +firmware_map_find_entry_in_list(u64 start, u64 end, const char *type, + struct list_head *list) +{ + struct firmware_map_entry *entry; + + list_for_each_entry(entry, list, list) + if ((entry->start == start) && (entry->end == end) && + (!strcmp(entry->type, type))) { + return entry; + } + + return NULL; +} + +/* + * firmware_map_find_entry() - Search memmap entry in map_entries. + * @start: Start of the memory range. + * @end: End of the memory range (exclusive). + * @type: Type of the memory range. + * + * This function is to find the memmap entey of a given memory range. + * The caller must hold map_entries_lock, and must not release the lock + * until the processing of the returned entry has completed. + * + * Return: Pointer to the entry to be found on success, or NULL on failure. + */ +static struct firmware_map_entry * __meminit +firmware_map_find_entry(u64 start, u64 end, const char *type) +{ + return firmware_map_find_entry_in_list(start, end, type, &map_entries); +} + +/* + * firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem. + * @start: Start of the memory range. + * @end: End of the memory range (exclusive). + * @type: Type of the memory range. + * + * This function is similar to firmware_map_find_entry except that it find the + * given entry in map_entries_bootmem. + * + * Return: Pointer to the entry to be found on success, or NULL on failure. + */ +static struct firmware_map_entry * __meminit +firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type) +{ + return firmware_map_find_entry_in_list(start, end, type, + &map_entries_bootmem); +} + /** * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do * memory hotplug. @@ -161,9 +289,19 @@ int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type) { struct firmware_map_entry *entry; - entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); - if (!entry) - return -ENOMEM; + entry = firmware_map_find_entry_bootmem(start, end, type); + if (!entry) { + entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + } else { + /* Reuse storage allocated by bootmem. */ + spin_lock(&map_entries_bootmem_lock); + list_del(&entry->list); + spin_unlock(&map_entries_bootmem_lock); + + memset(entry, 0, sizeof(*entry)); + } firmware_map_add_entry(start, end, type, entry); /* create the memmap entry */ @@ -196,6 +334,36 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type) return firmware_map_add_entry(start, end, type, entry); } +/** + * firmware_map_remove() - remove a firmware mapping entry + * @start: Start of the memory range. + * @end: End of the memory range. + * @type: Type of the memory range. + * + * removes a firmware mapping entry. + * + * Returns 0 on success, or -EINVAL if no entry. + **/ +int __meminit firmware_map_remove(u64 start, u64 end, const char *type) +{ + struct firmware_map_entry *entry; + + spin_lock(&map_entries_lock); + entry = firmware_map_find_entry(start, end - 1, type); + if (!entry) { + spin_unlock(&map_entries_lock); + return -EINVAL; + } + + firmware_map_remove_entry(entry); + spin_unlock(&map_entries_lock); + + /* remove the memmap entry */ + remove_sysfs_fw_map_entry(entry); + + return 0; +} + /* * Sysfs functions ------------------------------------------------------------- */ @@ -217,8 +385,10 @@ static ssize_t type_show(struct firmware_map_entry *entry, char *buf) return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); } -#define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr) -#define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj) +static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr) +{ + return container_of(attr, struct memmap_attribute, attr); +} static ssize_t memmap_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 78f9420da478..58b6ca2621ea 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -358,15 +358,10 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, goto err_out; } - do { - ret = -ENOMEM; - if (idr_pre_get(&dirent_idr, GFP_KERNEL)) - ret = idr_get_new_above(&dirent_idr, value_sd, - 1, &id); - } while (ret == -EAGAIN); - - if (ret) + ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL); + if (ret < 0) goto free_sd; + id = ret; desc->flags &= GPIO_FLAGS_MASK; desc->flags |= (unsigned long)id << ID_SHIFT; diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 45adf97e678f..725968d38976 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -74,24 +74,13 @@ void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle) */ static int drm_ctxbitmap_next(struct drm_device * dev) { - int new_id; int ret; -again: - if (idr_pre_get(&dev->ctx_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Out of memory expanding drawable idr\n"); - return -ENOMEM; - } mutex_lock(&dev->struct_mutex); - ret = idr_get_new_above(&dev->ctx_idr, NULL, - DRM_RESERVED_CONTEXTS, &new_id); + ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0, + GFP_KERNEL); mutex_unlock(&dev->struct_mutex); - if (ret == -EAGAIN) - goto again; - else if (ret) - return ret; - - return new_id; + return ret; } /** @@ -118,7 +107,7 @@ int drm_ctxbitmap_init(struct drm_device * dev) void drm_ctxbitmap_cleanup(struct drm_device * dev) { mutex_lock(&dev->struct_mutex); - idr_remove_all(&dev->ctx_idr); + idr_destroy(&dev->ctx_idr); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9c797f6fea75..838c9b655e04 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -252,32 +252,21 @@ char *drm_get_connector_status_name(enum drm_connector_status status) static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type) { - int new_id = 0; int ret; -again: - if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Ran out memory getting a mode number\n"); - return -ENOMEM; - } - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); - - if (!ret) { + ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); + if (ret >= 0) { /* * Set up the object linking under the protection of the idr * lock so that other users can't see inconsistent state. */ - obj->id = new_id; + obj->id = ret; obj->type = obj_type; } mutex_unlock(&dev->mode_config.idr_mutex); - if (ret == -EAGAIN) - goto again; - - return ret; + return ret < 0 ? ret : 0; } /** @@ -1258,7 +1247,6 @@ void drm_mode_config_cleanup(struct drm_device *dev) crtc->funcs->destroy(crtc); } - idr_remove_all(&dev->mode_config.crtc_idr); idr_destroy(&dev->mode_config.crtc_idr); } EXPORT_SYMBOL(drm_mode_config_cleanup); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index be174cab105a..25f91cd23e60 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -297,7 +297,6 @@ static void __exit drm_core_exit(void) unregister_chrdev(DRM_MAJOR, "drm"); - idr_remove_all(&drm_minors_idr); idr_destroy(&drm_minors_idr); } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c6e25e979dd..79ed61822c30 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -353,6 +353,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) int i, j; /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) + return; + + /* * For each CRTC in this fb, turn the connectors on/off. */ drm_modeset_lock_all(dev); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 24efae464e2c..6577514941be 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -270,21 +270,19 @@ drm_gem_handle_create(struct drm_file *file_priv, int ret; /* - * Get the user-visible handle using idr. + * Get the user-visible handle using idr. Preload and perform + * allocation under our spinlock. */ -again: - /* ensure there is space available to allocate a handle */ - if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0) - return -ENOMEM; - - /* do the allocation under our spinlock */ + idr_preload(GFP_KERNEL); spin_lock(&file_priv->table_lock); - ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep); + + ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT); + spin_unlock(&file_priv->table_lock); - if (ret == -EAGAIN) - goto again; - else if (ret) + idr_preload_end(); + if (ret < 0) return ret; + *handlep = ret; drm_gem_object_handle_reference(obj); @@ -451,22 +449,15 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; -again: - if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { - ret = -ENOMEM; - goto err; - } - + idr_preload(GFP_KERNEL); spin_lock(&dev->object_name_lock); if (!obj->name) { - ret = idr_get_new_above(&dev->object_name_idr, obj, 1, - &obj->name); + ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT); + obj->name = ret; args->name = (uint64_t) obj->name; spin_unlock(&dev->object_name_lock); - if (ret == -EAGAIN) - goto again; - else if (ret) + if (ret < 0) goto err; /* Allocate a reference for the name table. */ @@ -561,8 +552,6 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private) { idr_for_each(&file_private->object_idr, &drm_gem_object_release_handle, file_private); - - idr_remove_all(&file_private->object_idr); idr_destroy(&file_private->object_idr); } diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 80254547a3f8..7e4bae760e27 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -60,14 +60,13 @@ void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; int count = 0; hashed_key = hash_long(key, ht->order); DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, list, h_list, head) + hlist_for_each_entry(entry, h_list, head) DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); } @@ -76,14 +75,13 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry(entry, list, h_list, head) { + hlist_for_each_entry(entry, h_list, head) { if (entry->key == key) - return list; + return &entry->head; if (entry->key > key) break; } @@ -95,14 +93,13 @@ static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list; unsigned int hashed_key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry_rcu(entry, h_list, head) { if (entry->key == key) - return list; + return &entry->head; if (entry->key > key) break; } @@ -113,19 +110,19 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) { struct drm_hash_item *entry; struct hlist_head *h_list; - struct hlist_node *list, *parent; + struct hlist_node *parent; unsigned int hashed_key; unsigned long key = item->key; hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; parent = NULL; - hlist_for_each_entry(entry, list, h_list, head) { + hlist_for_each_entry(entry, h_list, head) { if (entry->key == key) return -EINVAL; if (entry->key > key) break; - parent = list; + parent = &entry->head; } if (parent) { hlist_add_after_rcu(parent, &item->head); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 200e104f1fa0..7d30802a018f 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -109,7 +109,6 @@ EXPORT_SYMBOL(drm_ut_debug_printk); static int drm_minor_get_id(struct drm_device *dev, int type) { - int new_id; int ret; int base = 0, limit = 63; @@ -121,25 +120,11 @@ static int drm_minor_get_id(struct drm_device *dev, int type) limit = base + 255; } -again: - if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Out of memory expanding drawable idr\n"); - return -ENOMEM; - } mutex_lock(&dev->struct_mutex); - ret = idr_get_new_above(&drm_minors_idr, NULL, - base, &new_id); + ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL); mutex_unlock(&dev->struct_mutex); - if (ret == -EAGAIN) - goto again; - else if (ret) - return ret; - if (new_id >= limit) { - idr_remove(&drm_minors_idr, new_id); - return -EINVAL; - } - return new_id; + return ret == -ENOSPC ? -EINVAL : ret; } struct drm_master *drm_master_create(struct drm_minor *minor) diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 1a556354e92f..1adce07ecb5b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -137,21 +137,15 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, DRM_DEBUG_KMS("%s\n", __func__); -again: - /* ensure there is space available to allocate a handle */ - if (idr_pre_get(id_idr, GFP_KERNEL) == 0) { - DRM_ERROR("failed to get idr.\n"); - return -ENOMEM; - } - /* do the allocation under our mutexlock */ mutex_lock(lock); - ret = idr_get_new_above(id_idr, obj, 1, (int *)idp); + ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL); mutex_unlock(lock); - if (ret == -EAGAIN) - goto again; + if (ret < 0) + return ret; - return ret; + *idp = ret; + return 0; } static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) @@ -1786,8 +1780,6 @@ err_iommu: drm_iommu_detach_device(drm_dev, ippdrv->dev); err_idr: - idr_remove_all(&ctx->ipp_idr); - idr_remove_all(&ctx->prop_idr); idr_destroy(&ctx->ipp_idr); idr_destroy(&ctx->prop_idr); return ret; @@ -1965,8 +1957,6 @@ static int ipp_remove(struct platform_device *pdev) exynos_drm_subdrv_unregister(&ctx->subdrv); /* remove,destroy ipp idr */ - idr_remove_all(&ctx->ipp_idr); - idr_remove_all(&ctx->prop_idr); idr_destroy(&ctx->ipp_idr); idr_destroy(&ctx->prop_idr); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a3f06bcad551..27e586ad6b30 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -144,7 +144,7 @@ create_hw_context(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; struct i915_hw_context *ctx; - int ret, id; + int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (ctx == NULL) @@ -169,22 +169,11 @@ create_hw_context(struct drm_device *dev, ctx->file_priv = file_priv; -again: - if (idr_pre_get(&file_priv->context_idr, GFP_KERNEL) == 0) { - ret = -ENOMEM; - DRM_DEBUG_DRIVER("idr allocation failed\n"); - goto err_out; - } - - ret = idr_get_new_above(&file_priv->context_idr, ctx, - DEFAULT_CONTEXT_ID + 1, &id); - if (ret == 0) - ctx->id = id; - - if (ret == -EAGAIN) - goto again; - else if (ret) + ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, + GFP_KERNEL); + if (ret < 0) goto err_out; + ctx->id = ret; return ctx; diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 841065b998a1..5a5325e6b759 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -58,7 +58,6 @@ static int sis_driver_unload(struct drm_device *dev) { drm_sis_private_t *dev_priv = dev->dev_private; - idr_remove_all(&dev_priv->object_idr); idr_destroy(&dev_priv->object_idr); kfree(dev_priv); diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 2b2f78c428af..9a43d98e5003 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -128,17 +128,10 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, if (retval) goto fail_alloc; -again: - if (idr_pre_get(&dev_priv->object_idr, GFP_KERNEL) == 0) { - retval = -ENOMEM; - goto fail_idr; - } - - retval = idr_get_new_above(&dev_priv->object_idr, item, 1, &user_key); - if (retval == -EAGAIN) - goto again; - if (retval) + retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); + if (retval < 0) goto fail_idr; + user_key = retval; list_add(&item->owner_list, &file_priv->obj_list); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index c0f1cc7f5ca9..d0ab3fb32acd 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -120,7 +120,6 @@ int via_driver_unload(struct drm_device *dev) { drm_via_private_t *dev_priv = dev->dev_private; - idr_remove_all(&dev_priv->object_idr); idr_destroy(&dev_priv->object_idr); kfree(dev_priv); diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index 0d55432e02a2..0ab93ff09873 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -148,17 +148,10 @@ int via_mem_alloc(struct drm_device *dev, void *data, if (retval) goto fail_alloc; -again: - if (idr_pre_get(&dev_priv->object_idr, GFP_KERNEL) == 0) { - retval = -ENOMEM; - goto fail_idr; - } - - retval = idr_get_new_above(&dev_priv->object_idr, item, 1, &user_key); - if (retval == -EAGAIN) - goto again; - if (retval) + retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); + if (retval < 0) goto fail_idr; + user_key = retval; list_add(&item->owner_list, &file_priv->obj_list); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index e01a17b407b2..c9d067643f01 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -177,17 +177,16 @@ int vmw_resource_alloc_id(struct vmw_resource *res) BUG_ON(res->id != -1); - do { - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) - return -ENOMEM; - - write_lock(&dev_priv->resource_lock); - ret = idr_get_new_above(idr, res, 1, &res->id); - write_unlock(&dev_priv->resource_lock); + idr_preload(GFP_KERNEL); + write_lock(&dev_priv->resource_lock); - } while (ret == -EAGAIN); + ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT); + if (ret >= 0) + res->id = ret; - return ret; + write_unlock(&dev_priv->resource_lock); + idr_preload_end(); + return ret < 0 ? ret : 0; } /** diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 66a30f7ac882..795c916cf42a 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -935,25 +935,16 @@ out_list: */ int i2c_add_adapter(struct i2c_adapter *adapter) { - int id, res = 0; - -retry: - if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) - return -ENOMEM; + int res; mutex_lock(&core_lock); - /* "above" here means "above or equal to", sigh */ - res = idr_get_new_above(&i2c_adapter_idr, adapter, - __i2c_first_dynamic_bus_num, &id); + res = idr_alloc(&i2c_adapter_idr, adapter, + __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); - - if (res < 0) { - if (res == -EAGAIN) - goto retry; + if (res < 0) return res; - } - adapter->nr = id; + adapter->nr = res; return i2c_register_adapter(adapter); } EXPORT_SYMBOL(i2c_add_adapter); @@ -984,33 +975,17 @@ EXPORT_SYMBOL(i2c_add_adapter); int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; - int status; if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); - if (adap->nr & ~MAX_IDR_MASK) - return -EINVAL; - -retry: - if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) - return -ENOMEM; mutex_lock(&core_lock); - /* "above" here means "above or equal to", sigh; - * we need the "equal to" result to force the result - */ - status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); - if (status == 0 && id != adap->nr) { - status = -EBUSY; - idr_remove(&i2c_adapter_idr, id); - } + id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, + GFP_KERNEL); mutex_unlock(&core_lock); - if (status == -EAGAIN) - goto retry; - - if (status == 0) - status = i2c_register_adapter(adap); - return status; + if (id < 0) + return id == -ENOSPC ? -EBUSY : id; + return 0; } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 394fea2ba1bc..98281fe5ea4b 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -382,20 +382,21 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) static int cm_alloc_id(struct cm_id_private *cm_id_priv) { unsigned long flags; - int ret, id; + int id; static int next_id; - do { - spin_lock_irqsave(&cm.lock, flags); - ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, - next_id, &id); - if (!ret) - next_id = ((unsigned) id + 1) & MAX_IDR_MASK; - spin_unlock_irqrestore(&cm.lock, flags); - } while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) ); + idr_preload(GFP_KERNEL); + spin_lock_irqsave(&cm.lock, flags); + + id = idr_alloc(&cm.local_id_table, cm_id_priv, next_id, 0, GFP_NOWAIT); + if (id >= 0) + next_id = ((unsigned) id + 1) & MAX_IDR_MASK; + + spin_unlock_irqrestore(&cm.lock, flags); + idr_preload_end(); cm_id_priv->id.local_id = (__force __be32)id ^ cm.random_id_operand; - return ret; + return id < 0 ? id : 0; } static void cm_free_id(__be32 local_id) @@ -3844,7 +3845,6 @@ static int __init ib_cm_init(void) cm.remote_sidr_table = RB_ROOT; idr_init(&cm.local_id_table); get_random_bytes(&cm.random_id_operand, sizeof cm.random_id_operand); - idr_pre_get(&cm.local_id_table, GFP_KERNEL); INIT_LIST_HEAD(&cm.timewait_list); ret = class_register(&cm_class); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index d789eea32168..71c2c7116802 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2143,33 +2143,23 @@ static int cma_alloc_port(struct idr *ps, struct rdma_id_private *id_priv, unsigned short snum) { struct rdma_bind_list *bind_list; - int port, ret; + int ret; bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL); if (!bind_list) return -ENOMEM; - do { - ret = idr_get_new_above(ps, bind_list, snum, &port); - } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL)); - - if (ret) - goto err1; - - if (port != snum) { - ret = -EADDRNOTAVAIL; - goto err2; - } + ret = idr_alloc(ps, bind_list, snum, snum + 1, GFP_KERNEL); + if (ret < 0) + goto err; bind_list->ps = ps; - bind_list->port = (unsigned short) port; + bind_list->port = (unsigned short)ret; cma_bind_port(bind_list, id_priv); return 0; -err2: - idr_remove(ps, port); -err1: +err: kfree(bind_list); - return ret; + return ret == -ENOSPC ? -EADDRNOTAVAIL : ret; } static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) @@ -2214,10 +2204,9 @@ static int cma_check_port(struct rdma_bind_list *bind_list, { struct rdma_id_private *cur_id; struct sockaddr *addr, *cur_addr; - struct hlist_node *node; addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr; - hlist_for_each_entry(cur_id, node, &bind_list->owners, node) { + hlist_for_each_entry(cur_id, &bind_list->owners, node) { if (id_priv == cur_id) continue; diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c index 176c8f90f2bb..9f5ad7cc33c8 100644 --- a/drivers/infiniband/core/fmr_pool.c +++ b/drivers/infiniband/core/fmr_pool.c @@ -118,14 +118,13 @@ static inline struct ib_pool_fmr *ib_fmr_cache_lookup(struct ib_fmr_pool *pool, { struct hlist_head *bucket; struct ib_pool_fmr *fmr; - struct hlist_node *pos; if (!pool->cache_bucket) return NULL; bucket = pool->cache_bucket + ib_fmr_hash(*page_list); - hlist_for_each_entry(fmr, pos, bucket, cache_node) + hlist_for_each_entry(fmr, bucket, cache_node) if (io_virtual_address == fmr->io_virtual_address && page_list_len == fmr->page_list_len && !memcmp(page_list, fmr->page_list, diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index a8905abc56e4..934f45e79e5e 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -611,19 +611,21 @@ static void init_mad(struct ib_sa_mad *mad, struct ib_mad_agent *agent) static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask) { + bool preload = gfp_mask & __GFP_WAIT; unsigned long flags; int ret, id; -retry: - if (!idr_pre_get(&query_idr, gfp_mask)) - return -ENOMEM; + if (preload) + idr_preload(gfp_mask); spin_lock_irqsave(&idr_lock, flags); - ret = idr_get_new(&query_idr, query, &id); + + id = idr_alloc(&query_idr, query, 0, 0, GFP_NOWAIT); + spin_unlock_irqrestore(&idr_lock, flags); - if (ret == -EAGAIN) - goto retry; - if (ret) - return ret; + if (preload) + idr_preload_end(); + if (id < 0) + return id; query->mad_buf->timeout_ms = timeout_ms; query->mad_buf->context[0] = query; diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index 49b15ac1987e..f2f63933e8a9 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -176,7 +176,6 @@ static void ib_ucm_cleanup_events(struct ib_ucm_context *ctx) static struct ib_ucm_context *ib_ucm_ctx_alloc(struct ib_ucm_file *file) { struct ib_ucm_context *ctx; - int result; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) @@ -187,17 +186,10 @@ static struct ib_ucm_context *ib_ucm_ctx_alloc(struct ib_ucm_file *file) ctx->file = file; INIT_LIST_HEAD(&ctx->events); - do { - result = idr_pre_get(&ctx_id_table, GFP_KERNEL); - if (!result) - goto error; - - mutex_lock(&ctx_id_mutex); - result = idr_get_new(&ctx_id_table, ctx, &ctx->id); - mutex_unlock(&ctx_id_mutex); - } while (result == -EAGAIN); - - if (result) + mutex_lock(&ctx_id_mutex); + ctx->id = idr_alloc(&ctx_id_table, ctx, 0, 0, GFP_KERNEL); + mutex_unlock(&ctx_id_mutex); + if (ctx->id < 0) goto error; list_add_tail(&ctx->file_list, &file->ctxs); diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 2709ff581392..5ca44cd9b00c 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -145,7 +145,6 @@ static void ucma_put_ctx(struct ucma_context *ctx) static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) { struct ucma_context *ctx; - int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -156,17 +155,10 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) INIT_LIST_HEAD(&ctx->mc_list); ctx->file = file; - do { - ret = idr_pre_get(&ctx_idr, GFP_KERNEL); - if (!ret) - goto error; - - mutex_lock(&mut); - ret = idr_get_new(&ctx_idr, ctx, &ctx->id); - mutex_unlock(&mut); - } while (ret == -EAGAIN); - - if (ret) + mutex_lock(&mut); + ctx->id = idr_alloc(&ctx_idr, ctx, 0, 0, GFP_KERNEL); + mutex_unlock(&mut); + if (ctx->id < 0) goto error; list_add_tail(&ctx->list, &file->ctx_list); @@ -180,23 +172,15 @@ error: static struct ucma_multicast* ucma_alloc_multicast(struct ucma_context *ctx) { struct ucma_multicast *mc; - int ret; mc = kzalloc(sizeof(*mc), GFP_KERNEL); if (!mc) return NULL; - do { - ret = idr_pre_get(&multicast_idr, GFP_KERNEL); - if (!ret) - goto error; - - mutex_lock(&mut); - ret = idr_get_new(&multicast_idr, mc, &mc->id); - mutex_unlock(&mut); - } while (ret == -EAGAIN); - - if (ret) + mutex_lock(&mut); + mc->id = idr_alloc(&multicast_idr, mc, 0, 0, GFP_KERNEL); + mutex_unlock(&mut); + if (mc->id < 0) goto error; mc->ctx = ctx; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 0cb0007724a2..83bc309cea5c 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -124,18 +124,17 @@ static int idr_add_uobj(struct idr *idr, struct ib_uobject *uobj) { int ret; -retry: - if (!idr_pre_get(idr, GFP_KERNEL)) - return -ENOMEM; - + idr_preload(GFP_KERNEL); spin_lock(&ib_uverbs_idr_lock); - ret = idr_get_new(idr, uobj, &uobj->id); - spin_unlock(&ib_uverbs_idr_lock); - if (ret == -EAGAIN) - goto retry; + ret = idr_alloc(idr, uobj, 0, 0, GFP_NOWAIT); + if (ret >= 0) + uobj->id = ret; - return ret; + spin_unlock(&ib_uverbs_idr_lock); + idr_preload_end(); + + return ret < 0 ? ret : 0; } void idr_remove_uobj(struct idr *idr, struct ib_uobject *uobj) diff --git a/drivers/infiniband/hw/amso1100/c2_qp.c b/drivers/infiniband/hw/amso1100/c2_qp.c index 28cd5cb51859..0ab826b280b2 100644 --- a/drivers/infiniband/hw/amso1100/c2_qp.c +++ b/drivers/infiniband/hw/amso1100/c2_qp.c @@ -382,14 +382,17 @@ static int c2_alloc_qpn(struct c2_dev *c2dev, struct c2_qp *qp) { int ret; - do { - spin_lock_irq(&c2dev->qp_table.lock); - ret = idr_get_new_above(&c2dev->qp_table.idr, qp, - c2dev->qp_table.last++, &qp->qpn); - spin_unlock_irq(&c2dev->qp_table.lock); - } while ((ret == -EAGAIN) && - idr_pre_get(&c2dev->qp_table.idr, GFP_KERNEL)); - return ret; + idr_preload(GFP_KERNEL); + spin_lock_irq(&c2dev->qp_table.lock); + + ret = idr_alloc(&c2dev->qp_table.idr, qp, c2dev->qp_table.last++, 0, + GFP_NOWAIT); + if (ret >= 0) + qp->qpn = ret; + + spin_unlock_irq(&c2dev->qp_table.lock); + idr_preload_end(); + return ret < 0 ? ret : 0; } static void c2_free_qpn(struct c2_dev *c2dev, int qpn) diff --git a/drivers/infiniband/hw/cxgb3/iwch.h b/drivers/infiniband/hw/cxgb3/iwch.h index a1c44578e039..837862287a29 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.h +++ b/drivers/infiniband/hw/cxgb3/iwch.h @@ -153,19 +153,17 @@ static inline int insert_handle(struct iwch_dev *rhp, struct idr *idr, void *handle, u32 id) { int ret; - int newid; - - do { - if (!idr_pre_get(idr, GFP_KERNEL)) { - return -ENOMEM; - } - spin_lock_irq(&rhp->lock); - ret = idr_get_new_above(idr, handle, id, &newid); - BUG_ON(newid != id); - spin_unlock_irq(&rhp->lock); - } while (ret == -EAGAIN); - - return ret; + + idr_preload(GFP_KERNEL); + spin_lock_irq(&rhp->lock); + + ret = idr_alloc(idr, handle, id, id + 1, GFP_NOWAIT); + + spin_unlock_irq(&rhp->lock); + idr_preload_end(); + + BUG_ON(ret == -ENOSPC); + return ret < 0 ? ret : 0; } static inline void remove_handle(struct iwch_dev *rhp, struct idr *idr, u32 id) diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 9c1644fb0259..7f862da54e8e 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -260,20 +260,21 @@ static inline int _insert_handle(struct c4iw_dev *rhp, struct idr *idr, void *handle, u32 id, int lock) { int ret; - int newid; - do { - if (!idr_pre_get(idr, lock ? GFP_KERNEL : GFP_ATOMIC)) - return -ENOMEM; - if (lock) - spin_lock_irq(&rhp->lock); - ret = idr_get_new_above(idr, handle, id, &newid); - BUG_ON(!ret && newid != id); - if (lock) - spin_unlock_irq(&rhp->lock); - } while (ret == -EAGAIN); - - return ret; + if (lock) { + idr_preload(GFP_KERNEL); + spin_lock_irq(&rhp->lock); + } + + ret = idr_alloc(idr, handle, id, id + 1, GFP_ATOMIC); + + if (lock) { + spin_unlock_irq(&rhp->lock); + idr_preload_end(); + } + + BUG_ON(ret == -ENOSPC); + return ret < 0 ? ret : 0; } static inline int insert_handle(struct c4iw_dev *rhp, struct idr *idr, diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c index 8f5290147e8a..212150c25ea0 100644 --- a/drivers/infiniband/hw/ehca/ehca_cq.c +++ b/drivers/infiniband/hw/ehca/ehca_cq.c @@ -128,7 +128,7 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector, void *vpage; u32 counter; u64 rpage, cqx_fec, h_ret; - int ipz_rc, ret, i; + int ipz_rc, i; unsigned long flags; if (cqe >= 0xFFFFFFFF - 64 - additional_cqe) @@ -163,32 +163,19 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector, adapter_handle = shca->ipz_hca_handle; param.eq_handle = shca->eq.ipz_eq_handle; - do { - if (!idr_pre_get(&ehca_cq_idr, GFP_KERNEL)) { - cq = ERR_PTR(-ENOMEM); - ehca_err(device, "Can't reserve idr nr. device=%p", - device); - goto create_cq_exit1; - } - - write_lock_irqsave(&ehca_cq_idr_lock, flags); - ret = idr_get_new(&ehca_cq_idr, my_cq, &my_cq->token); - write_unlock_irqrestore(&ehca_cq_idr_lock, flags); - } while (ret == -EAGAIN); + idr_preload(GFP_KERNEL); + write_lock_irqsave(&ehca_cq_idr_lock, flags); + my_cq->token = idr_alloc(&ehca_cq_idr, my_cq, 0, 0x2000000, GFP_NOWAIT); + write_unlock_irqrestore(&ehca_cq_idr_lock, flags); + idr_preload_end(); - if (ret) { + if (my_cq->token < 0) { cq = ERR_PTR(-ENOMEM); ehca_err(device, "Can't allocate new idr entry. device=%p", device); goto create_cq_exit1; } - if (my_cq->token > 0x1FFFFFF) { - cq = ERR_PTR(-ENOMEM); - ehca_err(device, "Invalid number of cq. device=%p", device); - goto create_cq_exit2; - } - /* * CQs maximum depth is 4GB-64, but we need additional 20 as buffer * for receiving errors CQEs. diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index 149393915ae5..00d6861a6a18 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -636,30 +636,26 @@ static struct ehca_qp *internal_create_qp( my_qp->send_cq = container_of(init_attr->send_cq, struct ehca_cq, ib_cq); - do { - if (!idr_pre_get(&ehca_qp_idr, GFP_KERNEL)) { - ret = -ENOMEM; - ehca_err(pd->device, "Can't reserve idr resources."); - goto create_qp_exit0; - } + idr_preload(GFP_KERNEL); + write_lock_irqsave(&ehca_qp_idr_lock, flags); - write_lock_irqsave(&ehca_qp_idr_lock, flags); - ret = idr_get_new(&ehca_qp_idr, my_qp, &my_qp->token); - write_unlock_irqrestore(&ehca_qp_idr_lock, flags); - } while (ret == -EAGAIN); + ret = idr_alloc(&ehca_qp_idr, my_qp, 0, 0x2000000, GFP_NOWAIT); + if (ret >= 0) + my_qp->token = ret; - if (ret) { - ret = -ENOMEM; - ehca_err(pd->device, "Can't allocate new idr entry."); + write_unlock_irqrestore(&ehca_qp_idr_lock, flags); + idr_preload_end(); + if (ret < 0) { + if (ret == -ENOSPC) { + ret = -EINVAL; + ehca_err(pd->device, "Invalid number of qp"); + } else { + ret = -ENOMEM; + ehca_err(pd->device, "Can't allocate new idr entry."); + } goto create_qp_exit0; } - if (my_qp->token > 0x1FFFFFF) { - ret = -EINVAL; - ehca_err(pd->device, "Invalid number of qp"); - goto create_qp_exit1; - } - if (has_srq) parms.srq_token = my_qp->token; diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 7b371f545ece..fcdaeea9f7de 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -194,11 +194,6 @@ static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) struct ipath_devdata *dd; int ret; - if (!idr_pre_get(&unit_table, GFP_KERNEL)) { - dd = ERR_PTR(-ENOMEM); - goto bail; - } - dd = vzalloc(sizeof(*dd)); if (!dd) { dd = ERR_PTR(-ENOMEM); @@ -206,9 +201,10 @@ static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) } dd->ipath_unit = -1; + idr_preload(GFP_KERNEL); spin_lock_irqsave(&ipath_devs_lock, flags); - ret = idr_get_new(&unit_table, dd, &dd->ipath_unit); + ret = idr_alloc(&unit_table, dd, 0, 0, GFP_KERNEL); if (ret < 0) { printk(KERN_ERR IPATH_DRV_NAME ": Could not allocate unit ID: error %d\n", -ret); @@ -216,6 +212,7 @@ static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) dd = ERR_PTR(ret); goto bail_unlock; } + dd->ipath_unit = ret; dd->pcidev = pdev; pci_set_drvdata(pdev, dd); @@ -224,7 +221,7 @@ static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) bail_unlock: spin_unlock_irqrestore(&ipath_devs_lock, flags); - + idr_preload_end(); bail: return dd; } @@ -2503,11 +2500,6 @@ static int __init infinipath_init(void) * the PCI subsystem. */ idr_init(&unit_table); - if (!idr_pre_get(&unit_table, GFP_KERNEL)) { - printk(KERN_ERR IPATH_DRV_NAME ": idr_pre_get() failed\n"); - ret = -ENOMEM; - goto bail; - } ret = pci_register_driver(&ipath_driver); if (ret < 0) { diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 3eb7e454849b..62edc414e162 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -40,6 +40,7 @@ #include <linux/slab.h> #include <linux/highmem.h> #include <linux/io.h> +#include <linux/aio.h> #include <linux/jiffies.h> #include <linux/cpu.h> #include <asm/pgtable.h> diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index dbc99d41605c..80e59ed864b3 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -203,7 +203,7 @@ static void sl_id_map_add(struct ib_device *ibdev, struct id_map_entry *new) static struct id_map_entry * id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) { - int ret, id; + int ret; static int next_id; struct id_map_entry *ent; struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov; @@ -220,25 +220,23 @@ id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) ent->dev = to_mdev(ibdev); INIT_DELAYED_WORK(&ent->timeout, id_map_ent_timeout); - do { - spin_lock(&to_mdev(ibdev)->sriov.id_map_lock); - ret = idr_get_new_above(&sriov->pv_id_table, ent, - next_id, &id); - if (!ret) { - next_id = ((unsigned) id + 1) & MAX_IDR_MASK; - ent->pv_cm_id = (u32)id; - sl_id_map_add(ibdev, ent); - } + idr_preload(GFP_KERNEL); + spin_lock(&to_mdev(ibdev)->sriov.id_map_lock); - spin_unlock(&sriov->id_map_lock); - } while (ret == -EAGAIN && idr_pre_get(&sriov->pv_id_table, GFP_KERNEL)); - /*the function idr_get_new_above can return -ENOSPC, so don't insert in that case.*/ - if (!ret) { - spin_lock(&sriov->id_map_lock); + ret = idr_alloc(&sriov->pv_id_table, ent, next_id, 0, GFP_NOWAIT); + if (ret >= 0) { + next_id = ((unsigned)ret + 1) & MAX_IDR_MASK; + ent->pv_cm_id = (u32)ret; + sl_id_map_add(ibdev, ent); list_add_tail(&ent->list, &sriov->cm_list); - spin_unlock(&sriov->id_map_lock); - return ent; } + + spin_unlock(&sriov->id_map_lock); + idr_preload_end(); + + if (ret >= 0) + return ent; + /*error flow*/ kfree(ent); mlx4_ib_warn(ibdev, "No more space in the idr (err:0x%x)\n", ret); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index c4e0131f1b57..48928c8e7774 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -51,18 +51,6 @@ static DEFINE_IDR(ocrdma_dev_id); static union ib_gid ocrdma_zero_sgid; -static int ocrdma_get_instance(void) -{ - int instance = 0; - - /* Assign an unused number */ - if (!idr_pre_get(&ocrdma_dev_id, GFP_KERNEL)) - return -1; - if (idr_get_new(&ocrdma_dev_id, NULL, &instance)) - return -1; - return instance; -} - void ocrdma_get_guid(struct ocrdma_dev *dev, u8 *guid) { u8 mac_addr[6]; @@ -416,7 +404,7 @@ static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info) goto idr_err; memcpy(&dev->nic_info, dev_info, sizeof(*dev_info)); - dev->id = ocrdma_get_instance(); + dev->id = idr_alloc(&ocrdma_dev_id, NULL, 0, 0, GFP_KERNEL); if (dev->id < 0) goto idr_err; diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 959a5c4ff812..488300c3c330 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -39,7 +39,7 @@ #include <linux/vmalloc.h> #include <linux/highmem.h> #include <linux/io.h> -#include <linux/uio.h> +#include <linux/aio.h> #include <linux/jiffies.h> #include <asm/pgtable.h> #include <linux/delay.h> diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index ddf066d9abb6..50e33aa0b4e3 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -1060,22 +1060,23 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra) struct qib_devdata *dd; int ret; - if (!idr_pre_get(&qib_unit_table, GFP_KERNEL)) { - dd = ERR_PTR(-ENOMEM); - goto bail; - } - dd = (struct qib_devdata *) ib_alloc_device(sizeof(*dd) + extra); if (!dd) { dd = ERR_PTR(-ENOMEM); goto bail; } + idr_preload(GFP_KERNEL); spin_lock_irqsave(&qib_devs_lock, flags); - ret = idr_get_new(&qib_unit_table, dd, &dd->unit); - if (ret >= 0) + + ret = idr_alloc(&qib_unit_table, dd, 0, 0, GFP_NOWAIT); + if (ret >= 0) { + dd->unit = ret; list_add(&dd->list, &qib_dev_list); + } + spin_unlock_irqrestore(&qib_devs_lock, flags); + idr_preload_end(); if (ret < 0) { qib_early_err(&pdev->dev, @@ -1180,11 +1181,6 @@ static int __init qlogic_ib_init(void) * the PCI subsystem. */ idr_init(&qib_unit_table); - if (!idr_pre_get(&qib_unit_table, GFP_KERNEL)) { - pr_err("idr_pre_get() failed\n"); - ret = -ENOMEM; - goto bail_cq_wq; - } ret = pci_register_driver(&qib_driver); if (ret < 0) { @@ -1199,7 +1195,6 @@ static int __init qlogic_ib_init(void) bail_unit: idr_destroy(&qib_unit_table); -bail_cq_wq: destroy_workqueue(qib_cq_wq); bail_dev: qib_dev_cleanup(); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index abe2d699b6f3..8b07f83d48ad 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -483,7 +483,6 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; struct sock *sk = sock->sk; - struct hlist_node *node; struct sock *csk; int err = 0; @@ -508,7 +507,7 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) if (sk->sk_protocol < ISDN_P_B_START) { read_lock_bh(&data_sockets.lock); - sk_for_each(csk, node, &data_sockets.head) { + sk_for_each(csk, &data_sockets.head) { if (sk == csk) continue; if (_pms(csk)->dev != _pms(sk)->dev) diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index deda591f70b9..9cb4b621fbc3 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -64,12 +64,11 @@ unlock: static void send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) { - struct hlist_node *node; struct sock *sk; struct sk_buff *cskb = NULL; read_lock(&sl->lock); - sk_for_each(sk, node, &sl->head) { + sk_for_each(sk, &sl->head) { if (sk->sk_state != MISDN_BOUND) continue; if (!cskb) diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c index ee14662ed5ce..98cae529373f 100644 --- a/drivers/leds/leds-ot200.c +++ b/drivers/leds/leds-ot200.c @@ -47,37 +47,37 @@ static struct ot200_led leds[] = { { .name = "led_1", .port = 0x49, - .mask = BIT(7), + .mask = BIT(6), }, { .name = "led_2", .port = 0x49, - .mask = BIT(6), + .mask = BIT(5), }, { .name = "led_3", .port = 0x49, - .mask = BIT(5), + .mask = BIT(4), }, { .name = "led_4", .port = 0x49, - .mask = BIT(4), + .mask = BIT(3), }, { .name = "led_5", .port = 0x49, - .mask = BIT(3), + .mask = BIT(2), }, { .name = "led_6", .port = 0x49, - .mask = BIT(2), + .mask = BIT(1), }, { .name = "led_7", .port = 0x49, - .mask = BIT(1), + .mask = BIT(0), } }; diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c index 4d670bbe963c..144067c95aba 100644 --- a/drivers/md/dm-bio-prison.c +++ b/drivers/md/dm-bio-prison.c @@ -111,9 +111,8 @@ static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket, struct dm_cell_key *key) { struct dm_bio_prison_cell *cell; - struct hlist_node *tmp; - hlist_for_each_entry(cell, tmp, bucket, list) + hlist_for_each_entry(cell, bucket, list) if (keys_equal(&cell->key, key)) return cell; diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index f12fdbcc4f76..3c955e10a618 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -859,9 +859,8 @@ static void __check_watermark(struct dm_bufio_client *c) static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block) { struct dm_buffer *b; - struct hlist_node *hn; - hlist_for_each_entry(b, hn, &c->cache_hash[DM_BUFIO_HASH(block)], + hlist_for_each_entry(b, &c->cache_hash[DM_BUFIO_HASH(block)], hash_list) { dm_bufio_cond_resched(); if (b->block == block) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index dcbd4f6957ce..c3678650a607 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -227,12 +227,11 @@ static void stop_tracking_chunk(struct dm_snapshot *s, struct bio *bio) static int __chunk_is_tracked(struct dm_snapshot *s, chunk_t chunk) { struct dm_snap_tracked_chunk *c; - struct hlist_node *hn; int found = 0; spin_lock_irq(&s->tracked_chunk_lock); - hlist_for_each_entry(c, hn, + hlist_for_each_entry(c, &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)], node) { if (c->chunk == chunk) { found = 1; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 5b7bc58a5ea3..ac74b81bf1d4 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -318,7 +318,6 @@ static void __exit dm_exit(void) /* * Should be empty by this point. */ - idr_remove_all(&_minor_idr); idr_destroy(&_minor_idr); } @@ -711,7 +710,7 @@ static void end_clone_bio(struct bio *clone, int error) * Do not use blk_end_request() here, because it may complete * the original request before the clone, and break the ordering. */ - blk_update_request(tio->orig, 0, nr_bytes); + blk_update_request(tio->orig, 0, nr_bytes, NULL); } /* @@ -1802,62 +1801,38 @@ static void free_minor(int minor) */ static int specific_minor(int minor) { - int r, m; + int r; if (minor >= (1 << MINORBITS)) return -EINVAL; - r = idr_pre_get(&_minor_idr, GFP_KERNEL); - if (!r) - return -ENOMEM; - + idr_preload(GFP_KERNEL); spin_lock(&_minor_lock); - if (idr_find(&_minor_idr, minor)) { - r = -EBUSY; - goto out; - } - - r = idr_get_new_above(&_minor_idr, MINOR_ALLOCED, minor, &m); - if (r) - goto out; + r = idr_alloc(&_minor_idr, MINOR_ALLOCED, minor, minor + 1, GFP_NOWAIT); - if (m != minor) { - idr_remove(&_minor_idr, m); - r = -EBUSY; - goto out; - } - -out: spin_unlock(&_minor_lock); - return r; + idr_preload_end(); + if (r < 0) + return r == -ENOSPC ? -EBUSY : r; + return 0; } static int next_free_minor(int *minor) { - int r, m; - - r = idr_pre_get(&_minor_idr, GFP_KERNEL); - if (!r) - return -ENOMEM; + int r; + idr_preload(GFP_KERNEL); spin_lock(&_minor_lock); - r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m); - if (r) - goto out; - - if (m >= (1 << MINORBITS)) { - idr_remove(&_minor_idr, m); - r = -ENOSPC; - goto out; - } - - *minor = m; + r = idr_alloc(&_minor_idr, MINOR_ALLOCED, 0, 1 << MINORBITS, GFP_NOWAIT); -out: spin_unlock(&_minor_lock); - return r; + idr_preload_end(); + if (r < 0) + return r; + *minor = r; + return 0; } static const struct block_device_operations dm_blk_dops; diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index d247a35da3c6..81da1a26042e 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -25,8 +25,8 @@ struct shadow_info { /* * It would be nice if we scaled with the size of transaction. */ -#define HASH_SIZE 256 -#define HASH_MASK (HASH_SIZE - 1) +#define DM_HASH_SIZE 256 +#define DM_HASH_MASK (DM_HASH_SIZE - 1) struct dm_transaction_manager { int is_clone; @@ -36,7 +36,7 @@ struct dm_transaction_manager { struct dm_space_map *sm; spinlock_t lock; - struct hlist_head buckets[HASH_SIZE]; + struct hlist_head buckets[DM_HASH_SIZE]; }; /*----------------------------------------------------------------*/ @@ -44,12 +44,11 @@ struct dm_transaction_manager { static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b) { int r = 0; - unsigned bucket = dm_hash_block(b, HASH_MASK); + unsigned bucket = dm_hash_block(b, DM_HASH_MASK); struct shadow_info *si; - struct hlist_node *n; spin_lock(&tm->lock); - hlist_for_each_entry(si, n, tm->buckets + bucket, hlist) + hlist_for_each_entry(si, tm->buckets + bucket, hlist) if (si->where == b) { r = 1; break; @@ -71,7 +70,7 @@ static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b) si = kmalloc(sizeof(*si), GFP_NOIO); if (si) { si->where = b; - bucket = dm_hash_block(b, HASH_MASK); + bucket = dm_hash_block(b, DM_HASH_MASK); spin_lock(&tm->lock); hlist_add_head(&si->hlist, tm->buckets + bucket); spin_unlock(&tm->lock); @@ -81,14 +80,14 @@ static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b) static void wipe_shadow_table(struct dm_transaction_manager *tm) { struct shadow_info *si; - struct hlist_node *n, *tmp; + struct hlist_node *tmp; struct hlist_head *bucket; int i; spin_lock(&tm->lock); - for (i = 0; i < HASH_SIZE; i++) { + for (i = 0; i < DM_HASH_SIZE; i++) { bucket = tm->buckets + i; - hlist_for_each_entry_safe(si, n, tmp, bucket, hlist) + hlist_for_each_entry_safe(si, tmp, bucket, hlist) kfree(si); INIT_HLIST_HEAD(bucket); @@ -115,7 +114,7 @@ static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm, tm->sm = sm; spin_lock_init(&tm->lock); - for (i = 0; i < HASH_SIZE; i++) + for (i = 0; i < DM_HASH_SIZE; i++) INIT_HLIST_HEAD(tm->buckets + i); return tm; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 77c4a585fb82..96ea6200b301 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -389,10 +389,9 @@ static struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector, short generation) { struct stripe_head *sh; - struct hlist_node *hn; pr_debug("__find_stripe, sector %llu\n", (unsigned long long)sector); - hlist_for_each_entry(sh, hn, stripe_hash(conf, sector), hash) + hlist_for_each_entry(sh, stripe_hash(conf, sector), hash) if (sh->sector == sector && sh->generation == generation) return sh; pr_debug("__stripe %llu not in cache\n", (unsigned long long)sector); diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 56ff19cdc2ad..ffcb10ac4341 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -512,18 +512,17 @@ int memstick_add_host(struct memstick_host *host) { int rc; - while (1) { - if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL)) - return -ENOMEM; + idr_preload(GFP_KERNEL); + spin_lock(&memstick_host_lock); - spin_lock(&memstick_host_lock); - rc = idr_get_new(&memstick_host_idr, host, &host->id); - spin_unlock(&memstick_host_lock); - if (!rc) - break; - else if (rc != -EAGAIN) - return rc; - } + rc = idr_alloc(&memstick_host_idr, host, 0, 0, GFP_NOWAIT); + if (rc >= 0) + host->id = rc; + + spin_unlock(&memstick_host_lock); + idr_preload_end(); + if (rc < 0) + return rc; dev_set_name(&host->dev, "memstick%u", host->id); diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 9729b92fbfdd..f12b78dbce04 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -1213,21 +1213,10 @@ static int mspro_block_init_disk(struct memstick_dev *card) msb->page_size = be16_to_cpu(sys_info->unit_size); mutex_lock(&mspro_block_disk_lock); - if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL)) { - mutex_unlock(&mspro_block_disk_lock); - return -ENOMEM; - } - - rc = idr_get_new(&mspro_block_disk_idr, card, &disk_id); + disk_id = idr_alloc(&mspro_block_disk_idr, card, 0, 256, GFP_KERNEL); mutex_unlock(&mspro_block_disk_lock); - - if (rc) - return rc; - - if ((disk_id << MSPRO_BLOCK_PART_SHIFT) > 255) { - rc = -ENOSPC; - goto out_release_id; - } + if (disk_id < 0) + return disk_id; msb->disk = alloc_disk(1 << MSPRO_BLOCK_PART_SHIFT); if (!msb->disk) { diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 822237e322ba..45e97b443cd4 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -1061,15 +1061,14 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, } handle->pcr = pcr; - if (!idr_pre_get(&rtsx_pci_idr, GFP_KERNEL)) { - ret = -ENOMEM; - goto free_handle; - } - + idr_preload(GFP_KERNEL); spin_lock(&rtsx_pci_lock); - ret = idr_get_new(&rtsx_pci_idr, pcr, &pcr->id); + ret = idr_alloc(&rtsx_pci_idr, pcr, 0, 0, GFP_NOWAIT); + if (ret >= 0) + pcr->id = ret; spin_unlock(&rtsx_pci_lock); - if (ret) + idr_preload_end(); + if (ret < 0) goto free_handle; pcr->pci = pcidev; diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index f428d86bfc10..f32550a74bdd 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -885,7 +885,7 @@ struct c2port_device *c2port_device_register(char *name, struct c2port_ops *ops, void *devdata) { struct c2port_device *c2dev; - int id, ret; + int ret; if (unlikely(!ops) || unlikely(!ops->access) || \ unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ @@ -897,22 +897,18 @@ struct c2port_device *c2port_device_register(char *name, if (unlikely(!c2dev)) return ERR_PTR(-ENOMEM); - ret = idr_pre_get(&c2port_idr, GFP_KERNEL); - if (!ret) { - ret = -ENOMEM; - goto error_idr_get_new; - } - + idr_preload(GFP_KERNEL); spin_lock_irq(&c2port_idr_lock); - ret = idr_get_new(&c2port_idr, c2dev, &id); + ret = idr_alloc(&c2port_idr, c2dev, 0, 0, GFP_NOWAIT); spin_unlock_irq(&c2port_idr_lock); + idr_preload_end(); if (ret < 0) - goto error_idr_get_new; - c2dev->id = id; + goto error_idr_alloc; + c2dev->id = ret; c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, - "c2port%d", id); + "c2port%d", c2dev->id); if (unlikely(IS_ERR(c2dev->dev))) { ret = PTR_ERR(c2dev->dev); goto error_device_create; @@ -946,10 +942,10 @@ error_device_create_bin_file: error_device_create: spin_lock_irq(&c2port_idr_lock); - idr_remove(&c2port_idr, id); + idr_remove(&c2port_idr, c2dev->id); spin_unlock_irq(&c2port_idr_lock); -error_idr_get_new: +error_idr_alloc: kfree(c2dev); return ERR_PTR(ret); diff --git a/drivers/misc/sgi-gru/grutlbpurge.c b/drivers/misc/sgi-gru/grutlbpurge.c index 240a6d361665..2129274ef7ab 100644 --- a/drivers/misc/sgi-gru/grutlbpurge.c +++ b/drivers/misc/sgi-gru/grutlbpurge.c @@ -280,11 +280,10 @@ static struct mmu_notifier *mmu_find_ops(struct mm_struct *mm, const struct mmu_notifier_ops *ops) { struct mmu_notifier *mn, *gru_mn = NULL; - struct hlist_node *n; if (mm->mmu_notifier_mm) { rcu_read_lock(); - hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, + hlist_for_each_entry_rcu(mn, &mm->mmu_notifier_mm->list, hlist) if (mn->ops == ops) { gru_mn = mn; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 0bd5349b0422..0ab7c922212c 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -196,13 +196,14 @@ int tifm_add_adapter(struct tifm_adapter *fm) { int rc; - if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL)) - return -ENOMEM; - + idr_preload(GFP_KERNEL); spin_lock(&tifm_adapter_lock); - rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); + rc = idr_alloc(&tifm_adapter_idr, fm, 0, 0, GFP_NOWAIT); + if (rc >= 0) + fm->id = rc; spin_unlock(&tifm_adapter_lock); - if (rc) + idr_preload_end(); + if (rc < 0) return rc; dev_set_name(&fm->dev, "tifm%u", fm->id); diff --git a/drivers/misc/vmw_vmci/vmci_doorbell.c b/drivers/misc/vmw_vmci/vmci_doorbell.c index c3e8397f62ed..a8cee33ae8d2 100644 --- a/drivers/misc/vmw_vmci/vmci_doorbell.c +++ b/drivers/misc/vmw_vmci/vmci_doorbell.c @@ -127,9 +127,8 @@ static struct dbell_entry *dbell_index_table_find(u32 idx) { u32 bucket = VMCI_DOORBELL_HASH(idx); struct dbell_entry *dbell; - struct hlist_node *node; - hlist_for_each_entry(dbell, node, &vmci_doorbell_it.entries[bucket], + hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) { if (idx == dbell->idx) return dbell; @@ -359,12 +358,10 @@ static void dbell_fire_entries(u32 notify_idx) { u32 bucket = VMCI_DOORBELL_HASH(notify_idx); struct dbell_entry *dbell; - struct hlist_node *node; spin_lock_bh(&vmci_doorbell_it.lock); - hlist_for_each_entry(dbell, node, - &vmci_doorbell_it.entries[bucket], node) { + hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) { if (dbell->idx == notify_idx && atomic_read(&dbell->active) == 1) { if (dbell->run_delayed) { diff --git a/drivers/misc/vmw_vmci/vmci_resource.c b/drivers/misc/vmw_vmci/vmci_resource.c index a196f84a4fd2..9a53a30de445 100644 --- a/drivers/misc/vmw_vmci/vmci_resource.c +++ b/drivers/misc/vmw_vmci/vmci_resource.c @@ -46,11 +46,10 @@ static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle, enum vmci_resource_type type) { struct vmci_resource *r, *resource = NULL; - struct hlist_node *node; unsigned int idx = vmci_resource_hash(handle); rcu_read_lock(); - hlist_for_each_entry_rcu(r, node, + hlist_for_each_entry_rcu(r, &vmci_resource_table.entries[idx], node) { u32 cid = r->handle.context; u32 rid = r->handle.resource; @@ -146,12 +145,11 @@ void vmci_resource_remove(struct vmci_resource *resource) struct vmci_handle handle = resource->handle; unsigned int idx = vmci_resource_hash(handle); struct vmci_resource *r; - struct hlist_node *node; /* Remove resource from hash table. */ spin_lock(&vmci_resource_table.lock); - hlist_for_each_entry(r, node, &vmci_resource_table.entries[idx], node) { + hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) { if (vmci_handle_is_equal(r->handle, resource->handle)) { hlist_del_init_rcu(&r->node); break; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index ee2e16b17017..1117de7c3410 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -306,19 +306,20 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) int err; struct mmc_host *host; - if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) - return NULL; - host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (!host) return NULL; /* scanning will be enabled when we're ready */ host->rescan_disable = 1; + idr_preload(GFP_KERNEL); spin_lock(&mmc_host_lock); - err = idr_get_new(&mmc_host_idr, host, &host->index); + err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT); + if (err >= 0) + host->index = err; spin_unlock(&mmc_host_lock); - if (err) + idr_preload_end(); + if (err < 0) goto free; dev_set_name(&host->class_dev, "mmc%d", host->index); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ec794a72975d..61d5f56473e1 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -349,13 +349,8 @@ int add_mtd_device(struct mtd_info *mtd) BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); - do { - if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) - goto fail_locked; - error = idr_get_new(&mtd_idr, mtd, &i); - } while (error == -EAGAIN); - - if (error) + i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL); + if (i < 0) goto fail_locked; mtd->index = i; diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index 5015d10591d9..70106607c247 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -256,7 +256,7 @@ static int nand_ecc_test_run(const size_t size) goto error; } - get_random_bytes(correct_data, size); + prandom_bytes(correct_data, size); __nand_calculate_ecc(correct_data, size, correct_ecc); for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) { diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index e827fa8cd844..3e24b379ffa4 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -29,6 +29,7 @@ #include <linux/mtd/mtd.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/random.h> static int dev = -EINVAL; module_param(dev, int, S_IRUGO); @@ -46,26 +47,7 @@ static int use_offset; static int use_len; static int use_len_max; static int vary_offset; -static unsigned long next = 1; - -static inline unsigned int simple_rand(void) -{ - next = next * 1103515245 + 12345; - return (unsigned int)((next / 65536) % 32768); -} - -static inline void simple_srand(unsigned long seed) -{ - next = seed; -} - -static void set_random_data(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) - buf[i] = simple_rand(); -} +static struct rnd_state rnd_state; static int erase_eraseblock(int ebnum) { @@ -129,7 +111,7 @@ static int write_eraseblock(int ebnum) loff_t addr = ebnum * mtd->erasesize; for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { - set_random_data(writebuf, use_len); + prandom_bytes_state(&rnd_state, writebuf, use_len); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; @@ -182,7 +164,7 @@ static int verify_eraseblock(int ebnum) loff_t addr = ebnum * mtd->erasesize; for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { - set_random_data(writebuf, use_len); + prandom_bytes_state(&rnd_state, writebuf, use_len); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; @@ -273,7 +255,7 @@ static int verify_eraseblock_in_one_go(int ebnum) loff_t addr = ebnum * mtd->erasesize; size_t len = mtd->ecclayout->oobavail * pgcnt; - set_random_data(writebuf, len); + prandom_bytes_state(&rnd_state, writebuf, len); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; @@ -424,12 +406,12 @@ static int __init mtd_oobtest_init(void) if (err) goto out; - simple_srand(1); + prandom_seed_state(&rnd_state, 1); err = write_whole_device(); if (err) goto out; - simple_srand(1); + prandom_seed_state(&rnd_state, 1); err = verify_all_eraseblocks(); if (err) goto out; @@ -444,13 +426,13 @@ static int __init mtd_oobtest_init(void) if (err) goto out; - simple_srand(3); + prandom_seed_state(&rnd_state, 3); err = write_whole_device(); if (err) goto out; /* Check all eraseblocks */ - simple_srand(3); + prandom_seed_state(&rnd_state, 3); pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -479,7 +461,7 @@ static int __init mtd_oobtest_init(void) use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 1; - simple_srand(5); + prandom_seed_state(&rnd_state, 5); err = write_whole_device(); if (err) @@ -490,7 +472,7 @@ static int __init mtd_oobtest_init(void) use_len = mtd->ecclayout->oobavail; use_len_max = mtd->ecclayout->oobavail; vary_offset = 1; - simple_srand(5); + prandom_seed_state(&rnd_state, 5); err = verify_all_eraseblocks(); if (err) goto out; @@ -649,7 +631,7 @@ static int __init mtd_oobtest_init(void) goto out; /* Write all eraseblocks */ - simple_srand(11); + prandom_seed_state(&rnd_state, 11); pr_info("writing OOBs of whole device\n"); for (i = 0; i < ebcnt - 1; ++i) { int cnt = 2; @@ -659,7 +641,7 @@ static int __init mtd_oobtest_init(void) continue; addr = (i + 1) * mtd->erasesize - mtd->writesize; for (pg = 0; pg < cnt; ++pg) { - set_random_data(writebuf, sz); + prandom_bytes_state(&rnd_state, writebuf, sz); ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; ops.retlen = 0; @@ -680,12 +662,13 @@ static int __init mtd_oobtest_init(void) pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ - simple_srand(11); + prandom_seed_state(&rnd_state, 11); pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt - 1; ++i) { if (bbt[i] || bbt[i + 1]) continue; - set_random_data(writebuf, mtd->ecclayout->oobavail * 2); + prandom_bytes_state(&rnd_state, writebuf, + mtd->ecclayout->oobavail * 2); addr = (i + 1) * mtd->erasesize - mtd->writesize; ops.mode = MTD_OPS_AUTO_OOB; ops.len = 0; diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index f93a76f88113..0c1140b6c286 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -29,6 +29,7 @@ #include <linux/mtd/mtd.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/random.h> static int dev = -EINVAL; module_param(dev, int, S_IRUGO); @@ -45,26 +46,7 @@ static int bufsize; static int ebcnt; static int pgcnt; static int errcnt; -static unsigned long next = 1; - -static inline unsigned int simple_rand(void) -{ - next = next * 1103515245 + 12345; - return (unsigned int)((next / 65536) % 32768); -} - -static inline void simple_srand(unsigned long seed) -{ - next = seed; -} - -static void set_random_data(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) - buf[i] = simple_rand(); -} +static struct rnd_state rnd_state; static int erase_eraseblock(int ebnum) { @@ -98,7 +80,7 @@ static int write_eraseblock(int ebnum) size_t written; loff_t addr = ebnum * mtd->erasesize; - set_random_data(writebuf, mtd->erasesize); + prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); cond_resched(); err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); if (err || written != mtd->erasesize) @@ -124,7 +106,7 @@ static int verify_eraseblock(int ebnum) for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) addrn -= mtd->erasesize; - set_random_data(writebuf, mtd->erasesize); + prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { /* Do a read to set the internal dataRAMs to different data */ err = mtd_read(mtd, addr0, bufsize, &read, twopages); @@ -160,7 +142,8 @@ static int verify_eraseblock(int ebnum) } /* Check boundary between eraseblocks */ if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { - unsigned long oldnext = next; + struct rnd_state old_state = rnd_state; + /* Do a read to set the internal dataRAMs to different data */ err = mtd_read(mtd, addr0, bufsize, &read, twopages); if (mtd_is_bitflip(err)) @@ -188,13 +171,13 @@ static int verify_eraseblock(int ebnum) return err; } memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); - set_random_data(boundary + pgsize, pgsize); + prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); if (memcmp(twopages, boundary, bufsize)) { pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } - next = oldnext; + rnd_state = old_state; } return err; } @@ -326,7 +309,7 @@ static int erasecrosstest(void) return err; pr_info("writing 1st page of block %d\n", ebnum); - set_random_data(writebuf, pgsize); + prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { @@ -359,7 +342,7 @@ static int erasecrosstest(void) return err; pr_info("writing 1st page of block %d\n", ebnum); - set_random_data(writebuf, pgsize); + prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { @@ -417,7 +400,7 @@ static int erasetest(void) return err; pr_info("writing 1st page of block %d\n", ebnum); - set_random_data(writebuf, pgsize); + prandom_bytes_state(&rnd_state, writebuf, pgsize); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { pr_err("error: write failed at %#llx\n", @@ -565,7 +548,7 @@ static int __init mtd_pagetest_init(void) pr_info("erased %u eraseblocks\n", i); /* Write all eraseblocks */ - simple_srand(1); + prandom_seed_state(&rnd_state, 1); pr_info("writing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -580,7 +563,7 @@ static int __init mtd_pagetest_init(void) pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ - simple_srand(1); + prandom_seed_state(&rnd_state, 1); pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 596cbea8df4c..a6ce9c1fa6c5 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -49,13 +49,6 @@ static int pgcnt; static int goodebcnt; static struct timeval start, finish; -static void set_random_data(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) - buf[i] = random32(); -} static int erase_eraseblock(int ebnum) { @@ -396,7 +389,7 @@ static int __init mtd_speedtest_init(void) goto out; } - set_random_data(iobuf, mtd->erasesize); + prandom_bytes(iobuf, mtd->erasesize); err = scan_for_bad_eraseblocks(); if (err) diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 1d29739db916..787f539d16ca 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -282,8 +282,7 @@ static int __init mtd_stresstest_init(void) } for (i = 0; i < ebcnt; i++) offsets[i] = mtd->erasesize; - for (i = 0; i < bufsize; i++) - writebuf[i] = random32(); + prandom_bytes(writebuf, bufsize); err = scan_for_bad_eraseblocks(); if (err) diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index c880c2229c59..aade56f27945 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -28,6 +28,7 @@ #include <linux/mtd/mtd.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/random.h> static int dev = -EINVAL; module_param(dev, int, S_IRUGO); @@ -43,26 +44,7 @@ static int bufsize; static int ebcnt; static int pgcnt; static int errcnt; -static unsigned long next = 1; - -static inline unsigned int simple_rand(void) -{ - next = next * 1103515245 + 12345; - return (unsigned int)((next / 65536) % 32768); -} - -static inline void simple_srand(unsigned long seed) -{ - next = seed; -} - -static void set_random_data(unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) - buf[i] = simple_rand(); -} +static struct rnd_state rnd_state; static inline void clear_data(unsigned char *buf, size_t len) { @@ -119,7 +101,7 @@ static int write_eraseblock(int ebnum) int err = 0; loff_t addr = ebnum * mtd->erasesize; - set_random_data(writebuf, subpgsize); + prandom_bytes_state(&rnd_state, writebuf, subpgsize); err = mtd_write(mtd, addr, subpgsize, &written, writebuf); if (unlikely(err || written != subpgsize)) { pr_err("error: write failed at %#llx\n", @@ -133,7 +115,7 @@ static int write_eraseblock(int ebnum) addr += subpgsize; - set_random_data(writebuf, subpgsize); + prandom_bytes_state(&rnd_state, writebuf, subpgsize); err = mtd_write(mtd, addr, subpgsize, &written, writebuf); if (unlikely(err || written != subpgsize)) { pr_err("error: write failed at %#llx\n", @@ -157,7 +139,7 @@ static int write_eraseblock2(int ebnum) for (k = 1; k < 33; ++k) { if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) break; - set_random_data(writebuf, subpgsize * k); + prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf); if (unlikely(err || written != subpgsize * k)) { pr_err("error: write failed at %#llx\n", @@ -193,7 +175,7 @@ static int verify_eraseblock(int ebnum) int err = 0; loff_t addr = ebnum * mtd->erasesize; - set_random_data(writebuf, subpgsize); + prandom_bytes_state(&rnd_state, writebuf, subpgsize); clear_data(readbuf, subpgsize); err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { @@ -220,7 +202,7 @@ static int verify_eraseblock(int ebnum) addr += subpgsize; - set_random_data(writebuf, subpgsize); + prandom_bytes_state(&rnd_state, writebuf, subpgsize); clear_data(readbuf, subpgsize); err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { @@ -257,7 +239,7 @@ static int verify_eraseblock2(int ebnum) for (k = 1; k < 33; ++k) { if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) break; - set_random_data(writebuf, subpgsize * k); + prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); clear_data(readbuf, subpgsize * k); err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); if (unlikely(err || read != subpgsize * k)) { @@ -430,7 +412,7 @@ static int __init mtd_subpagetest_init(void) goto out; pr_info("writing whole device\n"); - simple_srand(1); + prandom_seed_state(&rnd_state, 1); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -443,7 +425,7 @@ static int __init mtd_subpagetest_init(void) } pr_info("written %u eraseblocks\n", i); - simple_srand(1); + prandom_seed_state(&rnd_state, 1); pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -466,7 +448,7 @@ static int __init mtd_subpagetest_init(void) goto out; /* Write all eraseblocks */ - simple_srand(3); + prandom_seed_state(&rnd_state, 3); pr_info("writing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -481,7 +463,7 @@ static int __init mtd_subpagetest_init(void) pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ - simple_srand(3); + prandom_seed_state(&rnd_state, 3); pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index db6735931d66..9b90d20ae56b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -2175,13 +2175,13 @@ static int ixgbe_get_ethtool_fdir_entry(struct ixgbe_adapter *adapter, union ixgbe_atr_input *mask = &adapter->fdir_mask; struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; - struct hlist_node *node, *node2; + struct hlist_node *node2; struct ixgbe_fdir_filter *rule = NULL; /* report total rule count */ cmd->data = (1024 << adapter->fdir_pballoc) - 2; - hlist_for_each_entry_safe(rule, node, node2, + hlist_for_each_entry_safe(rule, node2, &adapter->fdir_filter_list, fdir_node) { if (fsp->location <= rule->sw_idx) break; @@ -2242,14 +2242,14 @@ static int ixgbe_get_ethtool_fdir_all(struct ixgbe_adapter *adapter, struct ethtool_rxnfc *cmd, u32 *rule_locs) { - struct hlist_node *node, *node2; + struct hlist_node *node2; struct ixgbe_fdir_filter *rule; int cnt = 0; /* report total rule count */ cmd->data = (1024 << adapter->fdir_pballoc) - 2; - hlist_for_each_entry_safe(rule, node, node2, + hlist_for_each_entry_safe(rule, node2, &adapter->fdir_filter_list, fdir_node) { if (cnt == cmd->rule_cnt) return -EMSGSIZE; @@ -2336,19 +2336,19 @@ static int ixgbe_update_ethtool_fdir_entry(struct ixgbe_adapter *adapter, u16 sw_idx) { struct ixgbe_hw *hw = &adapter->hw; - struct hlist_node *node, *node2, *parent; - struct ixgbe_fdir_filter *rule; + struct hlist_node *node2; + struct ixgbe_fdir_filter *rule, *parent; int err = -EINVAL; parent = NULL; rule = NULL; - hlist_for_each_entry_safe(rule, node, node2, + hlist_for_each_entry_safe(rule, node2, &adapter->fdir_filter_list, fdir_node) { /* hash found, or no matching entry */ if (rule->sw_idx >= sw_idx) break; - parent = node; + parent = rule; } /* if there is an old rule occupying our place remove it */ @@ -2377,7 +2377,7 @@ static int ixgbe_update_ethtool_fdir_entry(struct ixgbe_adapter *adapter, /* add filter to the list */ if (parent) - hlist_add_after(parent, &input->fdir_node); + hlist_add_after(&parent->fdir_node, &input->fdir_node); else hlist_add_head(&input->fdir_node, &adapter->fdir_filter_list); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 396e280c4373..be828a02aee5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3874,7 +3874,7 @@ static void ixgbe_configure_pb(struct ixgbe_adapter *adapter) static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - struct hlist_node *node, *node2; + struct hlist_node *node2; struct ixgbe_fdir_filter *filter; spin_lock(&adapter->fdir_perfect_lock); @@ -3882,7 +3882,7 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter) if (!hlist_empty(&adapter->fdir_filter_list)) ixgbe_fdir_set_input_mask_82599(hw, &adapter->fdir_mask); - hlist_for_each_entry_safe(filter, node, node2, + hlist_for_each_entry_safe(filter, node2, &adapter->fdir_filter_list, fdir_node) { ixgbe_fdir_write_perfect_filter_82599(hw, &filter->filter, @@ -4339,12 +4339,12 @@ static void ixgbe_clean_all_tx_rings(struct ixgbe_adapter *adapter) static void ixgbe_fdir_filter_exit(struct ixgbe_adapter *adapter) { - struct hlist_node *node, *node2; + struct hlist_node *node2; struct ixgbe_fdir_filter *filter; spin_lock(&adapter->fdir_perfect_lock); - hlist_for_each_entry_safe(filter, node, node2, + hlist_for_each_entry_safe(filter, node2, &adapter->fdir_filter_list, fdir_node) { hlist_del(&filter->fdir_node); kfree(filter); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index ac1c14f7424a..656e1ddf1015 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -228,11 +228,10 @@ static inline struct mlx4_en_filter * mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, __be16 src_port, __be16 dst_port) { - struct hlist_node *elem; struct mlx4_en_filter *filter; struct mlx4_en_filter *ret = NULL; - hlist_for_each_entry(filter, elem, + hlist_for_each_entry(filter, filter_hash_bucket(priv, src_ip, dst_ip, src_port, dst_port), filter_chain) { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 51716ab8739a..eec7c101a097 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -576,14 +576,14 @@ void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter) { struct qlcnic_filter *tmp_fil; - struct hlist_node *tmp_hnode, *n; + struct hlist_node *n; struct hlist_head *head; int i, time; u8 cmd; for (i = 0; i < adapter->fhash.fbucket_size; i++) { head = &(adapter->fhash.fhead[i]); - hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { cmd = tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; time = tmp_fil->ftime; @@ -605,14 +605,14 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter) void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter) { struct qlcnic_filter *tmp_fil; - struct hlist_node *tmp_hnode, *n; + struct hlist_node *n; struct hlist_head *head; int i; u8 cmd; for (i = 0; i < adapter->fhash.fbucket_size; i++) { head = &(adapter->fhash.fhead[i]); - hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { cmd = tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; qlcnic_sre_macaddr_change(adapter, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index bb4311e9aea9..afd31d3b5958 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -187,7 +187,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb) { struct qlcnic_filter *fil, *tmp_fil; - struct hlist_node *tmp_hnode, *n; + struct hlist_node *n; struct hlist_head *head; struct net_device *netdev = adapter->netdev; struct ethhdr *phdr = (struct ethhdr *)(skb->data); @@ -212,7 +212,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter, hindex = qlcnic_mac_hash(src_addr) & (adapter->fhash.fbucket_size - 1); head = &(adapter->fhash.fhead[hindex]); - hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) && tmp_fil->vlan_id == vlan_id) { if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 289b4eefb42f..1df0ff3839e8 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -614,10 +614,9 @@ struct vnet_port *__tx_port_find(struct vnet *vp, struct sk_buff *skb) { unsigned int hash = vnet_hashfn(skb->data); struct hlist_head *hp = &vp->port_hash[hash]; - struct hlist_node *n; struct vnet_port *port; - hlist_for_each_entry(port, n, hp, hash) { + hlist_for_each_entry(port, hp, hash) { if (ether_addr_equal(port->raddr, skb->data)) return port; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index f494da82c33f..bbe533f31ee4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -55,9 +55,8 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, const unsigned char *addr) { struct macvlan_dev *vlan; - struct hlist_node *n; - hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[addr[5]], hlist) { + hlist_for_each_entry_rcu(vlan, &port->vlan_hash[addr[5]], hlist) { if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr)) return vlan; } @@ -141,7 +140,6 @@ static void macvlan_broadcast(struct sk_buff *skb, { const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; - struct hlist_node *n; struct sk_buff *nskb; unsigned int i; int err; @@ -151,7 +149,7 @@ static void macvlan_broadcast(struct sk_buff *skb, return; for (i = 0; i < MACVLAN_HASH_SIZE; i++) { - hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { + hlist_for_each_entry_rcu(vlan, &port->vlan_hash[i], hlist) { if (vlan->dev == src || !(vlan->mode & mode)) continue; diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index b181dfb3d6d6..1814cfe86e8e 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -279,28 +279,17 @@ static int macvtap_receive(struct sk_buff *skb) static int macvtap_get_minor(struct macvlan_dev *vlan) { int retval = -ENOMEM; - int id; mutex_lock(&minor_lock); - if (idr_pre_get(&minor_idr, GFP_KERNEL) == 0) - goto exit; - - retval = idr_get_new_above(&minor_idr, vlan, 1, &id); - if (retval < 0) { - if (retval == -EAGAIN) - retval = -ENOMEM; - goto exit; - } - if (id < MACVTAP_NUM_DEVS) { - vlan->minor = id; - } else { + retval = idr_alloc(&minor_idr, vlan, 1, MACVTAP_NUM_DEVS, GFP_KERNEL); + if (retval >= 0) { + vlan->minor = retval; + } else if (retval == -ENOSPC) { printk(KERN_ERR "too many macvtap devices\n"); retval = -EINVAL; - idr_remove(&minor_idr, id); } -exit: mutex_unlock(&minor_lock); - return retval; + return retval < 0 ? retval : 0; } static void macvtap_free_minor(struct macvlan_dev *vlan) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 0b2706abe3e3..7efdb9149e68 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2946,46 +2946,21 @@ static void __exit ppp_cleanup(void) * by holding all_ppp_mutex */ -static int __unit_alloc(struct idr *p, void *ptr, int n) -{ - int unit, err; - -again: - if (!idr_pre_get(p, GFP_KERNEL)) { - pr_err("PPP: No free memory for idr\n"); - return -ENOMEM; - } - - err = idr_get_new_above(p, ptr, n, &unit); - if (err < 0) { - if (err == -EAGAIN) - goto again; - return err; - } - - return unit; -} - /* associate pointer with specified number */ static int unit_set(struct idr *p, void *ptr, int n) { int unit; - unit = __unit_alloc(p, ptr, n); - if (unit < 0) - return unit; - else if (unit != n) { - idr_remove(p, unit); - return -EINVAL; - } - + unit = idr_alloc(p, ptr, n, n + 1, GFP_KERNEL); + if (unit == -ENOSPC) + unit = -EINVAL; return unit; } /* get new free unit number and associate pointer with it */ static int unit_get(struct idr *p, void *ptr) { - return __unit_alloc(p, ptr, 0); + return idr_alloc(p, ptr, 0, 0, GFP_KERNEL); } /* put unit number back to a pool */ diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b1038c0e2240..d52d1b148ce7 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -197,9 +197,8 @@ static inline u32 tun_hashfn(u32 rxhash) static struct tun_flow_entry *tun_flow_find(struct hlist_head *head, u32 rxhash) { struct tun_flow_entry *e; - struct hlist_node *n; - hlist_for_each_entry_rcu(e, n, head, hash_link) { + hlist_for_each_entry_rcu(e, head, hash_link) { if (e->rxhash == rxhash) return e; } @@ -241,9 +240,9 @@ static void tun_flow_flush(struct tun_struct *tun) spin_lock_bh(&tun->lock); for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) { struct tun_flow_entry *e; - struct hlist_node *h, *n; + struct hlist_node *n; - hlist_for_each_entry_safe(e, h, n, &tun->flows[i], hash_link) + hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) tun_flow_delete(tun, e); } spin_unlock_bh(&tun->lock); @@ -256,9 +255,9 @@ static void tun_flow_delete_by_queue(struct tun_struct *tun, u16 queue_index) spin_lock_bh(&tun->lock); for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) { struct tun_flow_entry *e; - struct hlist_node *h, *n; + struct hlist_node *n; - hlist_for_each_entry_safe(e, h, n, &tun->flows[i], hash_link) { + hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) { if (e->queue_index == queue_index) tun_flow_delete(tun, e); } @@ -279,9 +278,9 @@ static void tun_flow_cleanup(unsigned long data) spin_lock_bh(&tun->lock); for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) { struct tun_flow_entry *e; - struct hlist_node *h, *n; + struct hlist_node *n; - hlist_for_each_entry_safe(e, h, n, &tun->flows[i], hash_link) { + hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) { unsigned long this_timer; count++; this_timer = e->updated + delay; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 72485b9b9005..6b573406a6f9 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -144,9 +144,8 @@ static inline struct hlist_head *vni_head(struct net *net, u32 id) static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id) { struct vxlan_dev *vxlan; - struct hlist_node *node; - hlist_for_each_entry_rcu(vxlan, node, vni_head(net, id), hlist) { + hlist_for_each_entry_rcu(vxlan, vni_head(net, id), hlist) { if (vxlan->vni == id) return vxlan; } @@ -291,9 +290,8 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, { struct hlist_head *head = vxlan_fdb_head(vxlan, mac); struct vxlan_fdb *f; - struct hlist_node *node; - hlist_for_each_entry_rcu(f, node, head, hlist) { + hlist_for_each_entry_rcu(f, head, hlist) { if (compare_ether_addr(mac, f->eth_addr) == 0) return f; } @@ -420,10 +418,9 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, for (h = 0; h < FDB_HASH_SIZE; ++h) { struct vxlan_fdb *f; - struct hlist_node *n; int err; - hlist_for_each_entry_rcu(f, n, &vxlan->fdb_head[h], hlist) { + hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) { if (idx < cb->args[0]) goto skip; @@ -481,11 +478,10 @@ static bool vxlan_group_used(struct vxlan_net *vn, const struct vxlan_dev *this) { const struct vxlan_dev *vxlan; - struct hlist_node *node; unsigned h; for (h = 0; h < VNI_HASH_SIZE; ++h) - hlist_for_each_entry(vxlan, node, &vn->vni_list[h], hlist) { + hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) { if (vxlan == this) continue; diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 48273dd05b63..4941f201d6c8 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -309,7 +309,6 @@ static void zd1201_usbrx(struct urb *urb) if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) { int datalen = urb->actual_length-1; unsigned short len, fc, seq; - struct hlist_node *node; len = ntohs(*(__be16 *)&data[datalen-2]); if (len>datalen) @@ -362,7 +361,7 @@ static void zd1201_usbrx(struct urb *urb) hlist_add_head(&frag->fnode, &zd->fraglist); goto resubmit; } - hlist_for_each_entry(frag, node, &zd->fraglist, fnode) + hlist_for_each_entry(frag, &zd->fraglist, fnode) if (frag->seq == (seq&IEEE80211_SCTL_SEQ)) break; if (!frag) @@ -1831,14 +1830,14 @@ err_zd: static void zd1201_disconnect(struct usb_interface *interface) { struct zd1201 *zd = usb_get_intfdata(interface); - struct hlist_node *node, *node2; + struct hlist_node *node2; struct zd1201_frag *frag; if (!zd) return; usb_set_intfdata(interface, NULL); - hlist_for_each_entry_safe(frag, node, node2, &zd->fraglist, fnode) { + hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) { hlist_del_init(&frag->fnode); kfree_skb(frag->skb); kfree(frag); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 393cf095a0c4..816039fdd305 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -842,9 +842,8 @@ static struct pci_cap_saved_state *pci_find_saved_cap( struct pci_dev *pci_dev, char cap) { struct pci_cap_saved_state *tmp; - struct hlist_node *pos; - hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { + hlist_for_each_entry(tmp, &pci_dev->saved_cap_space, next) { if (tmp->cap.cap_nr == cap) return tmp; } @@ -1041,7 +1040,6 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) struct pci_saved_state *state; struct pci_cap_saved_state *tmp; struct pci_cap_saved_data *cap; - struct hlist_node *pos; size_t size; if (!dev->state_saved) @@ -1049,7 +1047,7 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) size = sizeof(*state) + sizeof(struct pci_cap_saved_data); - hlist_for_each_entry(tmp, pos, &dev->saved_cap_space, next) + hlist_for_each_entry(tmp, &dev->saved_cap_space, next) size += sizeof(struct pci_cap_saved_data) + tmp->cap.size; state = kzalloc(size, GFP_KERNEL); @@ -1060,7 +1058,7 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) sizeof(state->config_space)); cap = state->cap; - hlist_for_each_entry(tmp, pos, &dev->saved_cap_space, next) { + hlist_for_each_entry(tmp, &dev->saved_cap_space, next) { size_t len = sizeof(struct pci_cap_saved_data) + tmp->cap.size; memcpy(cap, &tmp->cap, len); cap = (struct pci_cap_saved_data *)((u8 *)cap + len); @@ -2035,9 +2033,9 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) void pci_free_cap_save_buffers(struct pci_dev *dev) { struct pci_cap_saved_state *tmp; - struct hlist_node *pos, *n; + struct hlist_node *n; - hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next) + hlist_for_each_entry_safe(tmp, n, &dev->saved_cap_space, next) kfree(tmp); } diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 673c14ea11e3..5292db69c426 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -484,7 +484,7 @@ static int socket_early_resume(struct pcmcia_socket *skt) static int socket_late_resume(struct pcmcia_socket *skt) { - int ret; + int ret = 0; mutex_lock(&skt->ops_mutex); skt->state &= ~SOCKET_SUSPEND; @@ -511,19 +511,31 @@ static int socket_late_resume(struct pcmcia_socket *skt) return socket_insert(skt); } + if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) + ret = skt->callback->early_resume(skt); + return ret; +} + +/* + * Finalize the resume. In case of a cardbus socket, we have + * to rebind the devices as we can't be certain that it has been + * replaced, or not. + */ +static int socket_complete_resume(struct pcmcia_socket *skt) +{ + int ret = 0; #ifdef CONFIG_CARDBUS if (skt->state & SOCKET_CARDBUS) { /* We can't be sure the CardBus card is the same * as the one previously inserted. Therefore, remove * and re-add... */ cb_free(skt); - cb_alloc(skt); - return 0; + ret = cb_alloc(skt); + if (ret) + cb_free(skt); } #endif - if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) - skt->callback->early_resume(skt); - return 0; + return ret; } /* @@ -533,11 +545,15 @@ static int socket_late_resume(struct pcmcia_socket *skt) */ static int socket_resume(struct pcmcia_socket *skt) { + int err; if (!(skt->state & SOCKET_SUSPEND)) return -EBUSY; socket_early_resume(skt); - return socket_late_resume(skt); + err = socket_late_resume(skt); + if (!err) + err = socket_complete_resume(skt); + return err; } static void socket_remove(struct pcmcia_socket *skt) @@ -848,6 +864,12 @@ static int __used pcmcia_socket_dev_resume(struct device *dev) return __pcmcia_pm_op(dev, socket_late_resume); } +static void __used pcmcia_socket_dev_complete(struct device *dev) +{ + WARN(__pcmcia_pm_op(dev, socket_complete_resume), + "failed to complete resume"); +} + static const struct dev_pm_ops pcmcia_socket_pm_ops = { /* dev_resume may be called with IRQs enabled */ SET_SYSTEM_SLEEP_PM_OPS(NULL, @@ -862,6 +884,7 @@ static const struct dev_pm_ops pcmcia_socket_pm_ops = { .resume_noirq = pcmcia_socket_dev_resume_noirq, .thaw_noirq = pcmcia_socket_dev_resume_noirq, .restore_noirq = pcmcia_socket_dev_resume_noirq, + .complete = pcmcia_socket_dev_complete, }; #define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index ca70365e9410..3f00568635e8 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1504,16 +1504,11 @@ static int bq2415x_probe(struct i2c_client *client, } /* Get new ID for the new device */ - ret = idr_pre_get(&bq2415x_id, GFP_KERNEL); - if (ret == 0) - return -ENOMEM; - mutex_lock(&bq2415x_id_mutex); - ret = idr_get_new(&bq2415x_id, client, &num); + num = idr_alloc(&bq2415x_id, client, 0, 0, GFP_KERNEL); mutex_unlock(&bq2415x_id_mutex); - - if (ret < 0) - return ret; + if (num < 0) + return num; name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); if (!name) { diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 8ccf5d7d0add..26037ca7efb4 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -791,14 +791,11 @@ static int bq27x00_battery_probe(struct i2c_client *client, int retval = 0; /* Get new ID for the new battery device */ - retval = idr_pre_get(&battery_id, GFP_KERNEL); - if (retval == 0) - return -ENOMEM; mutex_lock(&battery_mutex); - retval = idr_get_new(&battery_id, client, &num); + num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); mutex_unlock(&battery_mutex); - if (retval < 0) - return retval; + if (num < 0) + return num; name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); if (!name) { diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index e7301b3ed623..c09e7726c96c 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -395,17 +395,12 @@ static int ds278x_battery_probe(struct i2c_client *client, } /* Get an ID for this battery */ - ret = idr_pre_get(&battery_id, GFP_KERNEL); - if (ret == 0) { - ret = -ENOMEM; - goto fail_id; - } - mutex_lock(&battery_lock); - ret = idr_get_new(&battery_id, client, &num); + ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); mutex_unlock(&battery_lock); if (ret < 0) goto fail_id; + num = ret; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index f197e8ea185c..cdad4d95b20e 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -102,7 +102,7 @@ struct pps_device *pps_register_source(struct pps_source_info *info, goto pps_register_source_exit; } - /* These initializations must be done before calling idr_get_new() + /* These initializations must be done before calling idr_alloc() * in order to avoid reces into pps_event(). */ pps->params.api_version = PPS_API_VERS; diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 2420d5af0583..de8e663ee137 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -290,29 +290,21 @@ int pps_register_cdev(struct pps_device *pps) dev_t devt; mutex_lock(&pps_idr_lock); - /* Get new ID for the new PPS source */ - if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { - mutex_unlock(&pps_idr_lock); - return -ENOMEM; - } - - /* Now really allocate the PPS source. - * After idr_get_new() calling the new source will be freely available - * into the kernel. + /* + * Get new ID for the new PPS source. After idr_alloc() calling + * the new source will be freely available into the kernel. */ - err = idr_get_new(&pps_idr, pps, &pps->id); - mutex_unlock(&pps_idr_lock); - - if (err < 0) - return err; - - pps->id &= MAX_IDR_MASK; - if (pps->id >= PPS_MAX_SOURCES) { - pr_err("%s: too many PPS sources in the system\n", - pps->info.name); - err = -EBUSY; - goto free_idr; + err = idr_alloc(&pps_idr, pps, 0, PPS_MAX_SOURCES, GFP_KERNEL); + if (err < 0) { + if (err == -ENOSPC) { + pr_err("%s: too many PPS sources in the system\n", + pps->info.name); + err = -EBUSY; + } + goto out_unlock; } + pps->id = err; + mutex_unlock(&pps_idr_lock); devt = MKDEV(MAJOR(pps_devt), pps->id); @@ -345,8 +337,8 @@ del_cdev: free_idr: mutex_lock(&pps_idr_lock); idr_remove(&pps_idr, pps->id); +out_unlock: mutex_unlock(&pps_idr_lock); - return err; } diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index dd3bfaf1ad40..29387df4bfc9 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -199,11 +199,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) /* actual size of vring (in bytes) */ size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); - if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { - dev_err(dev, "idr_pre_get failed\n"); - return -ENOMEM; - } - /* * Allocate non-cacheable memory for the vring. In the future * this call will also configure the IOMMU for us @@ -221,12 +216,13 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) * TODO: let the rproc know the notifyid of this vring * TODO: support predefined notifyids (via resource table) */ - ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid); + ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); if (ret) { - dev_err(dev, "idr_get_new failed: %d\n", ret); + dev_err(dev, "idr_alloc failed: %d\n", ret); dma_free_coherent(dev->parent, size, va, dma); return ret; } + notifyid = ret; /* Store largest notifyid */ rproc->max_notifyid = max(rproc->max_notifyid, notifyid); @@ -1180,7 +1176,6 @@ static void rproc_type_release(struct device *dev) rproc_delete_debug_dir(rproc); - idr_remove_all(&rproc->notifyids); idr_destroy(&rproc->notifyids); if (rproc->index >= 0) diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index d85446021ddb..a59684b5fc68 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -213,13 +213,10 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, void *priv, u32 addr) { - int err, tmpaddr, request; + int id_min, id_max, id; struct rpmsg_endpoint *ept; struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; - if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL)) - return NULL; - ept = kzalloc(sizeof(*ept), GFP_KERNEL); if (!ept) { dev_err(dev, "failed to kzalloc a new ept\n"); @@ -234,31 +231,28 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, ept->priv = priv; /* do we need to allocate a local address ? */ - request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr; + if (addr == RPMSG_ADDR_ANY) { + id_min = RPMSG_RESERVED_ADDRESSES; + id_max = 0; + } else { + id_min = addr; + id_max = addr + 1; + } mutex_lock(&vrp->endpoints_lock); /* bind the endpoint to an rpmsg address (and allocate one if needed) */ - err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr); - if (err) { - dev_err(dev, "idr_get_new_above failed: %d\n", err); + id = idr_alloc(&vrp->endpoints, ept, id_min, id_max, GFP_KERNEL); + if (id < 0) { + dev_err(dev, "idr_alloc failed: %d\n", id); goto free_ept; } - - /* make sure the user's address request is fulfilled, if relevant */ - if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) { - dev_err(dev, "address 0x%x already in use\n", addr); - goto rem_idr; - } - - ept->addr = tmpaddr; + ept->addr = id; mutex_unlock(&vrp->endpoints_lock); return ept; -rem_idr: - idr_remove(&vrp->endpoints, request); free_ept: mutex_unlock(&vrp->endpoints_lock); kref_put(&ept->refcount, __ept_release); @@ -1036,7 +1030,6 @@ static void rpmsg_remove(struct virtio_device *vdev) if (vrp->ns_ept) __rpmsg_destroy_ept(vrp, vrp->ns_ept); - idr_remove_all(&vrp->endpoints); idr_destroy(&vrp->endpoints); vdev->config->del_vqs(vrp->vdev); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3847515500b1..8d1f108b1975 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -204,6 +204,12 @@ config RTC_DRV_DS3232 This driver can also be built as a module. If so, the module will be called rtc-ds3232. +config RTC_DRV_LP8788 + tristate "TI LP8788 RTC driver" + depends on MFD_LP8788 + help + Say Y to enable support for the LP8788 RTC/ALARM driver. + config RTC_DRV_MAX6900 tristate "Maxim MAX6900" help @@ -243,6 +249,16 @@ config RTC_DRV_MAX8998 This driver can also be built as a module. If so, the module will be called rtc-max8998. +config RTC_DRV_MAX77686 + tristate "Maxim MAX77686" + depends on MFD_MAX77686 + help + If you say yes here you will get support for the + RTC of Maxim MAX77686 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max77686. + config RTC_DRV_RS5C372 tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" help @@ -390,6 +406,14 @@ config RTC_DRV_TPS65910 This driver can also be built as a module. If so, the module will be called rtc-tps65910. +config RTC_DRV_TPS80031 + tristate "TI TPS80031/TPS80032 RTC driver" + depends on MFD_TPS80031 + help + TI Power Managment IC TPS80031 supports RTC functionality + along with alarm. This driver supports the RTC driver for + the TPS80031 RTC module. + config RTC_DRV_RC5T583 tristate "RICOH 5T583 RTC driver" depends on MFD_RC5T583 @@ -547,6 +571,14 @@ config RTC_DRV_PCF2123 This driver can also be built as a module. If so, the module will be called rtc-pcf2123. +config RTC_DRV_RX4581 + tristate "Epson RX-4581" + help + If you say yes here you will get support for the Epson RX-4581. + + This driver can also be built as a module. If so the module + will be called rtc-rx4581. + endif # SPI_MASTER comment "Platform RTC drivers" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 805603897ee5..e864b1eb6533 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o +obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o @@ -72,6 +73,7 @@ obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o @@ -98,6 +100,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o +obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o @@ -116,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o +obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 16630aa87f45..9deb9e47a67c 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1098,7 +1098,6 @@ static __init void cmos_of_init(struct platform_device *pdev) } #else static inline void cmos_of_init(struct platform_device *pdev) {} -#define of_cmos_match NULL #endif /*----------------------------------------------------------------*/ @@ -1140,7 +1139,7 @@ static struct platform_driver cmos_platform_driver = { #ifdef CONFIG_PM .pm = &cmos_pm_ops, #endif - .of_match_table = of_cmos_match, + .of_match_table = of_match_ptr(of_cmos_match), } }; diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index d578773f5ce2..b05a6dc96405 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -635,9 +635,7 @@ static int ds1305_probe(struct spi_device *spi) goto fail0; } - dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", - "read", ds1305->ctrl[0], - ds1305->ctrl[1], ds1305->ctrl[2]); + dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "read", ds1305->ctrl); /* Sanity check register values ... partially compensating for the * fact that SPI has no device handshake. A pullup on MISO would @@ -723,9 +721,7 @@ static int ds1305_probe(struct spi_device *spi) goto fail0; } - dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", - "write", ds1305->ctrl[0], - ds1305->ctrl[1], ds1305->ctrl[2]); + dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "write", ds1305->ctrl); } /* see if non-Linux software set up AM/PM mode */ diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index e0d0ba4de03f..a65621c42170 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -4,6 +4,7 @@ * Copyright (C) 2005 James Chapman (ds1337 core) * Copyright (C) 2006 David Brownell * Copyright (C) 2009 Matthias Fuchs (rx8025 support) + * Copyright (C) 2012 Bertrand Achard (nvram access fixes) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -196,7 +197,7 @@ static s32 ds1307_read_block_data_once(const struct i2c_client *client, static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values) { - u8 oldvalues[I2C_SMBUS_BLOCK_MAX]; + u8 oldvalues[255]; s32 ret; int tries = 0; @@ -222,7 +223,7 @@ static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command, static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values) { - u8 currvalues[I2C_SMBUS_BLOCK_MAX]; + u8 currvalues[255]; int tries = 0; dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length); @@ -250,6 +251,57 @@ static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command, /*----------------------------------------------------------------------*/ +/* These RTC devices are not designed to be connected to a SMbus adapter. + SMbus limits block operations length to 32 bytes, whereas it's not + limited on I2C buses. As a result, accesses may exceed 32 bytes; + in that case, split them into smaller blocks */ + +static s32 ds1307_native_smbus_write_block_data(const struct i2c_client *client, + u8 command, u8 length, const u8 *values) +{ + u8 suboffset = 0; + + if (length <= I2C_SMBUS_BLOCK_MAX) + return i2c_smbus_write_i2c_block_data(client, + command, length, values); + + while (suboffset < length) { + s32 retval = i2c_smbus_write_i2c_block_data(client, + command + suboffset, + min(I2C_SMBUS_BLOCK_MAX, length - suboffset), + values + suboffset); + if (retval < 0) + return retval; + + suboffset += I2C_SMBUS_BLOCK_MAX; + } + return length; +} + +static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + u8 suboffset = 0; + + if (length <= I2C_SMBUS_BLOCK_MAX) + return i2c_smbus_read_i2c_block_data(client, + command, length, values); + + while (suboffset < length) { + s32 retval = i2c_smbus_read_i2c_block_data(client, + command + suboffset, + min(I2C_SMBUS_BLOCK_MAX, length - suboffset), + values + suboffset); + if (retval < 0) + return retval; + + suboffset += I2C_SMBUS_BLOCK_MAX; + } + return length; +} + +/*----------------------------------------------------------------------*/ + /* * The IRQ logic includes a "real" handler running in IRQ context just * long enough to schedule this workqueue entry. We need a task context @@ -322,12 +374,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) return -EIO; } - dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", - "read", - ds1307->regs[0], ds1307->regs[1], - ds1307->regs[2], ds1307->regs[3], - ds1307->regs[4], ds1307->regs[5], - ds1307->regs[6]); + dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs); t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f); t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); @@ -398,9 +445,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) break; } - dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", - "write", buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6]); + dev_dbg(dev, "%s: %7ph\n", "write", buf); result = ds1307->write_block_data(ds1307->client, ds1307->offset, 7, buf); @@ -653,8 +698,8 @@ static int ds1307_probe(struct i2c_client *client, buf = ds1307->regs; if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { - ds1307->read_block_data = i2c_smbus_read_i2c_block_data; - ds1307->write_block_data = i2c_smbus_write_i2c_block_data; + ds1307->read_block_data = ds1307_native_smbus_read_block_data; + ds1307->write_block_data = ds1307_native_smbus_write_block_data; } else { ds1307->read_block_data = ds1307_read_block_data; ds1307->write_block_data = ds1307_write_block_data; diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c index 5ea9df7c8c31..4539e37c2238 100644 --- a/drivers/rtc/rtc-ds2404.c +++ b/drivers/rtc/rtc-ds2404.c @@ -283,19 +283,7 @@ static struct platform_driver rtc_device_driver = { .owner = THIS_MODULE, }, }; - -static __init int ds2404_init(void) -{ - return platform_driver_register(&rtc_device_driver); -} - -static __exit void ds2404_exit(void) -{ - platform_driver_unregister(&rtc_device_driver); -} - -module_init(ds2404_init); -module_exit(ds2404_exit); +module_platform_driver(rtc_device_driver); MODULE_DESCRIPTION("DS2404 RTC"); MODULE_AUTHOR("Sven Schnelle"); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 04e93c6597f8..bff3cdc5140e 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -116,17 +116,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t) fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); - dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x" - "%02x %02x %02x %02x %02x %02x %02x\n", - "read", - fm3130->regs[0], fm3130->regs[1], - fm3130->regs[2], fm3130->regs[3], - fm3130->regs[4], fm3130->regs[5], - fm3130->regs[6], fm3130->regs[7], - fm3130->regs[8], fm3130->regs[9], - fm3130->regs[0xa], fm3130->regs[0xb], - fm3130->regs[0xc], fm3130->regs[0xd], - fm3130->regs[0xe]); + dev_dbg(dev, "%s: %15ph\n", "read", fm3130->regs); t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); @@ -175,12 +165,7 @@ static int fm3130_set_time(struct device *dev, struct rtc_time *t) tmp = t->tm_year - 100; buf[FM3130_RTC_YEARS] = bin2bcd(tmp); - dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x" - "%02x %02x %02x %02x %02x %02x %02x %02x\n", - "write", buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[0xa], buf[0xb], - buf[0xc], buf[0xd], buf[0xe]); + dev_dbg(dev, "%s: %15ph\n", "write", buf); fm3130_rtc_mode(dev, FM3130_MODE_WRITE); @@ -517,18 +502,8 @@ bad_alarm: bad_clock: if (!fm3130->data_valid || !fm3130->alarm_valid) - dev_dbg(&client->dev, - "%s: %02x %02x %02x %02x %02x %02x %02x %02x" - "%02x %02x %02x %02x %02x %02x %02x\n", - "bogus registers", - fm3130->regs[0], fm3130->regs[1], - fm3130->regs[2], fm3130->regs[3], - fm3130->regs[4], fm3130->regs[5], - fm3130->regs[6], fm3130->regs[7], - fm3130->regs[8], fm3130->regs[9], - fm3130->regs[0xa], fm3130->regs[0xb], - fm3130->regs[0xc], fm3130->regs[0xd], - fm3130->regs[0xe]); + dev_dbg(&client->dev, "%s: %15ph\n", "bogus registers", + fm3130->regs); /* We won't bail out here because we just got invalid data. Time setting from u-boot doesn't work anyway */ diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 1850104705c0..6b4298ea683d 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -227,7 +227,7 @@ static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm) buf[ISL12022_REG_SC + i]); if (ret) return -EIO; - }; + } return 0; } diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c new file mode 100644 index 000000000000..4a4e78e2231c --- /dev/null +++ b/drivers/rtc/rtc-lp8788.c @@ -0,0 +1,344 @@ +/* + * TI LP8788 MFD - rtc driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/err.h> +#include <linux/irqdomain.h> +#include <linux/mfd/lp8788.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +/* register address */ +#define LP8788_INTEN_3 0x05 +#define LP8788_RTC_UNLOCK 0x64 +#define LP8788_RTC_SEC 0x70 +#define LP8788_ALM1_SEC 0x77 +#define LP8788_ALM1_EN 0x7D +#define LP8788_ALM2_SEC 0x7E +#define LP8788_ALM2_EN 0x84 + +/* mask/shift bits */ +#define LP8788_INT_RTC_ALM1_M BIT(1) /* Addr 05h */ +#define LP8788_INT_RTC_ALM1_S 1 +#define LP8788_INT_RTC_ALM2_M BIT(2) /* Addr 05h */ +#define LP8788_INT_RTC_ALM2_S 2 +#define LP8788_ALM_EN_M BIT(7) /* Addr 7Dh or 84h */ +#define LP8788_ALM_EN_S 7 + +#define DEFAULT_ALARM_SEL LP8788_ALARM_1 +#define LP8788_MONTH_OFFSET 1 +#define LP8788_BASE_YEAR 2000 +#define MAX_WDAY_BITS 7 +#define LP8788_WDAY_SET 1 +#define RTC_UNLOCK 0x1 +#define RTC_LATCH 0x2 +#define ALARM_IRQ_FLAG (RTC_IRQF | RTC_AF) + +enum lp8788_time { + LPTIME_SEC, + LPTIME_MIN, + LPTIME_HOUR, + LPTIME_MDAY, + LPTIME_MON, + LPTIME_YEAR, + LPTIME_WDAY, + LPTIME_MAX, +}; + +struct lp8788_rtc { + struct lp8788 *lp; + struct rtc_device *rdev; + enum lp8788_alarm_sel alarm; + int irq; +}; + +static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = { + LP8788_ALM1_SEC, + LP8788_ALM2_SEC, +}; + +static const u8 addr_alarm_en[LP8788_ALARM_MAX] = { + LP8788_ALM1_EN, + LP8788_ALM2_EN, +}; + +static const u8 mask_alarm_en[LP8788_ALARM_MAX] = { + LP8788_INT_RTC_ALM1_M, + LP8788_INT_RTC_ALM2_M, +}; + +static const u8 shift_alarm_en[LP8788_ALARM_MAX] = { + LP8788_INT_RTC_ALM1_S, + LP8788_INT_RTC_ALM2_S, +}; + +static int _to_tm_wday(u8 lp8788_wday) +{ + int i; + + if (lp8788_wday == 0) + return 0; + + /* lookup defined weekday from read register value */ + for (i = 0; i < MAX_WDAY_BITS; i++) { + if ((lp8788_wday >> i) == LP8788_WDAY_SET) + break; + } + + return i + 1; +} + +static inline int _to_lp8788_wday(int tm_wday) +{ + return LP8788_WDAY_SET << (tm_wday - 1); +} + +static void lp8788_rtc_unlock(struct lp8788 *lp) +{ + lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK); + lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH); +} + +static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 data[LPTIME_MAX]; + int ret; + + lp8788_rtc_unlock(lp); + + ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX); + if (ret) + return ret; + + tm->tm_sec = data[LPTIME_SEC]; + tm->tm_min = data[LPTIME_MIN]; + tm->tm_hour = data[LPTIME_HOUR]; + tm->tm_mday = data[LPTIME_MDAY]; + tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; + tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; + tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); + + return 0; +} + +static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 data[LPTIME_MAX - 1]; + int ret, i, year; + + year = tm->tm_year + 1900 - LP8788_BASE_YEAR; + if (year < 0) { + dev_err(lp->dev, "invalid year: %d\n", year); + return -EINVAL; + } + + /* because rtc weekday is a readonly register, do not update */ + data[LPTIME_SEC] = tm->tm_sec; + data[LPTIME_MIN] = tm->tm_min; + data[LPTIME_HOUR] = tm->tm_hour; + data[LPTIME_MDAY] = tm->tm_mday; + data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; + data[LPTIME_YEAR] = year; + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]); + if (ret) + return ret; + } + + return 0; +} + +static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + struct rtc_time *tm = &alarm->time; + u8 addr, data[LPTIME_MAX]; + int ret; + + addr = addr_alarm_sec[rtc->alarm]; + ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX); + if (ret) + return ret; + + tm->tm_sec = data[LPTIME_SEC]; + tm->tm_min = data[LPTIME_MIN]; + tm->tm_hour = data[LPTIME_HOUR]; + tm->tm_mday = data[LPTIME_MDAY]; + tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; + tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; + tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); + alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M; + + return 0; +} + +static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + struct rtc_time *tm = &alarm->time; + u8 addr, data[LPTIME_MAX]; + int ret, i, year; + + year = tm->tm_year + 1900 - LP8788_BASE_YEAR; + if (year < 0) { + dev_err(lp->dev, "invalid year: %d\n", year); + return -EINVAL; + } + + data[LPTIME_SEC] = tm->tm_sec; + data[LPTIME_MIN] = tm->tm_min; + data[LPTIME_HOUR] = tm->tm_hour; + data[LPTIME_MDAY] = tm->tm_mday; + data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; + data[LPTIME_YEAR] = year; + data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday); + + for (i = 0; i < ARRAY_SIZE(data); i++) { + addr = addr_alarm_sec[rtc->alarm] + i; + ret = lp8788_write_byte(lp, addr, data[i]); + if (ret) + return ret; + } + + alarm->enabled = 1; + addr = addr_alarm_en[rtc->alarm]; + + return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M, + alarm->enabled << LP8788_ALM_EN_S); +} + +static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 mask, shift; + + if (!rtc->irq) + return -EIO; + + mask = mask_alarm_en[rtc->alarm]; + shift = shift_alarm_en[rtc->alarm]; + + return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift); +} + +static const struct rtc_class_ops lp8788_rtc_ops = { + .read_time = lp8788_rtc_read_time, + .set_time = lp8788_rtc_set_time, + .read_alarm = lp8788_read_alarm, + .set_alarm = lp8788_set_alarm, + .alarm_irq_enable = lp8788_alarm_irq_enable, +}; + +static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr) +{ + struct lp8788_rtc *rtc = ptr; + + rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG); + return IRQ_HANDLED; +} + +static int lp8788_alarm_irq_register(struct platform_device *pdev, + struct lp8788_rtc *rtc) +{ + struct resource *r; + struct lp8788 *lp = rtc->lp; + struct irq_domain *irqdm = lp->irqdm; + int irq; + + rtc->irq = 0; + + /* even the alarm IRQ number is not specified, rtc time should work */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ); + if (!r) + return 0; + + if (rtc->alarm == LP8788_ALARM_1) + irq = r->start; + else + irq = r->end; + + rtc->irq = irq_create_mapping(irqdm, irq); + + return request_threaded_irq(rtc->irq, NULL, lp8788_alarm_irq_handler, + 0, LP8788_ALM_IRQ, rtc); +} + +static void lp8788_alarm_irq_unregister(struct lp8788_rtc *rtc) +{ + if (rtc->irq) + free_irq(rtc->irq, rtc); +} + +static int lp8788_rtc_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct lp8788_rtc *rtc; + struct device *dev = &pdev->dev; + + rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->lp = lp; + rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL; + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(dev, 1); + + rtc->rdev = rtc_device_register("lp8788_rtc", dev, + &lp8788_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rdev)) { + dev_err(dev, "can not register rtc device\n"); + return PTR_ERR(rtc->rdev); + } + + if (lp8788_alarm_irq_register(pdev, rtc)) + dev_warn(lp->dev, "no rtc irq handler\n"); + + return 0; +} + +static int lp8788_rtc_remove(struct platform_device *pdev) +{ + struct lp8788_rtc *rtc = platform_get_drvdata(pdev); + + lp8788_alarm_irq_unregister(rtc); + rtc_device_unregister(rtc->rdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver lp8788_rtc_driver = { + .probe = lp8788_rtc_probe, + .remove = lp8788_rtc_remove, + .driver = { + .name = LP8788_DEV_RTC, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(lp8788_rtc_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-rtc"); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c new file mode 100644 index 000000000000..2da0855b742f --- /dev/null +++ b/drivers/rtc/rtc-max77686.c @@ -0,0 +1,645 @@ +/* + * RTC driver for Maxim MAX77686 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * + * based on rtc-max8997.c + * + * 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. + * + */ + +#include <linux/slab.h> +#include <linux/rtc.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/max77686-private.h> +#include <linux/irqdomain.h> +#include <linux/regmap.h> + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK (1 << BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK (1 << MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) +#define RTC_RBUDR_SHIFT 4 +#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT) +/* WTSR and SMPL Register */ +#define WTSRT_SHIFT 0 +#define SMPLT_SHIFT 2 +#define WTSR_EN_SHIFT 6 +#define SMPL_EN_SHIFT 7 +#define WTSRT_MASK (3 << WTSRT_SHIFT) +#define SMPLT_MASK (3 << SMPLT_SHIFT) +#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT) +#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT) +/* RTC Hour register */ +#define HOUR_PM_SHIFT 6 +#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) + +#define MAX77686_RTC_UPDATE_DELAY 16 +#undef MAX77686_RTC_WTSR_SMPL + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_DATE, + RTC_NR_TIME +}; + +struct max77686_rtc_info { + struct device *dev; + struct max77686_dev *max77686; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + struct mutex lock; + + struct regmap *regmap; + + int virq; + int rtc_24hr_mode; +}; + +enum MAX77686_RTC_OP { + MAX77686_RTC_WRITE, + MAX77686_RTC_READ, +}; + +static inline int max77686_rtc_calculate_wday(u8 shifted) +{ + int counter = -1; + while (shifted) { + shifted >>= 1; + counter++; + } + return counter; +} + +static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + int rtc_24hr_mode) +{ + tm->tm_sec = data[RTC_SEC] & 0x7f; + tm->tm_min = data[RTC_MIN] & 0x7f; + if (rtc_24hr_mode) + tm->tm_hour = data[RTC_HOUR] & 0x1f; + else { + tm->tm_hour = data[RTC_HOUR] & 0x0f; + if (data[RTC_HOUR] & HOUR_PM_MASK) + tm->tm_hour += 12; + } + + tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f); + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100; + tm->tm_yday = 0; + tm->tm_isdst = 0; +} + +static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + data[RTC_HOUR] = tm->tm_hour; + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ; + + if (tm->tm_year < 100) { + pr_warn("%s: MAX77686 RTC cannot handle the year %d." + "Assume it's 2000.\n", __func__, 1900 + tm->tm_year); + return -EINVAL; + } + return 0; +} + +static int max77686_rtc_update(struct max77686_rtc_info *info, + enum MAX77686_RTC_OP op) +{ + int ret; + unsigned int data; + + switch (op) { + case MAX77686_RTC_WRITE: + data = 1 << RTC_UDR_SHIFT; + break; + case MAX77686_RTC_READ: + data = 1 << RTC_RBUDR_SHIFT; + break; + } + + ret = regmap_update_bits(info->max77686->rtc_regmap, + MAX77686_RTC_UPDATE0, data, data); + if (ret < 0) + dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n", + __func__, ret, data); + else { + /* Minimum 16ms delay required before RTC update. */ + msleep(MAX77686_RTC_UPDATE_DELAY); + } + + return ret; +} + +static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77686->rtc_regmap, + MAX77686_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, ret); + goto out; + } + + max77686_rtc_data_to_tm(data, tm, info->rtc_24hr_mode); + + ret = rtc_valid_tm(tm); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77686_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = regmap_bulk_write(info->max77686->rtc_regmap, + MAX77686_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__, + ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + unsigned int val; + int i, ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); + + alrm->enabled = 0; + for (i = 0; i < RTC_NR_TIME; i++) { + if (data[i] & ALARM_ENABLE_MASK) { + alrm->enabled = 1; + break; + } + } + + alrm->pending = 0; + ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS1, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + if (val & (1 << 4)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return 0; +} + +static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret, i; + struct rtc_time tm; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode); + + for (i = 0; i < RTC_NR_TIME; i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = regmap_bulk_write(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); +out: + return ret; +} + +static int max77686_rtc_start_alarm(struct max77686_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret; + struct rtc_time tm; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode); + + data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; + if (data[RTC_MONTH] & 0xf) + data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_YEAR] & 0x7f) + data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_DATE] & 0x1f) + data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); + + ret = regmap_bulk_write(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); +out: + return ret; +} + +static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77686_rtc_tm_to_data(&alrm->time, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_stop_alarm(info); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(info->max77686->rtc_regmap, + MAX77686_ALARM1_SEC, data, RTC_NR_TIME); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = max77686_rtc_start_alarm(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + int ret; + + mutex_lock(&info->lock); + if (enabled) + ret = max77686_rtc_start_alarm(info); + else + ret = max77686_rtc_stop_alarm(info); + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data) +{ + struct max77686_rtc_info *info = data; + + dev_info(info->dev, "%s:irq(%d)\n", __func__, irq); + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max77686_rtc_ops = { + .read_time = max77686_rtc_read_time, + .set_time = max77686_rtc_set_time, + .read_alarm = max77686_rtc_read_alarm, + .set_alarm = max77686_rtc_set_alarm, + .alarm_irq_enable = max77686_rtc_alarm_irq_enable, +}; + +#ifdef MAX77686_RTC_WTSR_SMPL +static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable) +{ + int ret; + unsigned int val, mask; + + if (enable) + val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT); + else + val = 0; + + mask = WTSR_EN_MASK | WTSRT_MASK; + + dev_info(info->dev, "%s: %s WTSR\n", __func__, + enable ? "enable" : "disable"); + + ret = regmap_update_bits(info->max77686->rtc_regmap, + MAX77686_WTSR_SMPL_CNTL, mask, val); + if (ret < 0) { + dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", + __func__, ret); + return; + } + + max77686_rtc_update(info, MAX77686_RTC_WRITE); +} + +static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable) +{ + int ret; + unsigned int val, mask; + + if (enable) + val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT); + else + val = 0; + + mask = SMPL_EN_MASK | SMPLT_MASK; + + dev_info(info->dev, "%s: %s SMPL\n", __func__, + enable ? "enable" : "disable"); + + ret = regmap_update_bits(info->max77686->rtc_regmap, + MAX77686_WTSR_SMPL_CNTL, mask, val); + if (ret < 0) { + dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", + __func__, ret); + return; + } + + max77686_rtc_update(info, MAX77686_RTC_WRITE); + + val = 0; + regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); + pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val); +} +#endif /* MAX77686_RTC_WTSR_SMPL */ + +static int max77686_rtc_init_reg(struct max77686_rtc_info *info) +{ + u8 data[2]; + int ret; + + /* Set RTC control register : Binary mode, 24hour mdoe */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + info->rtc_24hr_mode = 1; + + ret = regmap_bulk_write(info->max77686->rtc_regmap, MAX77686_RTC_CONTROLM, data, 2); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", + __func__, ret); + return ret; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + return ret; +} + +static struct regmap_config max77686_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max77686_rtc_probe(struct platform_device *pdev) +{ + struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent); + struct max77686_rtc_info *info; + int ret, virq; + + printk(KERN_INFO "%s\n", __func__); + + info = kzalloc(sizeof(struct max77686_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + info->max77686 = max77686; + info->rtc = max77686->rtc; + info->max77686->rtc_regmap = regmap_init_i2c(info->max77686->rtc, + &max77686_rtc_regmap_config); + if (IS_ERR(info->max77686->rtc_regmap)) { + ret = PTR_ERR(info->max77686->rtc_regmap); + dev_err(info->max77686->dev, "Failed to allocate register map: %d\n", + ret); + kfree(info); + return ret; + } + platform_set_drvdata(pdev, info); + + ret = max77686_rtc_init_reg(info); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); + goto err_rtc; + } + +#ifdef MAX77686_RTC_WTSR_SMPL + max77686_rtc_enable_wtsr(info, true); + max77686_rtc_enable_smpl(info, true); +#endif + + device_init_wakeup(&pdev->dev, 1); + + info->rtc_dev = rtc_device_register("max77686-rtc", &pdev->dev, + &max77686_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + printk(KERN_INFO "%s: fail\n", __func__); + + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + if (ret == 0) + ret = -EINVAL; + goto err_rtc; + } + virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1); + if (!virq) + goto err_rtc; + info->virq = virq; + + ret = request_threaded_irq(virq, NULL, max77686_rtc_alarm_irq, 0, + "rtc-alarm0", info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->virq, ret); + goto err_rtc; + } + + goto out; +err_rtc: + kfree(info); + return ret; +out: + return ret; +} + +static int max77686_rtc_remove(struct platform_device *pdev) +{ + struct max77686_rtc_info *info = platform_get_drvdata(pdev); + + if (info) { + free_irq(info->virq, info); + rtc_device_unregister(info->rtc_dev); + kfree(info); + } + + return 0; +} + +static void max77686_rtc_shutdown(struct platform_device *pdev) +{ +#ifdef MAX77686_RTC_WTSR_SMPL + struct max77686_rtc_info *info = platform_get_drvdata(pdev); + int i; + u8 val = 0; + + for (i = 0; i < 3; i++) { + max77686_rtc_enable_wtsr(info, false); + regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); + pr_info("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val); + if (val & WTSR_EN_MASK) + pr_emerg("%s: fail to disable WTSR\n", __func__); + else { + pr_info("%s: success to disable WTSR\n", __func__); + break; + } + } + + /* Disable SMPL when power off */ + max77686_rtc_enable_smpl(info, false); +#endif /* MAX77686_RTC_WTSR_SMPL */ +} + +static const struct platform_device_id rtc_id[] = { + { "max77686-rtc", 0 }, + {}, +}; + +static struct platform_driver max77686_rtc_driver = { + .driver = { + .name = "max77686-rtc", + .owner = THIS_MODULE, + }, + .probe = max77686_rtc_probe, + .remove = max77686_rtc_remove, + .shutdown = max77686_rtc_shutdown, + .id_table = rtc_id, +}; + +static int __init max77686_rtc_init(void) +{ + return platform_driver_register(&max77686_rtc_driver); +} +module_init(max77686_rtc_init); + +static void __exit max77686_rtc_exit(void) +{ + platform_driver_unregister(&max77686_rtc_driver); +} +module_exit(max77686_rtc_exit); + +MODULE_DESCRIPTION("Maxim MAX77686 RTC driver"); +MODULE_AUTHOR("<woong.byun@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index bec10be96f84..bdcc60830aec 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/rtc.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/of_platform.h> #include <linux/io.h> @@ -403,17 +404,19 @@ static int mpc5121_rtc_remove(struct platform_device *op) return 0; } +#ifdef CONFIG_OF static struct of_device_id mpc5121_rtc_match[] = { { .compatible = "fsl,mpc5121-rtc", }, { .compatible = "fsl,mpc5200-rtc", }, {}, }; +#endif static struct platform_driver mpc5121_rtc_driver = { .driver = { .name = "mpc5121-rtc", .owner = THIS_MODULE, - .of_match_table = mpc5121_rtc_match, + .of_match_table = of_match_ptr(mpc5121_rtc_match), }, .probe = mpc5121_rtc_probe, .remove = mpc5121_rtc_remove, diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index be05a645f99e..889e3160e701 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -23,6 +23,7 @@ #define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */ #define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */ #define REG_CONTROL3_PM_MASK 0xe0 +#define REG_CONTROL3_BLF (1 << 2) /* battery low bit, read-only */ #define REG_SECONDS 0x03 #define REG_SECONDS_OS (1 << 7) @@ -250,9 +251,39 @@ static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm) return pcf8523_start_rtc(client); } +#ifdef CONFIG_RTC_INTF_DEV +static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 value; + int ret = 0, err; + + switch (cmd) { + case RTC_VL_READ: + err = pcf8523_read(client, REG_CONTROL3, &value); + if (err < 0) + return err; + + if (value & REG_CONTROL3_BLF) + ret = 1; + + if (copy_to_user((void __user *)arg, &ret, sizeof(int))) + return -EFAULT; + + return 0; + default: + return -ENOIOCTLCMD; + } +} +#else +#define pcf8523_rtc_ioctl NULL +#endif + static const struct rtc_class_ops pcf8523_rtc_ops = { .read_time = pcf8523_rtc_read_time, .set_time = pcf8523_rtc_set_time, + .ioctl = pcf8523_rtc_ioctl, }; static int pcf8523_probe(struct i2c_client *client, diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 7098ee89bd29..f7daf18a112e 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -181,7 +181,7 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) __func__, err, data[0], data[1]); return -EIO; } - }; + } return 0; } diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 81c5077feff3..8900ea784817 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -384,6 +384,8 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_irq; } + device_init_wakeup(&adev->dev, 1); + return 0; out_no_irq: diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index f771b2ee4b18..b7e462978609 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -19,6 +19,7 @@ * */ +#include <linux/delay.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/module.h> @@ -62,6 +63,10 @@ #define RYxR_MONTH_S 5 #define RYxR_MONTH_MASK (0xf << RYxR_MONTH_S) #define RYxR_DAY_MASK 0x1f +#define RDxR_WOM_S 20 +#define RDxR_WOM_MASK (0x7 << RDxR_WOM_S) +#define RDxR_DOW_S 17 +#define RDxR_DOW_MASK (0x7 << RDxR_DOW_S) #define RDxR_HOUR_S 12 #define RDxR_HOUR_MASK (0x1f << RDxR_HOUR_S) #define RDxR_MIN_S 6 @@ -76,21 +81,29 @@ #define RYAR1 0x1c #define RTCPICR 0x34 #define PIAR 0x38 +#define PSBR_RTC 0x00 #define rtc_readl(pxa_rtc, reg) \ __raw_readl((pxa_rtc)->base + (reg)) #define rtc_writel(pxa_rtc, reg, value) \ __raw_writel((value), (pxa_rtc)->base + (reg)) +#define rtc_readl_psbr(pxa_rtc, reg) \ + __raw_readl((pxa_rtc)->base_psbr + (reg)) +#define rtc_writel_psbr(pxa_rtc, reg, value) \ + __raw_writel((value), (pxa_rtc)->base_psbr + (reg)) struct pxa_rtc { struct resource *ress; + struct resource *ress_psbr; void __iomem *base; + void __iomem *base_psbr; int irq_1Hz; int irq_Alrm; struct rtc_device *rtc; spinlock_t lock; /* Protects this structure */ }; +static struct pxa_rtc *rtc_info; static u32 ryxr_calc(struct rtc_time *tm) { return ((tm->tm_year + 1900) << RYxR_YEAR_S) @@ -100,7 +113,10 @@ static u32 ryxr_calc(struct rtc_time *tm) static u32 rdxr_calc(struct rtc_time *tm) { - return (tm->tm_hour << RDxR_HOUR_S) | (tm->tm_min << RDxR_MIN_S) + return ((((tm->tm_mday + 6) / 7) << RDxR_WOM_S) & RDxR_WOM_MASK) + | (((tm->tm_wday + 1) << RDxR_DOW_S) & RDxR_DOW_MASK) + | (tm->tm_hour << RDxR_HOUR_S) + | (tm->tm_min << RDxR_MIN_S) | tm->tm_sec; } @@ -109,6 +125,7 @@ static void tm_calc(u32 rycr, u32 rdcr, struct rtc_time *tm) tm->tm_year = ((rycr & RYxR_YEAR_MASK) >> RYxR_YEAR_S) - 1900; tm->tm_mon = (((rycr & RYxR_MONTH_MASK) >> RYxR_MONTH_S)) - 1; tm->tm_mday = (rycr & RYxR_DAY_MASK); + tm->tm_wday = ((rdcr & RDxR_DOW_MASK) >> RDxR_DOW_S) - 1; tm->tm_hour = (rdcr & RDxR_HOUR_MASK) >> RDxR_HOUR_S; tm->tm_min = (rdcr & RDxR_MIN_MASK) >> RDxR_MIN_S; tm->tm_sec = rdcr & RDxR_SEC_MASK; @@ -166,7 +183,6 @@ static irqreturn_t pxa_rtc_irq(int irq, void *dev_id) /* enable back rtc interrupts */ rtc_writel(pxa_rtc, RTSR, rtsr & ~RTSR_TRIG_MASK); - spin_unlock(&pxa_rtc->lock); return IRQ_HANDLED; } @@ -241,12 +257,45 @@ static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm) static int pxa_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - + /* sequence to wirte pxa rtc register RCNR RDCR RYCR is + *1. set PSBR[RWE] bit, take 2x32-khz to complete + *2. write to RTC register,take 2x32-khz to complete + *3. clear PSBR[RWE] bit,take 2x32-khz to complete + */ + if ((tm->tm_year < 70) || (tm->tm_year > 138)) + return -EINVAL; + rtc_writel_psbr(rtc_info, PSBR_RTC, 0x01); + udelay(100); rtc_writel(pxa_rtc, RYCR, ryxr_calc(tm)); rtc_writel(pxa_rtc, RDCR, rdxr_calc(tm)); + udelay(100); + rtc_writel_psbr(rtc_info, PSBR_RTC, 0x00); + udelay(100); + pxa_rtc_read_time(dev, tm); + dev_info(dev, "tm.year = %d, tm.month = %d, tm.day = %d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday); + return 0; +} +int pxa_rtc_sync_time(unsigned int ticks) +{ + /* sequence to wirte pxa rtc register RCNR RDCR RYCR is + *1. set PSBR[RWE] bit, take 2x32-khz to complete + *2. write to RTC register,take 2x32-khz to complete + *3. clear PSBR[RWE] bit,take 2x32-khz to complete + */ + struct rtc_time tm; + rtc_time_to_tm(ticks, &tm); + rtc_writel_psbr(rtc_info, PSBR_RTC, 0x01); + udelay(100); + rtc_writel(rtc_info, RYCR, ryxr_calc(&tm)); + rtc_writel(rtc_info, RDCR, rdxr_calc(&tm)); + udelay(100); + rtc_writel_psbr(rtc_info, PSBR_RTC, 0x00); + udelay(100); return 0; } +EXPORT_SYMBOL(pxa_rtc_sync_time); static int pxa_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { @@ -300,8 +349,6 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq) } static const struct rtc_class_ops pxa_rtc_ops = { - .open = pxa_rtc_open, - .release = pxa_rtc_release, .read_time = pxa_rtc_read_time, .set_time = pxa_rtc_set_time, .read_alarm = pxa_rtc_read_alarm, @@ -320,7 +367,7 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) pxa_rtc = kzalloc(sizeof(struct pxa_rtc), GFP_KERNEL); if (!pxa_rtc) return -ENOMEM; - + rtc_info = pxa_rtc; spin_lock_init(&pxa_rtc->lock); platform_set_drvdata(pdev, pxa_rtc); @@ -330,6 +377,11 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) dev_err(dev, "No I/O memory resource defined\n"); goto err_ress; } + pxa_rtc->ress_psbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!pxa_rtc->ress_psbr) { + dev_err(dev, "No I/O memory resource defined\n"); + goto err_ress; + } pxa_rtc->irq_1Hz = platform_get_irq(pdev, 0); if (pxa_rtc->irq_1Hz < 0) { @@ -341,7 +393,6 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) dev_err(dev, "No alarm IRQ resource defined\n"); goto err_ress; } - ret = -ENOMEM; pxa_rtc->base = ioremap(pxa_rtc->ress->start, resource_size(pxa_rtc->ress)); @@ -350,6 +401,12 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) goto err_map; } + pxa_rtc->base_psbr = ioremap(pxa_rtc->ress_psbr->start, + resource_size(pxa_rtc->ress_psbr)); + if (!pxa_rtc->base_psbr) { + dev_err(&pdev->dev, "Unable to map pxa RTC PSBR I/O memory\n"); + goto err_map; + } /* * If the clock divider is uninitialized then reset it to the * default value to get the 1Hz clock. @@ -372,7 +429,7 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) } device_init_wakeup(dev, 1); - + pxa_rtc_open(dev); return 0; err_rtc_reg: @@ -387,6 +444,9 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev) { struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + pxa_rtc_release(dev); + rtc_device_unregister(pxa_rtc->rtc); spin_lock_irq(&pxa_rtc->lock); @@ -444,10 +504,7 @@ static struct platform_driver pxa_rtc_driver = { static int __init pxa_rtc_init(void) { - if (cpu_is_pxa27x() || cpu_is_pxa3xx()) - return platform_driver_probe(&pxa_rtc_driver, pxa_rtc_probe); - - return -ENODEV; + return platform_driver_probe(&pxa_rtc_driver, pxa_rtc_probe); } static void __exit pxa_rtc_exit(void) diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c new file mode 100644 index 000000000000..599ec73ec886 --- /dev/null +++ b/drivers/rtc/rtc-rx4581.c @@ -0,0 +1,314 @@ +/* drivers/rtc/rtc-rx4581.c + * + * written by Torben Hohn <torbenh@linutronix.de> + * + * Based on: + * drivers/rtc/rtc-max6902.c + * + * Copyright (C) 2006 8D Technologies inc. + * Copyright (C) 2004 Compulab Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Driver for MAX6902 spi RTC + * + * and based on: + * drivers/rtc/rtc-rx8581.c + * + * An I2C driver for the Epson RX8581 RTC + * + * Author: Martyn Welch <martyn.welch@ge.com> + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC) + * Copyright 2005-06 Tower Technologies + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +#define RX4581_REG_SC 0x00 /* Second in BCD */ +#define RX4581_REG_MN 0x01 /* Minute in BCD */ +#define RX4581_REG_HR 0x02 /* Hour in BCD */ +#define RX4581_REG_DW 0x03 /* Day of Week */ +#define RX4581_REG_DM 0x04 /* Day of Month in BCD */ +#define RX4581_REG_MO 0x05 /* Month in BCD */ +#define RX4581_REG_YR 0x06 /* Year in BCD */ +#define RX4581_REG_RAM 0x07 /* RAM */ +#define RX4581_REG_AMN 0x08 /* Alarm Min in BCD*/ +#define RX4581_REG_AHR 0x09 /* Alarm Hour in BCD */ +#define RX4581_REG_ADM 0x0A +#define RX4581_REG_ADW 0x0A +#define RX4581_REG_TMR0 0x0B +#define RX4581_REG_TMR1 0x0C +#define RX4581_REG_EXT 0x0D /* Extension Register */ +#define RX4581_REG_FLAG 0x0E /* Flag Register */ +#define RX4581_REG_CTRL 0x0F /* Control Register */ + + +/* Flag Register bit definitions */ +#define RX4581_FLAG_UF 0x20 /* Update */ +#define RX4581_FLAG_TF 0x10 /* Timer */ +#define RX4581_FLAG_AF 0x08 /* Alarm */ +#define RX4581_FLAG_VLF 0x02 /* Voltage Low */ + +/* Control Register bit definitions */ +#define RX4581_CTRL_UIE 0x20 /* Update Interrupt Enable */ +#define RX4581_CTRL_TIE 0x10 /* Timer Interrupt Enable */ +#define RX4581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */ +#define RX4581_CTRL_STOP 0x02 /* STOP bit */ +#define RX4581_CTRL_RESET 0x01 /* RESET bit */ + +static int rx4581_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* high nibble must be '0' to write */ + buf[0] = address & 0x0f; + buf[1] = data; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int rx4581_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + + /* Set MSB to indicate read */ + *data = address | 0x80; + + return spi_write_then_read(spi, data, 1, data, 1); +} + +/* + * In the routines that deal directly with the rx8581 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int rx4581_get_datetime(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char date[7]; + unsigned char data; + int err; + + /* First we ensure that the "update flag" is not set, we read the + * time and date then re-read the "update flag". If the update flag + * has been set, we know that the time has changed during the read so + * we repeat the whole process again. + */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read device flags\n"); + return -EIO; + } + + do { + /* If update flag set, clear it */ + if (data & RX4581_FLAG_UF) { + err = rx4581_set_reg(dev, + RX4581_REG_FLAG, (data & ~RX4581_FLAG_UF)); + if (err != 0) { + dev_err(dev, "Unable to write device " + "flags\n"); + return -EIO; + } + } + + /* Now read time and date */ + date[0] = 0x80; + err = spi_write_then_read(spi, date, 1, date, 7); + if (err < 0) { + dev_err(dev, "Unable to read date\n"); + return -EIO; + } + + /* Check flag register */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read device flags\n"); + return -EIO; + } + } while (data & RX4581_FLAG_UF); + + if (data & RX4581_FLAG_VLF) + dev_info(dev, + "low voltage detected, date/time is not reliable.\n"); + + dev_dbg(dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "wday=%02x, mday=%02x, mon=%02x, year=%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + tm->tm_sec = bcd2bin(date[RX4581_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(date[RX4581_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(date[RX4581_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_wday = ilog2(date[RX4581_REG_DW] & 0x7F); + tm->tm_mday = bcd2bin(date[RX4581_REG_DM] & 0x3F); + tm->tm_mon = bcd2bin(date[RX4581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(date[RX4581_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + err = rtc_valid_tm(tm); + if (err < 0) + dev_err(dev, "retrieved date/time is not valid.\n"); + + return err; +} + +static int rx4581_set_datetime(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + int err; + unsigned char buf[8], data; + + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[0] = 0x00; + /* hours, minutes and seconds */ + buf[RX4581_REG_SC+1] = bin2bcd(tm->tm_sec); + buf[RX4581_REG_MN+1] = bin2bcd(tm->tm_min); + buf[RX4581_REG_HR+1] = bin2bcd(tm->tm_hour); + + buf[RX4581_REG_DM+1] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[RX4581_REG_MO+1] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[RX4581_REG_YR+1] = bin2bcd(tm->tm_year % 100); + buf[RX4581_REG_DW+1] = (0x1 << tm->tm_wday); + + /* Stop the clock */ + err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data); + if (err != 0) { + dev_err(dev, "Unable to read control register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_CTRL, + (data | RX4581_CTRL_STOP)); + if (err != 0) { + dev_err(dev, "Unable to write control register\n"); + return -EIO; + } + + /* write register's data */ + err = spi_write_then_read(spi, buf, 8, NULL, 0); + if (err != 0) { + dev_err(dev, "Unable to write to date registers\n"); + return -EIO; + } + + /* get VLF and clear it */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read flag register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_FLAG, + (data & ~(RX4581_FLAG_VLF))); + if (err != 0) { + dev_err(dev, "Unable to write flag register\n"); + return -EIO; + } + + /* Restart the clock */ + err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data); + if (err != 0) { + dev_err(dev, "Unable to read control register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_CTRL, + (data & ~(RX4581_CTRL_STOP))); + if (err != 0) { + dev_err(dev, "Unable to write control register\n"); + return -EIO; + } + + return 0; +} + +static const struct rtc_class_ops rx4581_rtc_ops = { + .read_time = rx4581_get_datetime, + .set_time = rx4581_set_datetime, +}; + +static int rx4581_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + int res; + + res = rx4581_get_reg(&spi->dev, RX4581_REG_SC, &tmp); + if (res != 0) + return res; + + rtc = rtc_device_register("rx4581", + &spi->dev, &rx4581_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + dev_set_drvdata(&spi->dev, rtc); + return 0; +} + +static int rx4581_remove(struct spi_device *spi) +{ + struct rtc_device *rtc = dev_get_drvdata(&spi->dev); + + rtc_device_unregister(rtc); + return 0; +} + +static const struct spi_device_id rx4581_id[] = { + { "rx4581", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rx4581_id); + +static struct spi_driver rx4581_driver = { + .driver = { + .name = "rtc-rx4581", + .owner = THIS_MODULE, + }, + .probe = rx4581_probe, + .remove = rx4581_remove, + .id_table = rx4581_id, +}; + +module_spi_driver(rx4581_driver); + +MODULE_DESCRIPTION("rx4581 spi RTC driver"); +MODULE_AUTHOR("Torben Hohn"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-rx4581"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 0c397ac3b132..fb994e9ddc15 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -115,7 +115,7 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { unsigned int tmp; - pr_debug("%s: aie=%d\n", __func__, enabled); + dev_dbg(dev, "%s: aie=%d\n", __func__, enabled); clk_enable(rtc_clk); tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; @@ -203,7 +203,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_year += 100; - pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", + dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n", 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); @@ -218,7 +218,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) void __iomem *base = s3c_rtc_base; int year = tm->tm_year - 100; - pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", + dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -259,7 +259,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; - pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", + dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", alm_en, 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); @@ -310,7 +310,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned int alrm_en; clk_enable(rtc_clk); - pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", + dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -333,7 +333,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); } - pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); + dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); writeb(alrm_en, base + S3C2410_RTCALM); @@ -459,7 +459,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) int ret; int tmp; - pr_debug("%s: probe=%p\n", __func__, pdev); + dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev); /* find the IRQs */ @@ -475,7 +475,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) return s3c_rtc_alarmno; } - pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", + dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", s3c_rtc_tickno, s3c_rtc_alarmno); /* get the memory region */ @@ -504,7 +504,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_enable(pdev, 1); - pr_debug("s3c2410_rtc: RTCCON=%02x\n", + dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", readw(s3c_rtc_base + S3C2410_RTCCON)); device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 50a5c4adee48..df9180e80e3a 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -349,12 +349,14 @@ static const struct dev_pm_ops sa1100_rtc_pm_ops = { }; #endif +#ifdef CONFIG_OF static struct of_device_id sa1100_rtc_dt_ids[] = { { .compatible = "mrvl,sa1100-rtc", }, { .compatible = "mrvl,mmp-rtc", }, {} }; MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids); +#endif static struct platform_driver sa1100_rtc_driver = { .probe = sa1100_rtc_probe, @@ -364,7 +366,7 @@ static struct platform_driver sa1100_rtc_driver = { #ifdef CONFIG_PM .pm = &sa1100_rtc_pm_ops, #endif - .of_match_table = sa1100_rtc_dt_ids, + .of_match_table = of_match_ptr(sa1100_rtc_dt_ids), }, }; diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index 40662e9dc0ab..f7d90703db5e 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -338,7 +338,7 @@ static struct platform_driver snvs_rtc_driver = { .name = "snvs_rtc", .owner = THIS_MODULE, .pm = &snvs_rtc_pm_ops, - .of_match_table = snvs_dt_ids, + .of_match_table = of_match_ptr(snvs_dt_ids), }, .probe = snvs_rtc_probe, .remove = snvs_rtc_remove, diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index cc9a9b652b13..98f0d3c30738 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -26,6 +26,7 @@ #include <linux/rtc.h> #include <linux/slab.h> #include <linux/of_device.h> +#include <linux/of.h> #include <linux/stmp_device.h> #include <linux/stmp3xxx_rtc_wdt.h> @@ -344,7 +345,7 @@ static struct platform_driver stmp3xxx_rtcdrv = { .driver = { .name = "stmp3xxx-rtc", .owner = THIS_MODULE, - .of_match_table = rtc_dt_ids, + .of_match_table = of_match_ptr(rtc_dt_ids), }, }; diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c index e5fef141a0e2..8bd8115329b5 100644 --- a/drivers/rtc/rtc-tps65910.c +++ b/drivers/rtc/rtc-tps65910.c @@ -22,13 +22,13 @@ #include <linux/rtc.h> #include <linux/bcd.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/interrupt.h> #include <linux/mfd/tps65910.h> struct tps65910_rtc { struct rtc_device *rtc; - /* To store the list of enabled interrupts */ - u32 irqstat; + int irq; }; /* Total number of RTC registers needed to set time*/ @@ -267,13 +267,14 @@ static int tps65910_rtc_probe(struct platform_device *pdev) } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, + tps65910_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME, dev_name(&pdev->dev), &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "IRQ is not free.\n"); return ret; } - device_init_wakeup(&pdev->dev, 1); + tps_rtc->irq = irq; + device_set_wakeup_capable(&pdev->dev, 1); tps_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &tps65910_rtc_ops, THIS_MODULE); @@ -304,49 +305,36 @@ static int tps65910_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP - static int tps65910_rtc_suspend(struct device *dev) { - struct tps65910 *tps = dev_get_drvdata(dev->parent); - u8 alarm = TPS65910_RTC_INTERRUPTS_IT_ALARM; - int ret; - - /* Store current list of enabled interrupts*/ - ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, - &tps->rtc->irqstat); - if (ret < 0) - return ret; + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); - /* Enable RTC ALARM interrupt only */ - return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, alarm); + if (device_may_wakeup(dev)) + enable_irq_wake(tps_rtc->irq); + return 0; } static int tps65910_rtc_resume(struct device *dev) { - struct tps65910 *tps = dev_get_drvdata(dev->parent); + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); - /* Restore list of enabled interrupts before suspend */ - return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, - tps->rtc->irqstat); + if (device_may_wakeup(dev)) + disable_irq_wake(tps_rtc->irq); + return 0; } +#endif static const struct dev_pm_ops tps65910_rtc_pm_ops = { - .suspend = tps65910_rtc_suspend, - .resume = tps65910_rtc_resume, + SET_SYSTEM_SLEEP_PM_OPS(tps65910_rtc_suspend, tps65910_rtc_resume) }; -#define DEV_PM_OPS (&tps65910_rtc_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - static struct platform_driver tps65910_rtc_driver = { .probe = tps65910_rtc_probe, .remove = tps65910_rtc_remove, .driver = { .owner = THIS_MODULE, .name = "tps65910-rtc", - .pm = DEV_PM_OPS, + .pm = &tps65910_rtc_pm_ops, }, }; diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c new file mode 100644 index 000000000000..97406411d58c --- /dev/null +++ b/drivers/rtc/rtc-tps80031.c @@ -0,0 +1,349 @@ +/* + * rtc-tps80031.c -- TI TPS80031/TPS80032 RTC driver + * + * RTC driver for TI TPS80031/TPS80032 Fully Integrated + * Power Management with Power Path and Battery Charger + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/bcd.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/tps80031.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define ENABLE_ALARM_INT 0x08 +#define ALARM_INT_STATUS 0x40 + +/** + * Setting bit to 1 in STOP_RTC will run the RTC and + * setting this bit to 0 will freeze RTC. + */ +#define STOP_RTC 0x1 + +/* Power on reset Values of RTC registers */ +#define TPS80031_RTC_POR_YEAR 0 +#define TPS80031_RTC_POR_MONTH 1 +#define TPS80031_RTC_POR_DAY 1 + +/* Numbers of registers for time and alarms */ +#define TPS80031_RTC_TIME_NUM_REGS 7 +#define TPS80031_RTC_ALARM_NUM_REGS 6 + +/** + * PMU RTC have only 2 nibbles to store year information, so using an + * offset of 100 to set the base year as 2000 for our driver. + */ +#define RTC_YEAR_OFFSET 100 + +struct tps80031_rtc { + struct rtc_device *rtc; + int irq; +}; + +static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[TPS80031_RTC_TIME_NUM_REGS]; + int ret; + + ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_SECONDS_REG, TPS80031_RTC_TIME_NUM_REGS, buff); + if (ret < 0) { + dev_err(dev, "reading RTC_SECONDS_REG failed, err = %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(buff[0]); + tm->tm_min = bcd2bin(buff[1]); + tm->tm_hour = bcd2bin(buff[2]); + tm->tm_mday = bcd2bin(buff[3]); + tm->tm_mon = bcd2bin(buff[4]) - 1; + tm->tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET; + tm->tm_wday = bcd2bin(buff[6]); + return 0; +} + +static int tps80031_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int ret; + + buff[0] = bin2bcd(tm->tm_sec); + buff[1] = bin2bcd(tm->tm_min); + buff[2] = bin2bcd(tm->tm_hour); + buff[3] = bin2bcd(tm->tm_mday); + buff[4] = bin2bcd(tm->tm_mon + 1); + buff[5] = bin2bcd(tm->tm_year % RTC_YEAR_OFFSET); + buff[6] = bin2bcd(tm->tm_wday); + + /* Stop RTC while updating the RTC time registers */ + ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_CTRL_REG, STOP_RTC); + if (ret < 0) { + dev_err(dev->parent, "Stop RTC failed, err = %d\n", ret); + return ret; + } + + ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_SECONDS_REG, + TPS80031_RTC_TIME_NUM_REGS, buff); + if (ret < 0) { + dev_err(dev, "writing RTC_SECONDS_REG failed, err %d\n", ret); + return ret; + } + + ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_CTRL_REG, STOP_RTC); + if (ret < 0) + dev_err(dev->parent, "Start RTC failed, err = %d\n", ret); + return ret; +} + +static int tps80031_rtc_alarm_irq_enable(struct device *dev, + unsigned int enable) +{ + int ret; + + if (enable) + ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT); + else + ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT); + if (ret < 0) { + dev_err(dev, "Update on RTC_INT failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static int tps80031_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[TPS80031_RTC_ALARM_NUM_REGS]; + int ret; + + buff[0] = bin2bcd(alrm->time.tm_sec); + buff[1] = bin2bcd(alrm->time.tm_min); + buff[2] = bin2bcd(alrm->time.tm_hour); + buff[3] = bin2bcd(alrm->time.tm_mday); + buff[4] = bin2bcd(alrm->time.tm_mon + 1); + buff[5] = bin2bcd(alrm->time.tm_year % RTC_YEAR_OFFSET); + ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_ALARM_SECONDS_REG, + TPS80031_RTC_ALARM_NUM_REGS, buff); + if (ret < 0) { + dev_err(dev, "Writing RTC_ALARM failed, err %d\n", ret); + return ret; + } + return tps80031_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static int tps80031_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[6]; + int ret; + + ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_ALARM_SECONDS_REG, + TPS80031_RTC_ALARM_NUM_REGS, buff); + if (ret < 0) { + dev_err(dev->parent, + "reading RTC_ALARM failed, err = %d\n", ret); + return ret; + } + + alrm->time.tm_sec = bcd2bin(buff[0]); + alrm->time.tm_min = bcd2bin(buff[1]); + alrm->time.tm_hour = bcd2bin(buff[2]); + alrm->time.tm_mday = bcd2bin(buff[3]); + alrm->time.tm_mon = bcd2bin(buff[4]) - 1; + alrm->time.tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET; + return 0; +} + +static int clear_alarm_int_status(struct device *dev, struct tps80031_rtc *rtc) +{ + int ret; + u8 buf; + + /** + * As per datasheet, A dummy read of this RTC_STATUS_REG register + * is necessary before each I2C read in order to update the status + * register value. + */ + ret = tps80031_read(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_STATUS_REG, &buf); + if (ret < 0) { + dev_err(dev, "reading RTC_STATUS failed. err = %d\n", ret); + return ret; + } + + /* clear Alarm status bits.*/ + ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_STATUS_REG, ALARM_INT_STATUS); + if (ret < 0) { + dev_err(dev, "clear Alarm INT failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static irqreturn_t tps80031_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + int ret; + + ret = clear_alarm_int_status(dev, rtc); + if (ret < 0) + return ret; + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tps80031_rtc_ops = { + .read_time = tps80031_rtc_read_time, + .set_time = tps80031_rtc_set_time, + .set_alarm = tps80031_rtc_set_alarm, + .read_alarm = tps80031_rtc_read_alarm, + .alarm_irq_enable = tps80031_rtc_alarm_irq_enable, +}; + +static int tps80031_rtc_probe(struct platform_device *pdev) +{ + struct tps80031_rtc *rtc; + struct rtc_time tm; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->irq = platform_get_irq(pdev, 0); + platform_set_drvdata(pdev, rtc); + + /* Start RTC */ + ret = tps80031_set_bits(pdev->dev.parent, TPS80031_SLAVE_ID1, + TPS80031_RTC_CTRL_REG, STOP_RTC); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start RTC. err = %d\n", ret); + return ret; + } + + /* If RTC have POR values, set time 01:01:2000 */ + tps80031_rtc_read_time(&pdev->dev, &tm); + if ((tm.tm_year == RTC_YEAR_OFFSET + TPS80031_RTC_POR_YEAR) && + (tm.tm_mon == (TPS80031_RTC_POR_MONTH - 1)) && + (tm.tm_mday == TPS80031_RTC_POR_DAY)) { + tm.tm_year = 2000; + tm.tm_mday = 1; + tm.tm_mon = 1; + ret = tps80031_rtc_set_time(&pdev->dev, &tm); + if (ret < 0) { + dev_err(&pdev->dev, + "RTC set time failed, err = %d\n", ret); + return ret; + } + } + + /* Clear alarm intretupt status if it is there */ + ret = clear_alarm_int_status(&pdev->dev, rtc); + if (ret < 0) { + dev_err(&pdev->dev, "Clear alarm int failed, err = %d\n", ret); + return ret; + } + + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &tps80031_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + dev_err(&pdev->dev, "RTC registration failed, err %d\n", ret); + return ret; + } + + ret = request_threaded_irq(rtc->irq, NULL, tps80031_rtc_irq, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + dev_name(&pdev->dev), rtc); + if (ret < 0) { + dev_err(&pdev->dev, "request IRQ:%d failed, err = %d\n", + rtc->irq, ret); + rtc_device_unregister(rtc->rtc); + return ret; + } + device_set_wakeup_capable(&pdev->dev, 1); + return 0; +} + +static int tps80031_rtc_remove(struct platform_device *pdev) +{ + struct tps80031_rtc *rtc = platform_get_drvdata(pdev); + + free_irq(rtc->irq, rtc); + rtc_device_unregister(rtc->rtc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tps80031_rtc_suspend(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + return 0; +} + +static int tps80031_rtc_resume(struct device *dev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + return 0; +}; +#endif + +static const struct dev_pm_ops tps80031_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tps80031_rtc_suspend, tps80031_rtc_resume) +}; + +static struct platform_driver tps80031_rtc_driver = { + .driver = { + .name = "tps80031-rtc", + .owner = THIS_MODULE, + .pm = &tps80031_pm_ops, + }, + .probe = tps80031_rtc_probe, + .remove = tps80031_rtc_remove, +}; + +module_platform_driver(tps80031_rtc_driver); + +MODULE_ALIAS("platform:tps80031-rtc"); +MODULE_DESCRIPTION("TI TPS80031/TPS80032 RTC driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index ccd4ad370b32..8bc6c80b184c 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -27,6 +27,7 @@ #include <linux/bcd.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/i2c/twl.h> @@ -588,11 +589,14 @@ static int twl_rtc_resume(struct platform_device *pdev) #define twl_rtc_resume NULL #endif +#ifdef CONFIG_OF static const struct of_device_id twl_rtc_of_match[] = { {.compatible = "ti,twl4030-rtc", }, { }, }; MODULE_DEVICE_TABLE(of, twl_rtc_of_match); +#endif + MODULE_ALIAS("platform:twl_rtc"); static struct platform_driver twl4030rtc_driver = { @@ -604,7 +608,7 @@ static struct platform_driver twl4030rtc_driver = { .driver = { .owner = THIS_MODULE, .name = "twl_rtc", - .of_match_table = twl_rtc_of_match, + .of_match_table = of_match_ptr(twl_rtc_of_match), }, }; diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index 8f92732655c7..5864f987f206 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -523,20 +523,13 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port, int error = 1; mutex_lock(&bfad_mutex); - if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) { + error = idr_alloc(&bfad_im_port_index, im_port, 0, 0, GFP_KERNEL); + if (error < 0) { mutex_unlock(&bfad_mutex); - printk(KERN_WARNING "idr_pre_get failure\n"); + printk(KERN_WARNING "idr_alloc failure\n"); goto out; } - - error = idr_get_new(&bfad_im_port_index, im_port, - &im_port->idr_id); - if (error) { - mutex_unlock(&bfad_mutex); - printk(KERN_WARNING "idr_get_new failure\n"); - goto out; - } - + im_port->idr_id = error; mutex_unlock(&bfad_mutex); im_port->shost = bfad_scsi_host_alloc(im_port, bfad); diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index a15474eef5f7..2a323742ce04 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -895,7 +895,7 @@ static int ch_probe(struct device *dev) { struct scsi_device *sd = to_scsi_device(dev); struct device *class_dev; - int minor, ret = -ENOMEM; + int ret; scsi_changer *ch; if (sd->type != TYPE_MEDIUM_CHANGER) @@ -905,22 +905,19 @@ static int ch_probe(struct device *dev) if (NULL == ch) return -ENOMEM; - if (!idr_pre_get(&ch_index_idr, GFP_KERNEL)) - goto free_ch; - + idr_preload(GFP_KERNEL); spin_lock(&ch_index_lock); - ret = idr_get_new(&ch_index_idr, ch, &minor); + ret = idr_alloc(&ch_index_idr, ch, 0, CH_MAX_DEVS + 1, GFP_NOWAIT); spin_unlock(&ch_index_lock); + idr_preload_end(); - if (ret) + if (ret < 0) { + if (ret == -ENOSPC) + ret = -ENODEV; goto free_ch; - - if (minor > CH_MAX_DEVS) { - ret = -ENODEV; - goto remove_idr; } - ch->minor = minor; + ch->minor = ret; sprintf(ch->name,"ch%d",ch->minor); class_dev = device_create(ch_sysfs_class, dev, @@ -944,7 +941,7 @@ static int ch_probe(struct device *dev) return 0; remove_idr: - idr_remove(&ch_index_idr, minor); + idr_remove(&ch_index_idr, ch->minor); free_ch: kfree(ch); return ret; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 26ca2efa976e..314b4f61b9e3 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -3165,14 +3165,10 @@ destroy_port(struct lpfc_vport *vport) int lpfc_get_instance(void) { - int instance = 0; - - /* Assign an unused number */ - if (!idr_pre_get(&lpfc_hba_index, GFP_KERNEL)) - return -1; - if (idr_get_new(&lpfc_hba_index, NULL, &instance)) - return -1; - return instance; + int ret; + + ret = idr_alloc(&lpfc_hba_index, NULL, 0, 0, GFP_KERNEL); + return ret < 0 ? -1 : ret; } /** diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index be2c9a6561ff..df5e961484e1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -35,6 +35,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #include <linux/sched.h> #include <linux/string.h> #include <linux/mm.h> +#include <linux/aio.h> #include <linux/errno.h> #include <linux/mtio.h> #include <linux/ioctl.h> @@ -1391,24 +1392,23 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) return ERR_PTR(-ENOMEM); } - if (!idr_pre_get(&sg_index_idr, GFP_KERNEL)) { - printk(KERN_WARNING "idr expansion Sg_device failure\n"); - error = -ENOMEM; - goto out; - } - + idr_preload(GFP_KERNEL); write_lock_irqsave(&sg_index_lock, iflags); - error = idr_get_new(&sg_index_idr, sdp, &k); - if (error) { - write_unlock_irqrestore(&sg_index_lock, iflags); - printk(KERN_WARNING "idr allocation Sg_device failure: %d\n", - error); - goto out; + error = idr_alloc(&sg_index_idr, sdp, 0, SG_MAX_DEVS, GFP_NOWAIT); + if (error < 0) { + if (error == -ENOSPC) { + sdev_printk(KERN_WARNING, scsidp, + "Unable to attach sg device type=%d, minor number exceeds %d\n", + scsidp->type, SG_MAX_DEVS - 1); + error = -ENODEV; + } else { + printk(KERN_WARNING + "idr allocation Sg_device failure: %d\n", error); + } + goto out_unlock; } - - if (unlikely(k >= SG_MAX_DEVS)) - goto overflow; + k = error; SCSI_LOG_TIMEOUT(3, printk("sg_alloc: dev=%d \n", k)); sprintf(disk->disk_name, "sg%d", k); @@ -1420,25 +1420,17 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) sdp->sg_tablesize = queue_max_segments(q); sdp->index = k; kref_init(&sdp->d_ref); + error = 0; +out_unlock: write_unlock_irqrestore(&sg_index_lock, iflags); + idr_preload_end(); - error = 0; - out: if (error) { kfree(sdp); return ERR_PTR(error); } return sdp; - - overflow: - idr_remove(&sg_index_idr, k); - write_unlock_irqrestore(&sg_index_lock, iflags); - sdev_printk(KERN_WARNING, scsidp, - "Unable to attach sg device type=%d, minor " - "number exceeds %d\n", scsidp->type, SG_MAX_DEVS - 1); - error = -ENODEV; - goto out; } static int diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 98156a97c472..7c6edcaf880f 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4076,7 +4076,7 @@ static int st_probe(struct device *dev) struct st_modedef *STm; struct st_partstat *STps; struct st_buffer *buffer; - int i, dev_num, error; + int i, error; char *stp; if (SDp->type != TYPE_TAPE) @@ -4178,27 +4178,17 @@ static int st_probe(struct device *dev) tpnt->blksize_changed = 0; mutex_init(&tpnt->lock); - if (!idr_pre_get(&st_index_idr, GFP_KERNEL)) { - pr_warn("st: idr expansion failed\n"); - error = -ENOMEM; - goto out_put_disk; - } - + idr_preload(GFP_KERNEL); spin_lock(&st_index_lock); - error = idr_get_new(&st_index_idr, tpnt, &dev_num); + error = idr_alloc(&st_index_idr, tpnt, 0, ST_MAX_TAPES + 1, GFP_NOWAIT); spin_unlock(&st_index_lock); - if (error) { + idr_preload_end(); + if (error < 0) { pr_warn("st: idr allocation failed: %d\n", error); goto out_put_disk; } - - if (dev_num > ST_MAX_TAPES) { - pr_err("st: Too many tape devices (max. %d).\n", ST_MAX_TAPES); - goto out_put_index; - } - - tpnt->index = dev_num; - sprintf(disk->disk_name, "st%d", dev_num); + tpnt->index = error; + sprintf(disk->disk_name, "st%d", tpnt->index); dev_set_drvdata(dev, tpnt); @@ -4218,9 +4208,8 @@ static int st_probe(struct device *dev) out_remove_devs: remove_cdevs(tpnt); -out_put_index: spin_lock(&st_index_lock); - idr_remove(&st_index_idr, dev_num); + idr_remove(&st_index_idr, tpnt->index); spin_unlock(&st_index_lock); out_put_disk: put_disk(disk); diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 538ebe213129..24456a0de6b2 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2880,7 +2880,6 @@ static int binder_release(struct inode *nodp, struct file *filp) static void binder_deferred_release(struct binder_proc *proc) { - struct hlist_node *pos; struct binder_transaction *t; struct rb_node *n; int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count; @@ -2924,7 +2923,7 @@ static void binder_deferred_release(struct binder_proc *proc) node->local_weak_refs = 0; hlist_add_head(&node->dead_node, &binder_dead_nodes); - hlist_for_each_entry(ref, pos, &node->refs, node_entry) { + hlist_for_each_entry(ref, &node->refs, node_entry) { incoming_refs++; if (ref->death) { death++; @@ -3156,12 +3155,11 @@ static void print_binder_thread(struct seq_file *m, static void print_binder_node(struct seq_file *m, struct binder_node *node) { struct binder_ref *ref; - struct hlist_node *pos; struct binder_work *w; int count; count = 0; - hlist_for_each_entry(ref, pos, &node->refs, node_entry) + hlist_for_each_entry(ref, &node->refs, node_entry) count++; seq_printf(m, " node %d: u%p c%p hs %d hw %d ls %d lw %d is %d iw %d", @@ -3171,7 +3169,7 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node) node->internal_strong_refs, count); if (count) { seq_puts(m, " proc"); - hlist_for_each_entry(ref, pos, &node->refs, node_entry) + hlist_for_each_entry(ref, &node->refs, node_entry) seq_printf(m, " %d", ref->proc->pid); } seq_puts(m, "\n"); @@ -3369,7 +3367,6 @@ static void print_binder_proc_stats(struct seq_file *m, static int binder_state_show(struct seq_file *m, void *unused) { struct binder_proc *proc; - struct hlist_node *pos; struct binder_node *node; int do_lock = !binder_debug_no_lock; @@ -3380,10 +3377,10 @@ static int binder_state_show(struct seq_file *m, void *unused) if (!hlist_empty(&binder_dead_nodes)) seq_puts(m, "dead nodes:\n"); - hlist_for_each_entry(node, pos, &binder_dead_nodes, dead_node) + hlist_for_each_entry(node, &binder_dead_nodes, dead_node) print_binder_node(m, node); - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc(m, proc, 1); if (do_lock) binder_unlock(__func__); @@ -3393,7 +3390,6 @@ static int binder_state_show(struct seq_file *m, void *unused) static int binder_stats_show(struct seq_file *m, void *unused) { struct binder_proc *proc; - struct hlist_node *pos; int do_lock = !binder_debug_no_lock; if (do_lock) @@ -3403,7 +3399,7 @@ static int binder_stats_show(struct seq_file *m, void *unused) print_binder_stats(m, "", &binder_stats); - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc_stats(m, proc); if (do_lock) binder_unlock(__func__); @@ -3413,14 +3409,13 @@ static int binder_stats_show(struct seq_file *m, void *unused) static int binder_transactions_show(struct seq_file *m, void *unused) { struct binder_proc *proc; - struct hlist_node *pos; int do_lock = !binder_debug_no_lock; if (do_lock) binder_lock(__func__); seq_puts(m, "binder transactions:\n"); - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) + hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc(m, proc, 0); if (do_lock) binder_unlock(__func__); diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index dbc63cbb4d3a..b4a313cdbed5 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/vmalloc.h> +#include <linux/aio.h> #include "logger.h" #include <asm/ioctls.h> diff --git a/drivers/staging/zcache/zbud.c b/drivers/staging/zcache/zbud.c index 6835fab5d116..79ae9bd00676 100644 --- a/drivers/staging/zcache/zbud.c +++ b/drivers/staging/zcache/zbud.c @@ -404,7 +404,7 @@ static inline struct page *zbud_unuse_zbudpage(struct zbudpage *zbudpage, else zbud_pers_pageframes--; zbudpage_spin_unlock(zbudpage); - reset_page_mapcount(page); + page_mapcount_reset(page); init_page_count(page); page->index = 0; return page; diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 06f73a93a44d..e78d262c5249 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -472,7 +472,7 @@ static void reset_page(struct page *page) set_page_private(page, 0); page->mapping = NULL; page->freelist = NULL; - reset_page_mapcount(page); + page_mapcount_reset(page); } static void free_zspage(struct page *first_page) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 339f97f7085b..f1fdf4f4341c 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -144,23 +144,24 @@ struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf) spin_lock_init(&tiqn->login_stats.lock); spin_lock_init(&tiqn->logout_stats.lock); - if (!idr_pre_get(&tiqn_idr, GFP_KERNEL)) { - pr_err("idr_pre_get() for tiqn_idr failed\n"); - kfree(tiqn); - return ERR_PTR(-ENOMEM); - } tiqn->tiqn_state = TIQN_STATE_ACTIVE; + idr_preload(GFP_KERNEL); spin_lock(&tiqn_lock); - ret = idr_get_new(&tiqn_idr, NULL, &tiqn->tiqn_index); + + ret = idr_alloc(&tiqn_idr, NULL, 0, 0, GFP_NOWAIT); if (ret < 0) { - pr_err("idr_get_new() failed for tiqn->tiqn_index\n"); + pr_err("idr_alloc() failed for tiqn->tiqn_index\n"); spin_unlock(&tiqn_lock); + idr_preload_end(); kfree(tiqn); return ERR_PTR(ret); } + tiqn->tiqn_index = ret; list_add_tail(&tiqn->tiqn_list, &g_tiqn_list); + spin_unlock(&tiqn_lock); + idr_preload_end(); pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn); diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index fdb632f0ab85..2535d4d46c0e 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -247,19 +247,16 @@ static int iscsi_login_zero_tsih_s1( spin_lock_init(&sess->session_usage_lock); spin_lock_init(&sess->ttt_lock); - if (!idr_pre_get(&sess_idr, GFP_KERNEL)) { - pr_err("idr_pre_get() for sess_idr failed\n"); - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, - ISCSI_LOGIN_STATUS_NO_RESOURCES); - kfree(sess); - return -ENOMEM; - } + idr_preload(GFP_KERNEL); spin_lock_bh(&sess_idr_lock); - ret = idr_get_new(&sess_idr, NULL, &sess->session_index); + ret = idr_alloc(&sess_idr, NULL, 0, 0, GFP_NOWAIT); + if (ret >= 0) + sess->session_index = ret; spin_unlock_bh(&sess_idr_lock); + idr_preload_end(); if (ret < 0) { - pr_err("idr_get_new() for sess_idr failed\n"); + pr_err("idr_alloc() for sess_idr failed\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_NO_RESOURCES); kfree(sess); diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 6659dd36e806..113f33598b9f 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -169,7 +169,6 @@ static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id) { struct ft_tport *tport; struct hlist_head *head; - struct hlist_node *pos; struct ft_sess *sess; rcu_read_lock(); @@ -178,7 +177,7 @@ static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id) goto out; head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, pos, head, hash) { + hlist_for_each_entry_rcu(sess, head, hash) { if (sess->port_id == port_id) { kref_get(&sess->kref); rcu_read_unlock(); @@ -201,10 +200,9 @@ static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id, { struct ft_sess *sess; struct hlist_head *head; - struct hlist_node *pos; head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, pos, head, hash) + hlist_for_each_entry_rcu(sess, head, hash) if (sess->port_id == port_id) return sess; @@ -253,11 +251,10 @@ static void ft_sess_unhash(struct ft_sess *sess) static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id) { struct hlist_head *head; - struct hlist_node *pos; struct ft_sess *sess; head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, pos, head, hash) { + hlist_for_each_entry_rcu(sess, head, hash) { if (sess->port_id == port_id) { ft_sess_unhash(sess); return sess; @@ -273,12 +270,11 @@ static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id) static void ft_sess_delete_all(struct ft_tport *tport) { struct hlist_head *head; - struct hlist_node *pos; struct ft_sess *sess; for (head = tport->hash; head < &tport->hash[FT_SESS_HASH_SIZE]; head++) { - hlist_for_each_entry_rcu(sess, pos, head, hash) { + hlist_for_each_entry_rcu(sess, head, hash) { ft_sess_unhash(sess); transport_deregister_session_configfs(sess->se_sess); ft_sess_put(sess); /* release from table */ diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 455c77a961de..8dc44cbb3e09 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -73,21 +73,14 @@ static struct cpufreq_cooling_device *notify_device; */ static int get_idr(struct idr *idr, int *id) { - int err; -again: - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) - return -ENOMEM; + int ret; mutex_lock(&cooling_cpufreq_lock); - err = idr_get_new(idr, NULL, id); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); mutex_unlock(&cooling_cpufreq_lock); - - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return err; - - *id = *id & MAX_IDR_MASK; + if (unlikely(ret < 0)) + return ret; + *id = ret; return 0; } diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6472e7e9f969..2eaaaa60fb09 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -131,23 +131,16 @@ EXPORT_SYMBOL_GPL(thermal_unregister_governor); static int get_idr(struct idr *idr, struct mutex *lock, int *id) { - int err; - -again: - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) - return -ENOMEM; + int ret; if (lock) mutex_lock(lock); - err = idr_get_new(idr, NULL, id); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); if (lock) mutex_unlock(lock); - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return err; - - *id = *id & MAX_IDR_MASK; + if (unlikely(ret < 0)) + return ret; + *id = ret; return 0; } diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 5110f367f1f1..c8b926291e28 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -369,26 +369,15 @@ static void uio_dev_del_attributes(struct uio_device *idev) static int uio_get_minor(struct uio_device *idev) { int retval = -ENOMEM; - int id; mutex_lock(&minor_lock); - if (idr_pre_get(&uio_idr, GFP_KERNEL) == 0) - goto exit; - - retval = idr_get_new(&uio_idr, idev, &id); - if (retval < 0) { - if (retval == -EAGAIN) - retval = -ENOMEM; - goto exit; - } - if (id < UIO_MAX_DEVICES) { - idev->minor = id; - } else { + retval = idr_alloc(&uio_idr, idev, 0, UIO_MAX_DEVICES, GFP_KERNEL); + if (retval >= 0) { + idev->minor = retval; + } else if (retval == -ENOSPC) { dev_err(idev->dev, "too many uio devices\n"); retval = -EINVAL; - idr_remove(&uio_idr, id); } -exit: mutex_unlock(&minor_lock); return retval; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1775ad471edd..5480352f984d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5177,6 +5177,7 @@ int usb_reset_device(struct usb_device *udev) { int ret; int i; + unsigned int noio_flag; struct usb_host_config *config = udev->actconfig; if (udev->state == USB_STATE_NOTATTACHED || @@ -5186,6 +5187,17 @@ int usb_reset_device(struct usb_device *udev) return -EINVAL; } + /* + * Don't allocate memory with GFP_KERNEL in current + * context to avoid possible deadlock if usb mass + * storage interface or usbnet interface(iSCSI case) + * is included in current configuration. The easist + * approach is to do it for every device reset, + * because the device 'memalloc_noio' flag may have + * not been set before reseting the usb device. + */ + noio_flag = memalloc_noio_save(); + /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); @@ -5230,6 +5242,7 @@ int usb_reset_device(struct usb_device *udev) } usb_autosuspend_device(udev); + memalloc_noio_restore(noio_flag); return ret; } EXPORT_SYMBOL_GPL(usb_reset_device); diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 75973f33a4c8..142616a42f45 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3102,7 +3102,7 @@ static int init_dma_pools(struct udc *dev) } /* DMA setup */ - dev->data_requests = dma_pool_create("data_requests", NULL, + dev->data_requests = dma_pool_create("data_requests", &dev->pdev->dev, sizeof(struct udc_data_dma), 0, 0); if (!dev->data_requests) { DBG(dev, "can't get request data pool\n"); @@ -3114,7 +3114,7 @@ static int init_dma_pools(struct udc *dev) dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl; /* dma desc for setup data */ - dev->stp_requests = dma_pool_create("setup requests", NULL, + dev->stp_requests = dma_pool_create("setup requests", &dev->pdev->dev, sizeof(struct udc_stp_dma), 0, 0); if (!dev->stp_requests) { DBG(dev, "can't get stp request pool\n"); @@ -3235,6 +3235,12 @@ static int udc_pci_probe( pci_set_master(pdev); pci_try_set_mwi(pdev); + dev->phys_addr = resource; + dev->irq = pdev->irq; + dev->pdev = pdev; + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + /* init dma pools */ if (use_dma) { retval = init_dma_pools(dev); @@ -3242,12 +3248,6 @@ static int udc_pci_probe( goto finished; } - dev->phys_addr = resource; - dev->irq = pdev->irq; - dev->pdev = pdev; - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - /* general probing */ if (udc_probe(dev) == 0) return 0; diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 8ac840f25ba9..f4bbc7d39ae9 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -24,6 +24,8 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> +#include <linux/mmu_context.h> +#include <linux/aio.h> #include <linux/device.h> #include <linux/moduleparam.h> @@ -513,6 +515,9 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) struct kiocb_priv { struct usb_request *req; struct ep_data *epdata; + struct kiocb *iocb; + struct mm_struct *mm; + struct work_struct work; void *buf; const struct iovec *iv; unsigned long nr_segs; @@ -528,7 +533,6 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) local_irq_disable(); epdata = priv->epdata; // spin_lock(&epdata->dev->lock); - kiocbSetCancelled(iocb); if (likely(epdata && epdata->ep && priv->req)) value = usb_ep_dequeue (epdata->ep, priv->req); else @@ -540,15 +544,12 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) return value; } -static ssize_t ep_aio_read_retry(struct kiocb *iocb) +static ssize_t ep_copy_to_user(struct kiocb_priv *priv) { - struct kiocb_priv *priv = iocb->private; ssize_t len, total; void *to_copy; int i; - /* we "retry" to get the right mm context for this: */ - /* copy stuff into user buffers */ total = priv->actual; len = 0; @@ -568,9 +569,26 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) if (total == 0) break; } + + return len; +} + +static void ep_user_copy_worker(struct work_struct *work) +{ + struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work); + struct mm_struct *mm = priv->mm; + struct kiocb *iocb = priv->iocb; + size_t ret; + + use_mm(mm); + ret = ep_copy_to_user(priv); + unuse_mm(mm); + + /* completing the iocb can drop the ctx and mm, don't touch mm after */ + aio_complete(iocb, ret, ret); + kfree(priv->buf); kfree(priv); - return len; } static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) @@ -596,14 +614,14 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) aio_complete(iocb, req->actual ? req->actual : req->status, req->status); } else { - /* retry() won't report both; so we hide some faults */ + /* ep_copy_to_user() won't report both; we hide some faults */ if (unlikely(0 != req->status)) DBG(epdata->dev, "%s fault %d len %d\n", ep->name, req->status, req->actual); priv->buf = req->buf; priv->actual = req->actual; - kick_iocb(iocb); + schedule_work(&priv->work); } spin_unlock(&epdata->dev->lock); @@ -633,8 +651,10 @@ fail: return value; } iocb->private = priv; + priv->iocb = iocb; priv->iv = iv; priv->nr_segs = nr_segs; + INIT_WORK(&priv->work, ep_user_copy_worker); value = get_ready_ep(iocb->ki_filp->f_flags, epdata); if (unlikely(value < 0)) { @@ -642,10 +662,11 @@ fail: goto fail; } - iocb->ki_cancel = ep_aio_cancel; + kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; + priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ /* each kiocb is coupled to one usb_request, but we can't * allocate or submit those if the host disconnected. @@ -674,7 +695,7 @@ fail: kfree(priv); put_ep(epdata); } else - value = (iv ? -EIOCBRETRY : -EIOCBQUEUED); + value = -EIOCBQUEUED; return value; } @@ -692,7 +713,6 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, if (unlikely(!buf)) return -ENOMEM; - iocb->ki_retry = ep_aio_read_retry; return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); } diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 12c264d3b058..7f61abfa4563 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -139,23 +139,8 @@ EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver); */ static int vfio_alloc_group_minor(struct vfio_group *group) { - int ret, minor; - -again: - if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0)) - return -ENOMEM; - /* index 0 is used by /dev/vfio/vfio */ - ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor); - if (ret == -EAGAIN) - goto again; - if (ret || minor > MINORMASK) { - if (minor > MINORMASK) - idr_remove(&vfio.group_idr, minor); - return -ENOSPC; - } - - return minor; + return idr_alloc(&vfio.group_idr, group, 1, MINORMASK + 1, GFP_KERNEL); } static void vfio_free_group_minor(int minor) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e4e1765b82f2..55a66a60bb32 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -364,7 +364,7 @@ config FB_SA1100 Y here. config FB_IMX - tristate "Freescale i.MX LCD support" + tristate "Freescale i.MX1/21/25/27 LCD support" depends on FB && IMX_HAVE_PLATFORM_IMX_FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -2183,6 +2183,15 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. +config FB_GOLDFISH + tristate "Goldfish Framebuffer" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for Goldfish Virtual Platform + config FB_COBALT tristate "Cobalt server LCD frame buffer support" depends on FB && (MIPS_COBALT || MIPS_SEAD3) @@ -2422,6 +2431,7 @@ config FB_PUV3_UNIGFX source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" +source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" if VT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 768a137a1bac..0577f834fdcd 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o @@ -105,6 +106,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o +obj-$(CONFIG_MMP_DISP) += mmp/ obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index c072ed9aea36..2cd63507ed74 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -165,8 +165,10 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev, struct pm860x_backlight_data *data, char *name) { - struct device_node *nproot = pdev->dev.parent->of_node, *np; + struct device_node *nproot, *np; int iset = 0; + + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; nproot = of_find_node_by_name(nproot, "backlights"); @@ -184,6 +186,7 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev, break; } } + of_node_put(nproot); return 0; } #else diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 765a945f8ea1..e2fc9f50bbea 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -126,6 +126,21 @@ config LCD_AMS369FG06 If you have an AMS369FG06 AMOLED Panel, say Y to enable its LCD control driver. +config LCD_LMS501KF03 + tristate "LMS501KF03 LCD Driver" + depends on SPI + default n + help + If you have an LMS501KF03 LCD Panel, say Y to enable its + LCD control driver. + +config LCD_HX8357 + tristate "Himax HX-8357 LCD Driver" + depends on SPI + help + If you have a HX-8357 LCD panel, say Y to enable its LCD control + driver. + endif # LCD_CLASS_DEVICE # @@ -369,6 +384,12 @@ config BACKLIGHT_LP855X This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556 backlight driver. +config BACKLIGHT_LP8788 + tristate "Backlight driver for TI LP8788 MFD" + depends on BACKLIGHT_CLASS_DEVICE && MFD_LP8788 + help + This supports TI LP8788 backlight driver. + config BACKLIGHT_OT200 tristate "Backlight driver for ot200 visualisation device" depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535 @@ -390,6 +411,13 @@ config BACKLIGHT_TPS65217 If you have a Texas Instruments TPS65217 say Y to enable the backlight driver. +config BACKLIGHT_AS3711 + tristate "AS3711 Backlight" + depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711 + help + If you have an Austrian Microsystems AS3711 say Y to enable the + backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index e7ce7291635d..96c4d620c5ce 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,47 +1,51 @@ # Backlight & LCD drivers -obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o -obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o -obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o -obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o -obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o -obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o -obj-$(CONFIG_LCD_ILI9320) += ili9320.o -obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o -obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o -obj-$(CONFIG_LCD_TDO24M) += tdo24m.o -obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o -obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o -obj-$(CONFIG_LCD_LD9040) += ld9040.o -obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o +obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o +obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o +obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o +obj-$(CONFIG_LCD_HX8357) += hx8357.o +obj-$(CONFIG_LCD_ILI9320) += ili9320.o +obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o +obj-$(CONFIG_LCD_LD9040) += ld9040.o +obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o +obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o +obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o +obj-$(CONFIG_LCD_TDO24M) += tdo24m.o +obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o +obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o -obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o -obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o -obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o -obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o -obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o -obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o -obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o -obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o -obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o -obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o -obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o -obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o -obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o -obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o -obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o -obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o -obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o -obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o -obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o -obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o -obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o -obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o -obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o -obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o -obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o -obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o +obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o +obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o +obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o +obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o +obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o +obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o +obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o +obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o +obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o +obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o +obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o +obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o +obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o +obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o +obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o +obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o +obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o +obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o +obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o +obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o +obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o -obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o -obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o -obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o +obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o +obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o +obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o +obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index 7ff752288b92..c6fc668d6236 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -74,7 +74,7 @@ static int aat2870_bl_get_brightness(struct backlight_device *bd) static int aat2870_bl_update_status(struct backlight_device *bd) { - struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev); + struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd); struct aat2870_data *aat2870 = dev_get_drvdata(aat2870_bl->pdev->dev.parent); int brightness = bd->props.brightness; diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c index f57e1905236a..d29e49443f29 100644 --- a/drivers/video/backlight/ams369fg06.c +++ b/drivers/video/backlight/ams369fg06.c @@ -10,25 +10,16 @@ * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> #include <linux/lcd.h> -#include <linux/backlight.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #define SLEEPMSEC 0x1000 #define ENDDEF 0x2000 @@ -210,8 +201,9 @@ static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd, ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - mdelay(wbuf[i+1]); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -313,41 +305,32 @@ static int ams369fg06_ldi_disable(struct ams369fg06 *lcd) static int ams369fg06_power_is_on(int power) { - return ((power) <= FB_BLANK_NORMAL); + return power <= FB_BLANK_NORMAL; } static int ams369fg06_power_on(struct ams369fg06 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; - struct backlight_device *bd = NULL; + struct lcd_platform_data *pd; + struct backlight_device *bd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } - bd = lcd->bd; - if (!bd) { - dev_err(lcd->dev, "backlight device is NULL.\n"); - return -EFAULT; - } if (!pd->power_on) { dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->power_on(lcd->ld, 1); - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); } if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = ams369fg06_ldi_init(lcd); @@ -374,14 +357,10 @@ static int ams369fg06_power_on(struct ams369fg06 *lcd) static int ams369fg06_power_off(struct ams369fg06 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL\n"); - return -EFAULT; - } ret = ams369fg06_ldi_disable(lcd); if (ret) { @@ -389,13 +368,9 @@ static int ams369fg06_power_off(struct ams369fg06 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); - if (!pd->power_on) { - dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; - } else - pd->power_on(lcd->ld, 0); + pd->power_on(lcd->ld, 0); return 0; } @@ -446,7 +421,7 @@ static int ams369fg06_set_brightness(struct backlight_device *bd) { int ret = 0; int brightness = bd->props.brightness; - struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev); + struct ams369fg06 *lcd = bl_get_data(bd); if (brightness < MIN_BRIGHTNESS || brightness > bd->props.max_brightness) { @@ -501,7 +476,7 @@ static int ams369fg06_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL\n"); - return -EFAULT; + return -EINVAL; } ld = lcd_device_register("ams369fg06", &spi->dev, lcd, @@ -534,10 +509,11 @@ static int ams369fg06_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; ams369fg06_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); @@ -550,7 +526,7 @@ out_lcd_unregister: static int ams369fg06_remove(struct spi_device *spi) { - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); ams369fg06_power(lcd, FB_BLANK_POWERDOWN); backlight_device_unregister(lcd->bd); @@ -560,44 +536,26 @@ static int ams369fg06_remove(struct spi_device *spi) } #if defined(CONFIG_PM) -static unsigned int before_power; - static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); - before_power = lcd->power; - /* * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return ams369fg06_power(lcd, FB_BLANK_POWERDOWN); } static int ams369fg06_resume(struct spi_device *spi) { - int ret = 0; - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); - /* - * after suspended, if lcd panel status is FB_BLANK_UNBLANK - * (at that time, before_power is FB_BLANK_UNBLANK) then - * it changes that status to FB_BLANK_POWERDOWN to get lcd on. - */ - if (before_power == FB_BLANK_UNBLANK) - lcd->power = FB_BLANK_POWERDOWN; - - dev_dbg(&spi->dev, "before_power = %d\n", before_power); + lcd->power = FB_BLANK_POWERDOWN; - ret = ams369fg06_power(lcd, before_power); - - return ret; + return ams369fg06_power(lcd, FB_BLANK_UNBLANK); } #else #define ams369fg06_suspend NULL @@ -606,7 +564,7 @@ static int ams369fg06_resume(struct spi_device *spi) static void ams369fg06_shutdown(struct spi_device *spi) { - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); ams369fg06_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c new file mode 100644 index 000000000000..41d52fe52543 --- /dev/null +++ b/drivers/video/backlight/as3711_bl.c @@ -0,0 +1,380 @@ +/* + * AS3711 PMIC backlight driver, using DCDC Step Up Converters + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/kernel.h> +#include <linux/mfd/as3711.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +enum as3711_bl_type { + AS3711_BL_SU1, + AS3711_BL_SU2, +}; + +struct as3711_bl_data { + bool powered; + const char *fb_name; + struct device *fb_dev; + enum as3711_bl_type type; + int brightness; + struct backlight_device *bl; +}; + +struct as3711_bl_supply { + struct as3711_bl_data su1; + struct as3711_bl_data su2; + const struct as3711_bl_pdata *pdata; + struct as3711 *as3711; +}; + +static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su) +{ + switch (su->type) { + case AS3711_BL_SU1: + return container_of(su, struct as3711_bl_supply, su1); + case AS3711_BL_SU2: + return container_of(su, struct as3711_bl_supply, su2); + } + return NULL; +} + +static int as3711_set_brightness_auto_i(struct as3711_bl_data *data, + unsigned int brightness) +{ + struct as3711_bl_supply *supply = to_supply(data); + struct as3711 *as3711 = supply->as3711; + const struct as3711_bl_pdata *pdata = supply->pdata; + int ret = 0; + + /* Only all equal current values are supported */ + if (pdata->su2_auto_curr1) + ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, + brightness); + if (!ret && pdata->su2_auto_curr2) + ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, + brightness); + if (!ret && pdata->su2_auto_curr3) + ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, + brightness); + + return ret; +} + +static int as3711_set_brightness_v(struct as3711 *as3711, + unsigned int brightness, + unsigned int reg) +{ + if (brightness > 31) + return -EINVAL; + + return regmap_update_bits(as3711->regmap, reg, 0xf0, + brightness << 4); +} + +static int as3711_bl_su2_reset(struct as3711_bl_supply *supply) +{ + struct as3711 *as3711 = supply->as3711; + int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5, + 3, supply->pdata->su2_fbprot); + if (!ret) + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 0); + if (!ret) + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 1); + return ret; +} + +/* + * Someone with less fragile or less expensive hardware could try to simplify + * the brightness adjustment procedure. + */ +static int as3711_bl_update_status(struct backlight_device *bl) +{ + struct as3711_bl_data *data = bl_get_data(bl); + struct as3711_bl_supply *supply = to_supply(data); + struct as3711 *as3711 = supply->as3711; + int brightness = bl->props.brightness; + int ret = 0; + + dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n", + __func__, bl->props.brightness, bl->props.power, + bl->props.fb_blank, bl->props.state); + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (data->type == AS3711_BL_SU1) { + ret = as3711_set_brightness_v(as3711, brightness, + AS3711_STEPUP_CONTROL_1); + } else { + const struct as3711_bl_pdata *pdata = supply->pdata; + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + ret = as3711_set_brightness_v(as3711, brightness, + AS3711_STEPUP_CONTROL_2); + break; + case AS3711_SU2_CURR_AUTO: + ret = as3711_set_brightness_auto_i(data, brightness / 4); + if (ret < 0) + return ret; + if (brightness) { + ret = as3711_bl_su2_reset(supply); + if (ret < 0) + return ret; + udelay(500); + ret = as3711_set_brightness_auto_i(data, brightness); + } else { + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 0); + } + break; + /* Manual one current feedback pin below */ + case AS3711_SU2_CURR1: + ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, + brightness); + break; + case AS3711_SU2_CURR2: + ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, + brightness); + break; + case AS3711_SU2_CURR3: + ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, + brightness); + break; + default: + ret = -EINVAL; + } + } + if (!ret) + data->brightness = brightness; + + return ret; +} + +static int as3711_bl_get_brightness(struct backlight_device *bl) +{ + struct as3711_bl_data *data = bl_get_data(bl); + + return data->brightness; +} + +static const struct backlight_ops as3711_bl_ops = { + .update_status = as3711_bl_update_status, + .get_brightness = as3711_bl_get_brightness, +}; + +static int as3711_bl_init_su2(struct as3711_bl_supply *supply) +{ + struct as3711 *as3711 = supply->as3711; + const struct as3711_bl_pdata *pdata = supply->pdata; + u8 ctl = 0; + int ret; + + dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback); + + /* Turn SU2 off */ + ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0); + if (ret < 0) + return ret; + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0); + break; + case AS3711_SU2_CURR1: + ctl = 1; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1); + break; + case AS3711_SU2_CURR2: + ctl = 4; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2); + break; + case AS3711_SU2_CURR3: + ctl = 0x10; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3); + break; + case AS3711_SU2_CURR_AUTO: + if (pdata->su2_auto_curr1) + ctl = 2; + if (pdata->su2_auto_curr2) + ctl |= 8; + if (pdata->su2_auto_curr3) + ctl |= 0x20; + ret = 0; + break; + default: + return -EINVAL; + } + + if (!ret) + ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl); + + return ret; +} + +static int as3711_bl_register(struct platform_device *pdev, + unsigned int max_brightness, struct as3711_bl_data *su) +{ + struct backlight_properties props = {.type = BACKLIGHT_RAW,}; + struct backlight_device *bl; + + /* max tuning I = 31uA for voltage- and 38250uA for current-feedback */ + props.max_brightness = max_brightness; + + bl = backlight_device_register(su->type == AS3711_BL_SU1 ? + "as3711-su1" : "as3711-su2", + &pdev->dev, su, + &as3711_bl_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + return PTR_ERR(bl); + } + + bl->props.brightness = props.max_brightness; + + backlight_update_status(bl); + + su->bl = bl; + + return 0; +} + +static int as3711_backlight_probe(struct platform_device *pdev) +{ + struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev); + struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent); + struct as3711_bl_supply *supply; + struct as3711_bl_data *su; + unsigned int max_brightness; + int ret; + + if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + /* + * Due to possible hardware damage I chose to block all modes, + * unsupported on my hardware. Anyone, wishing to use any of those modes + * will have to first review the code, then activate and test it. + */ + if (pdata->su1_fb || + pdata->su2_fbprot != AS3711_SU2_GPIO4 || + pdata->su2_feedback != AS3711_SU2_CURR_AUTO) { + dev_warn(&pdev->dev, + "Attention! An untested mode has been chosen!\n" + "Please, review the code, enable, test, and report success:-)\n"); + return -EINVAL; + } + + supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL); + if (!supply) + return -ENOMEM; + + supply->as3711 = as3711; + supply->pdata = pdata; + + if (pdata->su1_fb) { + su = &supply->su1; + su->fb_name = pdata->su1_fb; + su->type = AS3711_BL_SU1; + + max_brightness = min(pdata->su1_max_uA, 31); + ret = as3711_bl_register(pdev, max_brightness, su); + if (ret < 0) + return ret; + } + + if (pdata->su2_fb) { + su = &supply->su2; + su->fb_name = pdata->su2_fb; + su->type = AS3711_BL_SU2; + + switch (pdata->su2_fbprot) { + case AS3711_SU2_GPIO2: + case AS3711_SU2_GPIO3: + case AS3711_SU2_GPIO4: + case AS3711_SU2_LX_SD4: + break; + default: + ret = -EINVAL; + goto esu2; + } + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + max_brightness = min(pdata->su2_max_uA, 31); + break; + case AS3711_SU2_CURR1: + case AS3711_SU2_CURR2: + case AS3711_SU2_CURR3: + case AS3711_SU2_CURR_AUTO: + max_brightness = min(pdata->su2_max_uA / 150, 255); + break; + default: + ret = -EINVAL; + goto esu2; + } + + ret = as3711_bl_init_su2(supply); + if (ret < 0) + return ret; + + ret = as3711_bl_register(pdev, max_brightness, su); + if (ret < 0) + goto esu2; + } + + platform_set_drvdata(pdev, supply); + + return 0; + +esu2: + backlight_device_unregister(supply->su1.bl); + return ret; +} + +static int as3711_backlight_remove(struct platform_device *pdev) +{ + struct as3711_bl_supply *supply = platform_get_drvdata(pdev); + + backlight_device_unregister(supply->su1.bl); + backlight_device_unregister(supply->su2.bl); + + return 0; +} + +static struct platform_driver as3711_backlight_driver = { + .driver = { + .name = "as3711-backlight", + .owner = THIS_MODULE, + }, + .probe = as3711_backlight_probe, + .remove = as3711_backlight_remove, +}; + +module_platform_driver(as3711_backlight_driver); + +MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:as3711-backlight"); diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index e323fcbe884e..aa782f302983 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -337,7 +337,7 @@ static void corgi_lcd_power_off(struct corgi_lcd *lcd) static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); int mode = CORGI_LCD_MODE_QVGA; if (m->xres == 640 || m->xres == 480) @@ -364,7 +364,7 @@ static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) static int corgi_lcd_set_power(struct lcd_device *ld, int power) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) corgi_lcd_power_on(lcd); @@ -378,7 +378,7 @@ static int corgi_lcd_set_power(struct lcd_device *ld, int power) static int corgi_lcd_get_power(struct lcd_device *ld) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); return lcd->power; } @@ -391,7 +391,7 @@ static struct lcd_ops corgi_lcd_ops = { static int corgi_bl_get_intensity(struct backlight_device *bd) { - struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + struct corgi_lcd *lcd = bl_get_data(bd); return lcd->intensity; } @@ -423,7 +423,7 @@ static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) static int corgi_bl_update_status(struct backlight_device *bd) { - struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + struct corgi_lcd *lcd = bl_get_data(bd); int intensity = bd->props.brightness; if (bd->props.power != FB_BLANK_UNBLANK) @@ -460,7 +460,7 @@ static const struct backlight_ops corgi_bl_ops = { #ifdef CONFIG_PM static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); corgibl_flags |= CORGIBL_SUSPENDED; corgi_bl_set_intensity(lcd, 0); @@ -470,7 +470,7 @@ static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) static int corgi_lcd_resume(struct spi_device *spi) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); corgibl_flags &= ~CORGIBL_SUSPENDED; corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); @@ -577,7 +577,7 @@ static int corgi_lcd_probe(struct spi_device *spi) lcd->kick_battery = pdata->kick_battery; - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); @@ -594,7 +594,7 @@ err_unregister_lcd: static int corgi_lcd_remove(struct spi_device *spi) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); lcd->bl_dev->props.power = FB_BLANK_UNBLANK; lcd->bl_dev->props.brightness = 0; diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c new file mode 100644 index 000000000000..00925c0a79b9 --- /dev/null +++ b/drivers/video/backlight/hx8357.c @@ -0,0 +1,482 @@ +/* + * Driver for the Himax HX-8357 LCD Controller + * + * Copyright 2012 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/delay.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/spi/spi.h> + +#define HX8357_NUM_IM_PINS 3 + +#define HX8357_SWRESET 0x01 +#define HX8357_GET_RED_CHANNEL 0x06 +#define HX8357_GET_GREEN_CHANNEL 0x07 +#define HX8357_GET_BLUE_CHANNEL 0x08 +#define HX8357_GET_POWER_MODE 0x0a +#define HX8357_GET_MADCTL 0x0b +#define HX8357_GET_PIXEL_FORMAT 0x0c +#define HX8357_GET_DISPLAY_MODE 0x0d +#define HX8357_GET_SIGNAL_MODE 0x0e +#define HX8357_GET_DIAGNOSTIC_RESULT 0x0f +#define HX8357_ENTER_SLEEP_MODE 0x10 +#define HX8357_EXIT_SLEEP_MODE 0x11 +#define HX8357_ENTER_PARTIAL_MODE 0x12 +#define HX8357_ENTER_NORMAL_MODE 0x13 +#define HX8357_EXIT_INVERSION_MODE 0x20 +#define HX8357_ENTER_INVERSION_MODE 0x21 +#define HX8357_SET_DISPLAY_OFF 0x28 +#define HX8357_SET_DISPLAY_ON 0x29 +#define HX8357_SET_COLUMN_ADDRESS 0x2a +#define HX8357_SET_PAGE_ADDRESS 0x2b +#define HX8357_WRITE_MEMORY_START 0x2c +#define HX8357_READ_MEMORY_START 0x2e +#define HX8357_SET_PARTIAL_AREA 0x30 +#define HX8357_SET_SCROLL_AREA 0x33 +#define HX8357_SET_TEAR_OFF 0x34 +#define HX8357_SET_TEAR_ON 0x35 +#define HX8357_SET_ADDRESS_MODE 0x36 +#define HX8357_SET_SCROLL_START 0x37 +#define HX8357_EXIT_IDLE_MODE 0x38 +#define HX8357_ENTER_IDLE_MODE 0x39 +#define HX8357_SET_PIXEL_FORMAT 0x3a +#define HX8357_SET_PIXEL_FORMAT_DBI_3BIT (0x1) +#define HX8357_SET_PIXEL_FORMAT_DBI_16BIT (0x5) +#define HX8357_SET_PIXEL_FORMAT_DBI_18BIT (0x6) +#define HX8357_SET_PIXEL_FORMAT_DPI_3BIT (0x1 << 4) +#define HX8357_SET_PIXEL_FORMAT_DPI_16BIT (0x5 << 4) +#define HX8357_SET_PIXEL_FORMAT_DPI_18BIT (0x6 << 4) +#define HX8357_WRITE_MEMORY_CONTINUE 0x3c +#define HX8357_READ_MEMORY_CONTINUE 0x3e +#define HX8357_SET_TEAR_SCAN_LINES 0x44 +#define HX8357_GET_SCAN_LINES 0x45 +#define HX8357_READ_DDB_START 0xa1 +#define HX8357_SET_DISPLAY_MODE 0xb4 +#define HX8357_SET_DISPLAY_MODE_RGB_THROUGH (0x3) +#define HX8357_SET_DISPLAY_MODE_RGB_INTERFACE (1 << 4) +#define HX8357_SET_PANEL_DRIVING 0xc0 +#define HX8357_SET_DISPLAY_FRAME 0xc5 +#define HX8357_SET_RGB 0xc6 +#define HX8357_SET_RGB_ENABLE_HIGH (1 << 1) +#define HX8357_SET_GAMMA 0xc8 +#define HX8357_SET_POWER 0xd0 +#define HX8357_SET_VCOM 0xd1 +#define HX8357_SET_POWER_NORMAL 0xd2 +#define HX8357_SET_PANEL_RELATED 0xe9 + +struct hx8357_data { + unsigned im_pins[HX8357_NUM_IM_PINS]; + unsigned reset; + struct spi_device *spi; + int state; +}; + +static int hx8357_spi_write_then_read(struct lcd_device *lcdev, + void *txbuf, u16 txlen, + void *rxbuf, u16 rxlen) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u16 *local_txbuf = NULL; + int ret = 0; + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + if (txlen) { + int i; + + local_txbuf = kcalloc(sizeof(*local_txbuf), txlen, GFP_KERNEL); + + if (!local_txbuf) { + dev_err(&lcdev->dev, "Couldn't allocate data buffer.\n"); + return -ENOMEM; + } + + for (i = 0; i < txlen; i++) { + local_txbuf[i] = ((u8 *)txbuf)[i]; + if (i > 0) + local_txbuf[i] |= 1 << 8; + } + + xfer[0].len = 2 * txlen; + xfer[0].bits_per_word = 9; + xfer[0].tx_buf = local_txbuf; + spi_message_add_tail(&xfer[0], &msg); + } + + if (rxlen) { + xfer[1].len = rxlen; + xfer[1].bits_per_word = 8; + xfer[1].rx_buf = rxbuf; + spi_message_add_tail(&xfer[1], &msg); + } + + ret = spi_sync(lcd->spi, &msg); + if (ret < 0) + dev_err(&lcdev->dev, "Couldn't send SPI data.\n"); + + if (txlen) + kfree(local_txbuf); + + return ret; +} + +static inline int hx8357_spi_write_array(struct lcd_device *lcdev, + u8 *value, u8 len) +{ + return hx8357_spi_write_then_read(lcdev, value, len, NULL, 0); +} + +static inline int hx8357_spi_write_byte(struct lcd_device *lcdev, + u8 value) +{ + return hx8357_spi_write_then_read(lcdev, &value, 1, NULL, 0); +} + +static int hx8357_enter_standby(struct lcd_device *lcdev) +{ + int ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF); + if (ret < 0) + return ret; + + usleep_range(10000, 12000); + + ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + return 0; +} + +static int hx8357_exit_standby(struct lcd_device *lcdev) +{ + int ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); + if (ret < 0) + return ret; + + return 0; +} + +static int hx8357_lcd_init(struct lcd_device *lcdev) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + u8 buf[16]; + int ret; + + /* + * Set the interface selection pins to SPI mode, with three + * wires + */ + gpio_set_value_cansleep(lcd->im_pins[0], 1); + gpio_set_value_cansleep(lcd->im_pins[1], 0); + gpio_set_value_cansleep(lcd->im_pins[2], 1); + + /* Reset the screen */ + gpio_set_value(lcd->reset, 1); + usleep_range(10000, 12000); + gpio_set_value(lcd->reset, 0); + usleep_range(10000, 12000); + gpio_set_value(lcd->reset, 1); + msleep(120); + + buf[0] = HX8357_SET_POWER; + buf[1] = 0x44; + buf[2] = 0x41; + buf[3] = 0x06; + ret = hx8357_spi_write_array(lcdev, buf, 4); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_VCOM; + buf[1] = 0x40; + buf[2] = 0x10; + ret = hx8357_spi_write_array(lcdev, buf, 3); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_POWER_NORMAL; + buf[1] = 0x05; + buf[2] = 0x12; + ret = hx8357_spi_write_array(lcdev, buf, 3); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_PANEL_DRIVING; + buf[1] = 0x14; + buf[2] = 0x3b; + buf[3] = 0x00; + buf[4] = 0x02; + buf[5] = 0x11; + ret = hx8357_spi_write_array(lcdev, buf, 6); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_DISPLAY_FRAME; + buf[1] = 0x0c; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_PANEL_RELATED; + buf[1] = 0x01; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + buf[0] = 0xea; + buf[1] = 0x03; + buf[2] = 0x00; + buf[3] = 0x00; + ret = hx8357_spi_write_array(lcdev, buf, 4); + if (ret < 0) + return ret; + + buf[0] = 0xeb; + buf[1] = 0x40; + buf[2] = 0x54; + buf[3] = 0x26; + buf[4] = 0xdb; + ret = hx8357_spi_write_array(lcdev, buf, 5); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_GAMMA; + buf[1] = 0x00; + buf[2] = 0x15; + buf[3] = 0x00; + buf[4] = 0x22; + buf[5] = 0x00; + buf[6] = 0x08; + buf[7] = 0x77; + buf[8] = 0x26; + buf[9] = 0x77; + buf[10] = 0x22; + buf[11] = 0x04; + buf[12] = 0x00; + ret = hx8357_spi_write_array(lcdev, buf, 13); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_ADDRESS_MODE; + buf[1] = 0xc0; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_PIXEL_FORMAT; + buf[1] = HX8357_SET_PIXEL_FORMAT_DPI_18BIT | + HX8357_SET_PIXEL_FORMAT_DBI_18BIT; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_COLUMN_ADDRESS; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x01; + buf[4] = 0x3f; + ret = hx8357_spi_write_array(lcdev, buf, 5); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_PAGE_ADDRESS; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x01; + buf[4] = 0xdf; + ret = hx8357_spi_write_array(lcdev, buf, 5); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_RGB; + buf[1] = 0x02; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + buf[0] = HX8357_SET_DISPLAY_MODE; + buf[1] = HX8357_SET_DISPLAY_MODE_RGB_THROUGH | + HX8357_SET_DISPLAY_MODE_RGB_INTERFACE; + ret = hx8357_spi_write_array(lcdev, buf, 2); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); + if (ret < 0) + return ret; + + usleep_range(5000, 7000); + + ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START); + if (ret < 0) + return ret; + + return 0; +} + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +static int hx8357_set_power(struct lcd_device *lcdev, int power) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state)) + ret = hx8357_exit_standby(lcdev); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state)) + ret = hx8357_enter_standby(lcdev); + + if (ret == 0) + lcd->state = power; + else + dev_warn(&lcdev->dev, "failed to set power mode %d\n", power); + + return ret; +} + +static int hx8357_get_power(struct lcd_device *lcdev) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + + return lcd->state; +} + +static struct lcd_ops hx8357_ops = { + .set_power = hx8357_set_power, + .get_power = hx8357_get_power, +}; + +static int hx8357_probe(struct spi_device *spi) +{ + struct lcd_device *lcdev; + struct hx8357_data *lcd; + int i, ret; + + lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "Couldn't allocate lcd internal structure!\n"); + return -ENOMEM; + } + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "SPI setup failed.\n"); + return ret; + } + + lcd->spi = spi; + + lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); + if (!gpio_is_valid(lcd->reset)) { + dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); + return -EINVAL; + } + + ret = devm_gpio_request_one(&spi->dev, lcd->reset, + GPIOF_OUT_INIT_HIGH, + "hx8357-reset"); + if (ret) { + dev_err(&spi->dev, + "failed to request gpio %d: %d\n", + lcd->reset, ret); + return -EINVAL; + } + + for (i = 0; i < HX8357_NUM_IM_PINS; i++) { + lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node, + "im-gpios", i); + if (lcd->im_pins[i] == -EPROBE_DEFER) { + dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n"); + return -EPROBE_DEFER; + } + if (!gpio_is_valid(lcd->im_pins[i])) { + dev_err(&spi->dev, "Missing dt property: im-gpios\n"); + return -EINVAL; + } + + ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i], + GPIOF_OUT_INIT_LOW, "im_pins"); + if (ret) { + dev_err(&spi->dev, "failed to request gpio %d: %d\n", + lcd->im_pins[i], ret); + return -EINVAL; + } + } + + lcdev = lcd_device_register("mxsfb", &spi->dev, lcd, &hx8357_ops); + if (IS_ERR(lcdev)) { + ret = PTR_ERR(lcdev); + return ret; + } + spi_set_drvdata(spi, lcdev); + + ret = hx8357_lcd_init(lcdev); + if (ret) { + dev_err(&spi->dev, "Couldn't initialize panel\n"); + goto init_error; + } + + dev_info(&spi->dev, "Panel probed\n"); + + return 0; + +init_error: + lcd_device_unregister(lcdev); + return ret; +} + +static int hx8357_remove(struct spi_device *spi) +{ + struct lcd_device *lcdev = spi_get_drvdata(spi); + + lcd_device_unregister(lcdev); + return 0; +} + +static const struct of_device_id hx8357_dt_ids[] = { + { .compatible = "himax,hx8357" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hx8357_dt_ids); + +static struct spi_driver hx8357_driver = { + .probe = hx8357_probe, + .remove = hx8357_remove, + .driver = { + .name = "hx8357", + .of_match_table = of_match_ptr(hx8357_dt_ids), + }, +}; + +module_spi_driver(hx8357_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Himax HX-8357 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 9a35196d12d7..fb6155771326 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -49,7 +49,7 @@ static void l4f00242t03_reset(unsigned int gpio) static void l4f00242t03_lcd_init(struct spi_device *spi) { struct l4f00242t03_pdata *pdata = spi->dev.platform_data; - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) }; dev_dbg(&spi->dev, "initializing LCD\n"); @@ -70,7 +70,7 @@ static void l4f00242t03_lcd_init(struct spi_device *spi) static void l4f00242t03_lcd_powerdown(struct spi_device *spi) { struct l4f00242t03_pdata *pdata = spi->dev.platform_data; - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); dev_dbg(&spi->dev, "Powering down LCD\n"); @@ -168,7 +168,7 @@ static int l4f00242t03_probe(struct spi_device *spi) return -ENOMEM; } - dev_set_drvdata(&spi->dev, priv); + spi_set_drvdata(spi, priv); spi->bits_per_word = 9; spi_setup(spi); @@ -190,27 +190,24 @@ static int l4f00242t03_probe(struct spi_device *spi) return ret; } - priv->io_reg = regulator_get(&spi->dev, "vdd"); + priv->io_reg = devm_regulator_get(&spi->dev, "vdd"); if (IS_ERR(priv->io_reg)) { dev_err(&spi->dev, "%s: Unable to get the IO regulator\n", __func__); return PTR_ERR(priv->io_reg); } - priv->core_reg = regulator_get(&spi->dev, "vcore"); + priv->core_reg = devm_regulator_get(&spi->dev, "vcore"); if (IS_ERR(priv->core_reg)) { - ret = PTR_ERR(priv->core_reg); dev_err(&spi->dev, "%s: Unable to get the core regulator\n", __func__); - goto err1; + return PTR_ERR(priv->core_reg); } priv->ld = lcd_device_register("l4f00242t03", &spi->dev, priv, &l4f_ops); - if (IS_ERR(priv->ld)) { - ret = PTR_ERR(priv->ld); - goto err2; - } + if (IS_ERR(priv->ld)) + return PTR_ERR(priv->ld); /* Init the LCD */ l4f00242t03_lcd_init(spi); @@ -220,33 +217,22 @@ static int l4f00242t03_probe(struct spi_device *spi) dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n"); return 0; - -err2: - regulator_put(priv->core_reg); -err1: - regulator_put(priv->io_reg); - - return ret; } static int l4f00242t03_remove(struct spi_device *spi) { - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); lcd_device_unregister(priv->ld); - - dev_set_drvdata(&spi->dev, NULL); - - regulator_put(priv->io_reg); - regulator_put(priv->core_reg); + spi_set_drvdata(spi, NULL); return 0; } static void l4f00242t03_shutdown(struct spi_device *spi) { - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); if (priv) l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c index 1cb352418513..1b642f5f381a 100644 --- a/drivers/video/backlight/ld9040.c +++ b/drivers/video/backlight/ld9040.c @@ -9,29 +9,20 @@ * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <linux/wait.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> -#include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/lcd.h> -#include <linux/backlight.h> #include <linux/module.h> #include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #include "ld9040_gamma.h" @@ -43,7 +34,6 @@ #define MIN_BRIGHTNESS 0 #define MAX_BRIGHTNESS 24 -#define power_is_on(pwr) ((pwr) <= FB_BLANK_NORMAL) struct ld9040 { struct device *dev; @@ -78,7 +68,7 @@ static void ld9040_regulator_enable(struct ld9040 *lcd) lcd->enabled = true; } - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); out: mutex_unlock(&lcd->lock); } @@ -474,8 +464,9 @@ static int ld9040_panel_send_sequence(struct ld9040 *lcd, ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - udelay(wbuf[i+1]*1000); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -513,14 +504,9 @@ gamma_err: static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma) { - int ret = 0; - - ret = _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); - - return ret; + return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); } - static int ld9040_ldi_init(struct ld9040 *lcd) { int ret, i; @@ -539,7 +525,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd) for (i = 0; i < ARRAY_SIZE(init_seq); i++) { ret = ld9040_panel_send_sequence(lcd, init_seq[i]); /* workaround: minimum delay time for transferring CMD */ - udelay(300); + usleep_range(300, 310); if (ret) break; } @@ -549,11 +535,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd) static int ld9040_ldi_enable(struct ld9040 *lcd) { - int ret = 0; - - ret = ld9040_panel_send_sequence(lcd, seq_display_on); - - return ret; + return ld9040_panel_send_sequence(lcd, seq_display_on); } static int ld9040_ldi_disable(struct ld9040 *lcd) @@ -566,25 +548,27 @@ static int ld9040_ldi_disable(struct ld9040 *lcd) return ret; } +static int ld9040_power_is_on(int power) +{ + return power <= FB_BLANK_NORMAL; +} + static int ld9040_power_on(struct ld9040 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; + struct lcd_platform_data *pd; + pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } /* lcd power on */ ld9040_regulator_enable(lcd); if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = ld9040_ldi_init(lcd); @@ -604,14 +588,10 @@ static int ld9040_power_on(struct ld9040 *lcd) static int ld9040_power_off(struct ld9040 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } ret = ld9040_ldi_disable(lcd); if (ret) { @@ -619,7 +599,7 @@ static int ld9040_power_off(struct ld9040 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); /* lcd power off */ ld9040_regulator_disable(lcd); @@ -631,9 +611,9 @@ static int ld9040_power(struct ld9040 *lcd, int power) { int ret = 0; - if (power_is_on(power) && !power_is_on(lcd->power)) + if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power)) ret = ld9040_power_on(lcd); - else if (!power_is_on(power) && power_is_on(lcd->power)) + else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power)) ret = ld9040_power_off(lcd); if (!ret) @@ -698,7 +678,6 @@ static const struct backlight_ops ld9040_backlight_ops = { .update_status = ld9040_set_brightness, }; - static int ld9040_probe(struct spi_device *spi) { int ret = 0; @@ -726,22 +705,20 @@ static int ld9040_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL.\n"); - return -EFAULT; + return -EINVAL; } mutex_init(&lcd->lock); - ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); if (ret) { dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); return ret; } ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops); - if (IS_ERR(ld)) { - ret = PTR_ERR(ld); - goto out_free_regulator; - } + if (IS_ERR(ld)) + return PTR_ERR(ld); lcd->ld = ld; @@ -772,30 +749,28 @@ static int ld9040_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; ld9040_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "ld9040 panel driver has been probed.\n"); return 0; out_unregister_lcd: lcd_device_unregister(lcd->ld); -out_free_regulator: - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); return ret; } static int ld9040_remove(struct spi_device *spi) { - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); ld9040_power(lcd, FB_BLANK_POWERDOWN); backlight_device_unregister(lcd->bd); lcd_device_unregister(lcd->ld); - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); return 0; } @@ -803,8 +778,7 @@ static int ld9040_remove(struct spi_device *spi) #if defined(CONFIG_PM) static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); @@ -812,21 +786,16 @@ static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg) * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = ld9040_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return ld9040_power(lcd, FB_BLANK_POWERDOWN); } static int ld9040_resume(struct spi_device *spi) { - int ret = 0; - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); lcd->power = FB_BLANK_POWERDOWN; - ret = ld9040_power(lcd, FB_BLANK_UNBLANK); - - return ret; + return ld9040_power(lcd, FB_BLANK_UNBLANK); } #else #define ld9040_suspend NULL @@ -836,7 +805,7 @@ static int ld9040_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt. */ static void ld9040_shutdown(struct spi_device *spi) { - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); ld9040_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/lm3630_bl.c b/drivers/video/backlight/lm3630_bl.c index a6d637b5c68f..76a62e978fc3 100644 --- a/drivers/video/backlight/lm3630_bl.c +++ b/drivers/video/backlight/lm3630_bl.c @@ -320,7 +320,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip, backlight_device_register(name, pchip->dev, pchip, &lm3630_bank_a_ops, &props); if (IS_ERR(pchip->bled1)) - return -EIO; + return PTR_ERR(pchip->bled1); break; case BLED_2: props.brightness = pdata->init_brt_led2; @@ -329,7 +329,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip, backlight_device_register(name, pchip->dev, pchip, &lm3630_bank_b_ops, &props); if (IS_ERR(pchip->bled2)) - return -EIO; + return PTR_ERR(pchip->bled2); break; } return 0; diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c index 7ab2d2a04e41..053964da8dd3 100644 --- a/drivers/video/backlight/lm3639_bl.c +++ b/drivers/video/backlight/lm3639_bl.c @@ -350,14 +350,13 @@ static int lm3639_probe(struct i2c_client *client, &lm3639_bled_ops, &props); if (IS_ERR(pchip->bled)) { dev_err(&client->dev, "fail : backlight register\n"); - ret = -EIO; + ret = PTR_ERR(pchip->bled); goto err_out; } ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); if (ret < 0) { dev_err(&client->dev, "failed : add sysfs entries\n"); - ret = -EIO; goto err_bled_mode; } @@ -369,7 +368,6 @@ static int lm3639_probe(struct i2c_client *client, &client->dev, &pchip->cdev_flash); if (ret < 0) { dev_err(&client->dev, "fail : flash register\n"); - ret = -EIO; goto err_flash; } @@ -381,7 +379,6 @@ static int lm3639_probe(struct i2c_client *client, &client->dev, &pchip->cdev_torch); if (ret < 0) { dev_err(&client->dev, "fail : torch register\n"); - ret = -EIO; goto err_torch; } diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index 55819b384701..4eec47261cd3 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -180,7 +180,7 @@ static int lms283gf05_probe(struct spi_device *spi) st->spi = spi; st->ld = ld; - dev_set_drvdata(&spi->dev, st); + spi_set_drvdata(spi, st); /* kick in the LCD */ if (pdata) @@ -192,7 +192,7 @@ static int lms283gf05_probe(struct spi_device *spi) static int lms283gf05_remove(struct spi_device *spi) { - struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); + struct lms283gf05_state *st = spi_get_drvdata(spi); lcd_device_unregister(st->ld); diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c new file mode 100644 index 000000000000..b43882abefaf --- /dev/null +++ b/drivers/video/backlight/lms501kf03.c @@ -0,0 +1,441 @@ +/* + * lms501kf03 TFT LCD panel driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * 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. + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> + +#define COMMAND_ONLY 0x00 +#define DATA_ONLY 0x01 + +struct lms501kf03 { + struct device *dev; + struct spi_device *spi; + unsigned int power; + struct lcd_device *ld; + struct lcd_platform_data *lcd_pd; +}; + +static const unsigned char seq_password[] = { + 0xb9, 0xff, 0x83, 0x69, +}; + +static const unsigned char seq_power[] = { + 0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28, + 0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, +}; + +static const unsigned char seq_display[] = { + 0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, +}; + +static const unsigned char seq_rgb_if[] = { + 0xb3, 0x09, +}; + +static const unsigned char seq_display_inv[] = { + 0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06, +}; + +static const unsigned char seq_vcom[] = { + 0xb6, 0x4c, 0x2e, +}; + +static const unsigned char seq_gate[] = { + 0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13, + 0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75, + 0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07, +}; + +static const unsigned char seq_panel[] = { + 0xcc, 0x02, +}; + +static const unsigned char seq_col_mod[] = { + 0x3a, 0x77, +}; + +static const unsigned char seq_w_gamma[] = { + 0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a, + 0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04, + 0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16, + 0x18, 0x16, 0x17, 0x0d, 0x15, +}; + +static const unsigned char seq_rgb_gamma[] = { + 0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c, + 0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92, + 0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde, + 0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb, + 0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a, + 0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b, + 0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca, + 0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe, + 0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17, + 0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63, + 0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0, + 0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const unsigned char seq_up_dn[] = { + 0x36, 0x10, +}; + +static const unsigned char seq_sleep_in[] = { + 0x10, +}; + +static const unsigned char seq_sleep_out[] = { + 0x11, +}; + +static const unsigned char seq_display_on[] = { + 0x29, +}; + +static const unsigned char seq_display_off[] = { + 0x10, +}; + +static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data) +{ + u16 buf[1]; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr << 8) | data; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address, + unsigned char command) +{ + return lms501kf03_spi_write_byte(lcd, address, command); +} + +static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd, + const unsigned char *wbuf, + unsigned int len) +{ + int ret = 0, i = 0; + + while (i < len) { + if (i == 0) + ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]); + else + ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]); + if (ret) + break; + i += 1; + } + + return ret; +} + +static int lms501kf03_ldi_init(struct lms501kf03 *lcd) +{ + int ret, i; + static const unsigned char *init_seq[] = { + seq_password, + seq_power, + seq_display, + seq_rgb_if, + seq_display_inv, + seq_vcom, + seq_gate, + seq_panel, + seq_col_mod, + seq_w_gamma, + seq_rgb_gamma, + seq_sleep_out, + }; + + static const unsigned int size_seq[] = { + ARRAY_SIZE(seq_password), + ARRAY_SIZE(seq_power), + ARRAY_SIZE(seq_display), + ARRAY_SIZE(seq_rgb_if), + ARRAY_SIZE(seq_display_inv), + ARRAY_SIZE(seq_vcom), + ARRAY_SIZE(seq_gate), + ARRAY_SIZE(seq_panel), + ARRAY_SIZE(seq_col_mod), + ARRAY_SIZE(seq_w_gamma), + ARRAY_SIZE(seq_rgb_gamma), + ARRAY_SIZE(seq_sleep_out), + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = lms501kf03_panel_send_sequence(lcd, init_seq[i], + size_seq[i]); + if (ret) + break; + } + /* + * According to the datasheet, 120ms delay time is required. + * After sleep out sequence, command is blocked for 120ms. + * Thus, LDI should wait for 120ms. + */ + msleep(120); + + return ret; +} + +static int lms501kf03_ldi_enable(struct lms501kf03 *lcd) +{ + return lms501kf03_panel_send_sequence(lcd, seq_display_on, + ARRAY_SIZE(seq_display_on)); +} + +static int lms501kf03_ldi_disable(struct lms501kf03 *lcd) +{ + return lms501kf03_panel_send_sequence(lcd, seq_display_off, + ARRAY_SIZE(seq_display_off)); +} + +static int lms501kf03_power_is_on(int power) +{ + return (power) <= FB_BLANK_NORMAL; +} + +static int lms501kf03_power_on(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd; + + pd = lcd->lcd_pd; + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EINVAL; + } else { + pd->power_on(lcd->ld, 1); + msleep(pd->power_on_delay); + } + + if (!pd->reset) { + dev_err(lcd->dev, "reset is NULL.\n"); + return -EINVAL; + } else { + pd->reset(lcd->ld); + msleep(pd->reset_delay); + } + + ret = lms501kf03_ldi_init(lcd); + if (ret) { + dev_err(lcd->dev, "failed to initialize ldi.\n"); + return ret; + } + + ret = lms501kf03_ldi_enable(lcd); + if (ret) { + dev_err(lcd->dev, "failed to enable ldi.\n"); + return ret; + } + + return 0; +} + +static int lms501kf03_power_off(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd; + + pd = lcd->lcd_pd; + + ret = lms501kf03_ldi_disable(lcd); + if (ret) { + dev_err(lcd->dev, "lcd setting failed.\n"); + return -EIO; + } + + msleep(pd->power_off_delay); + + pd->power_on(lcd->ld, 0); + + return 0; +} + +static int lms501kf03_power(struct lms501kf03 *lcd, int power) +{ + int ret = 0; + + if (lms501kf03_power_is_on(power) && + !lms501kf03_power_is_on(lcd->power)) + ret = lms501kf03_power_on(lcd); + else if (!lms501kf03_power_is_on(power) && + lms501kf03_power_is_on(lcd->power)) + ret = lms501kf03_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int lms501kf03_get_power(struct lcd_device *ld) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int lms501kf03_set_power(struct lcd_device *ld, int power) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + return lms501kf03_power(lcd, power); +} + +static struct lcd_ops lms501kf03_lcd_ops = { + .get_power = lms501kf03_get_power, + .set_power = lms501kf03_set_power, +}; + +static int lms501kf03_probe(struct spi_device *spi) +{ + struct lms501kf03 *lcd = NULL; + struct lcd_device *ld = NULL; + int ret = 0; + + lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */ + spi->bits_per_word = 9; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed.\n"); + return ret; + } + + lcd->spi = spi; + lcd->dev = &spi->dev; + + lcd->lcd_pd = spi->dev.platform_data; + if (!lcd->lcd_pd) { + dev_err(&spi->dev, "platform data is NULL\n"); + return -EINVAL; + } + + ld = lcd_device_register("lms501kf03", &spi->dev, lcd, + &lms501kf03_lcd_ops); + if (IS_ERR(ld)) + return PTR_ERR(ld); + + lcd->ld = ld; + + if (!lcd->lcd_pd->lcd_enabled) { + /* + * if lcd panel was off from bootloader then + * current lcd status is powerdown and then + * it enables lcd panel. + */ + lcd->power = FB_BLANK_POWERDOWN; + + lms501kf03_power(lcd, FB_BLANK_UNBLANK); + } else { + lcd->power = FB_BLANK_UNBLANK; + } + + spi_set_drvdata(spi, lcd); + + dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n"); + + return 0; +} + +static int lms501kf03_remove(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + + return 0; +} + +#if defined(CONFIG_PM) + +static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + + /* + * when lcd panel is suspend, lcd panel becomes off + * regardless of status. + */ + return lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static int lms501kf03_resume(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lcd->power = FB_BLANK_POWERDOWN; + + return lms501kf03_power(lcd, FB_BLANK_UNBLANK); +} +#else +#define lms501kf03_suspend NULL +#define lms501kf03_resume NULL +#endif + +static void lms501kf03_shutdown(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver lms501kf03_driver = { + .driver = { + .name = "lms501kf03", + .owner = THIS_MODULE, + }, + .probe = lms501kf03_probe, + .remove = lms501kf03_remove, + .shutdown = lms501kf03_shutdown, + .suspend = lms501kf03_suspend, + .resume = lms501kf03_resume, +}; + +module_spi_driver(lms501kf03_driver); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("lms501kf03 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c new file mode 100644 index 000000000000..fbf17bcd855c --- /dev/null +++ b/drivers/video/backlight/lp8788_bl.c @@ -0,0 +1,350 @@ +/* + * TI LP8788 MFD - backlight driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/mfd/lp8788.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +/* register address */ +#define LP8788_BL_CONFIG 0x96 +#define LP8788_BL_BRIGHTNESS 0x97 +#define LP8788_BL_RAMP 0x98 + +/* mask/shift bits */ +#define LP8788_BL_EN BIT(0) /* Addr 96h */ +#define LP8788_BL_PWM_EN BIT(5) +#define LP8788_BL_FULLSCALE_S 2 +#define LP8788_BL_DIM_MODE_S 1 +#define LP8788_BL_PWM_POLARITY_S 6 +#define LP8788_BL_RAMP_RISE_S 4 /* Addr 98h */ + +#define BUF_SIZE 20 +#define MAX_BRIGHTNESS 127 +#define DEFAULT_BL_NAME "lcd-backlight" + +struct lp8788_bl_config { + enum lp8788_bl_ctrl_mode bl_mode; + enum lp8788_bl_dim_mode dim_mode; + enum lp8788_bl_full_scale_current full_scale; + enum lp8788_bl_ramp_step rise_time; + enum lp8788_bl_ramp_step fall_time; + enum lp8788_bl_pwm_polarity pwm_pol; +}; + +struct lp8788_bl { + struct lp8788 *lp; + struct backlight_device *bl_dev; + struct lp8788_backlight_platform_data *pdata; + enum lp8788_bl_ctrl_mode mode; + struct pwm_device *pwm; +}; + +struct lp8788_bl_config default_bl_config = { + .bl_mode = LP8788_BL_REGISTER_ONLY, + .dim_mode = LP8788_DIM_EXPONENTIAL, + .full_scale = LP8788_FULLSCALE_1900uA, + .rise_time = LP8788_RAMP_8192us, + .fall_time = LP8788_RAMP_8192us, + .pwm_pol = LP8788_PWM_ACTIVE_HIGH, +}; + +static inline bool is_brightness_ctrl_by_pwm(enum lp8788_bl_ctrl_mode mode) +{ + return (mode == LP8788_BL_COMB_PWM_BASED); +} + +static inline bool is_brightness_ctrl_by_register(enum lp8788_bl_ctrl_mode mode) +{ + return (mode == LP8788_BL_REGISTER_ONLY || + mode == LP8788_BL_COMB_REGISTER_BASED); +} + +static int lp8788_backlight_configure(struct lp8788_bl *bl) +{ + struct lp8788_backlight_platform_data *pdata = bl->pdata; + struct lp8788_bl_config *cfg = &default_bl_config; + int ret; + u8 val; + + /* update chip configuration if platform data exists, + otherwise use the default settings */ + if (pdata) { + cfg->bl_mode = pdata->bl_mode; + cfg->dim_mode = pdata->dim_mode; + cfg->full_scale = pdata->full_scale; + cfg->rise_time = pdata->rise_time; + cfg->fall_time = pdata->fall_time; + cfg->pwm_pol = pdata->pwm_pol; + } + + /* brightness ramp up/down */ + val = (cfg->rise_time << LP8788_BL_RAMP_RISE_S) | cfg->fall_time; + ret = lp8788_write_byte(bl->lp, LP8788_BL_RAMP, val); + if (ret) { + /* no I2C needed in case of pwm control mode */ + if (is_brightness_ctrl_by_pwm(cfg->bl_mode)) + goto no_err; + else + return ret; + } + + /* fullscale current setting */ + val = (cfg->full_scale << LP8788_BL_FULLSCALE_S) | + (cfg->dim_mode << LP8788_BL_DIM_MODE_S); + + /* brightness control mode */ + switch (cfg->bl_mode) { + case LP8788_BL_REGISTER_ONLY: + val |= LP8788_BL_EN; + break; + case LP8788_BL_COMB_PWM_BASED: + case LP8788_BL_COMB_REGISTER_BASED: + val |= LP8788_BL_EN | LP8788_BL_PWM_EN | + (cfg->pwm_pol << LP8788_BL_PWM_POLARITY_S); + break; + default: + dev_err(bl->lp->dev, "invalid mode: %d\n", cfg->bl_mode); + return -EINVAL; + } + + bl->mode = cfg->bl_mode; + + return lp8788_write_byte(bl->lp, LP8788_BL_CONFIG, val); + +no_err: + return 0; +} + +static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br) +{ + unsigned int period; + unsigned int duty; + struct device *dev; + struct pwm_device *pwm; + + if (!bl->pdata) + return; + + period = bl->pdata->period_ns; + duty = br * period / max_br; + dev = bl->lp->dev; + + /* request pwm device with the consumer name */ + if (!bl->pwm) { + pwm = devm_pwm_get(dev, LP8788_DEV_BACKLIGHT); + if (IS_ERR(pwm)) { + dev_err(dev, "can not get pwm device\n"); + return; + } + + bl->pwm = pwm; + } + + pwm_config(bl->pwm, duty, period); + if (duty) + pwm_enable(bl->pwm); + else + pwm_disable(bl->pwm); +} + +static int lp8788_bl_update_status(struct backlight_device *bl_dev) +{ + struct lp8788_bl *bl = bl_get_data(bl_dev); + enum lp8788_bl_ctrl_mode mode = bl->mode; + + if (bl_dev->props.state & BL_CORE_SUSPENDED) + bl_dev->props.brightness = 0; + + if (is_brightness_ctrl_by_pwm(mode)) { + int brt = bl_dev->props.brightness; + int max = bl_dev->props.max_brightness; + + lp8788_pwm_ctrl(bl, brt, max); + } else if (is_brightness_ctrl_by_register(mode)) { + u8 brt = bl_dev->props.brightness; + + lp8788_write_byte(bl->lp, LP8788_BL_BRIGHTNESS, brt); + } + + return 0; +} + +static int lp8788_bl_get_brightness(struct backlight_device *bl_dev) +{ + struct lp8788_bl *bl = bl_get_data(bl_dev); + enum lp8788_bl_ctrl_mode mode = bl->mode; + + if (is_brightness_ctrl_by_register(mode)) { + u8 val = 0; + + lp8788_read_byte(bl->lp, LP8788_BL_BRIGHTNESS, &val); + bl_dev->props.brightness = val; + } + + return bl_dev->props.brightness; +} + +static const struct backlight_ops lp8788_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lp8788_bl_update_status, + .get_brightness = lp8788_bl_get_brightness, +}; + +static int lp8788_backlight_register(struct lp8788_bl *bl) +{ + struct backlight_device *bl_dev; + struct backlight_properties props; + struct lp8788_backlight_platform_data *pdata = bl->pdata; + int init_brt; + char *name; + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + + /* initial brightness */ + if (pdata) + init_brt = min_t(int, pdata->initial_brightness, + props.max_brightness); + else + init_brt = 0; + + props.brightness = init_brt; + + /* backlight device name */ + if (!pdata || !pdata->name) + name = DEFAULT_BL_NAME; + else + name = pdata->name; + + bl_dev = backlight_device_register(name, bl->lp->dev, bl, + &lp8788_bl_ops, &props); + if (IS_ERR(bl_dev)) + return PTR_ERR(bl_dev); + + bl->bl_dev = bl_dev; + + return 0; +} + +static void lp8788_backlight_unregister(struct lp8788_bl *bl) +{ + struct backlight_device *bl_dev = bl->bl_dev; + + if (bl_dev) + backlight_device_unregister(bl_dev); +} + +static ssize_t lp8788_get_bl_ctl_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp8788_bl *bl = dev_get_drvdata(dev); + enum lp8788_bl_ctrl_mode mode = bl->mode; + char *strmode; + + if (is_brightness_ctrl_by_pwm(mode)) + strmode = "pwm based"; + else if (is_brightness_ctrl_by_register(mode)) + strmode = "register based"; + else + strmode = "invalid mode"; + + return scnprintf(buf, BUF_SIZE, "%s\n", strmode); +} + +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp8788_get_bl_ctl_mode, NULL); + +static struct attribute *lp8788_attributes[] = { + &dev_attr_bl_ctl_mode.attr, + NULL, +}; + +static const struct attribute_group lp8788_attr_group = { + .attrs = lp8788_attributes, +}; + +static int lp8788_backlight_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct lp8788_bl *bl; + int ret; + + bl = devm_kzalloc(lp->dev, sizeof(struct lp8788_bl), GFP_KERNEL); + if (!bl) + return -ENOMEM; + + bl->lp = lp; + if (lp->pdata) + bl->pdata = lp->pdata->bl_pdata; + + platform_set_drvdata(pdev, bl); + + ret = lp8788_backlight_configure(bl); + if (ret) { + dev_err(lp->dev, "backlight config err: %d\n", ret); + goto err_dev; + } + + ret = lp8788_backlight_register(bl); + if (ret) { + dev_err(lp->dev, "register backlight err: %d\n", ret); + goto err_dev; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group); + if (ret) { + dev_err(lp->dev, "register sysfs err: %d\n", ret); + goto err_sysfs; + } + + backlight_update_status(bl->bl_dev); + + return 0; + +err_sysfs: + lp8788_backlight_unregister(bl); +err_dev: + return ret; +} + +static int lp8788_backlight_remove(struct platform_device *pdev) +{ + struct lp8788_bl *bl = platform_get_drvdata(pdev); + struct backlight_device *bl_dev = bl->bl_dev; + + bl_dev->props.brightness = 0; + backlight_update_status(bl_dev); + sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group); + lp8788_backlight_unregister(bl); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver lp8788_bl_driver = { + .probe = lp8788_backlight_probe, + .remove = lp8788_backlight_remove, + .driver = { + .name = LP8788_DEV_BACKLIGHT, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(lp8788_bl_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-backlight"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index 226d813edf01..c0b4b8f2de98 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -252,7 +252,7 @@ static int ltv350qv_probe(struct spi_device *spi) if (ret) goto out_unregister; - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); return 0; @@ -263,7 +263,7 @@ out_unregister: static int ltv350qv_remove(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); ltv350qv_power(lcd, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->ld); @@ -274,14 +274,14 @@ static int ltv350qv_remove(struct spi_device *spi) #ifdef CONFIG_PM static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); } static int ltv350qv_resume(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); return ltv350qv_power(lcd, FB_BLANK_UNBLANK); } @@ -293,7 +293,7 @@ static int ltv350qv_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt */ static void ltv350qv_shutdown(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); ltv350qv_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index af31c269baa6..627110163067 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -77,7 +77,7 @@ static void omapbl_blank(struct omap_backlight *bl, int mode) static int omapbl_suspend(struct platform_device *pdev, pm_message_t state) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, FB_BLANK_POWERDOWN); return 0; @@ -86,7 +86,7 @@ static int omapbl_suspend(struct platform_device *pdev, pm_message_t state) static int omapbl_resume(struct platform_device *pdev) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, bl->powermode); return 0; @@ -98,7 +98,7 @@ static int omapbl_resume(struct platform_device *pdev) static int omapbl_set_power(struct backlight_device *dev, int state) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, state); bl->powermode = state; @@ -108,7 +108,7 @@ static int omapbl_set_power(struct backlight_device *dev, int state) static int omapbl_update_status(struct backlight_device *dev) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); if (bl->current_intensity != dev->props.brightness) { if (bl->powermode == FB_BLANK_UNBLANK) @@ -124,7 +124,7 @@ static int omapbl_update_status(struct backlight_device *dev) static int omapbl_get_intensity(struct backlight_device *dev) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); return bl->current_intensity; } diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index a71ef4a79ec4..fa00304a63d8 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -37,7 +37,7 @@ struct pwm_bl_data { static int pwm_backlight_update_status(struct backlight_device *bl) { - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); int brightness = bl->props.brightness; int max = bl->props.max_brightness; @@ -82,7 +82,7 @@ static int pwm_backlight_get_brightness(struct backlight_device *bl) static int pwm_backlight_check_fb(struct backlight_device *bl, struct fb_info *info) { - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); return !pb->check_fb || pb->check_fb(pb->dev, info); } @@ -264,7 +264,7 @@ err_alloc: static int pwm_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); backlight_device_unregister(bl); pwm_config(pb->pwm, 0, pb->period); @@ -278,7 +278,7 @@ static int pwm_backlight_remove(struct platform_device *pdev) static int pwm_backlight_suspend(struct device *dev) { struct backlight_device *bl = dev_get_drvdata(dev); - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); if (pb->notify) pb->notify(pb->dev, 0); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index 3e1c1135f6df..9c2677f0ef7d 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -9,28 +9,19 @@ * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <linux/wait.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> -#include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/lcd.h> -#include <linux/backlight.h> #include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #include "s6e63m0_gamma.h" @@ -43,8 +34,6 @@ #define MIN_BRIGHTNESS 0 #define MAX_BRIGHTNESS 10 -#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) - struct s6e63m0 { struct device *dev; struct spi_device *spi; @@ -57,7 +46,7 @@ struct s6e63m0 { struct lcd_platform_data *lcd_pd; }; -static const unsigned short SEQ_PANEL_CONDITION_SET[] = { +static const unsigned short seq_panel_condition_set[] = { 0xF8, 0x01, DATA_ONLY, 0x27, DATA_ONLY, 0x27, @@ -76,7 +65,7 @@ static const unsigned short SEQ_PANEL_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { +static const unsigned short seq_display_condition_set[] = { 0xf2, 0x02, DATA_ONLY, 0x03, DATA_ONLY, 0x1c, @@ -90,7 +79,7 @@ static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_GAMMA_SETTING[] = { +static const unsigned short seq_gamma_setting[] = { 0xfa, 0x00, DATA_ONLY, 0x18, DATA_ONLY, 0x08, @@ -119,7 +108,7 @@ static const unsigned short SEQ_GAMMA_SETTING[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_ETC_CONDITION_SET[] = { +static const unsigned short seq_etc_condition_set[] = { 0xf6, 0x00, DATA_ONLY, 0x8c, DATA_ONLY, 0x07, @@ -318,47 +307,47 @@ static const unsigned short SEQ_ETC_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_ACL_ON[] = { +static const unsigned short seq_acl_on[] = { /* ACL on */ 0xc0, 0x01, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ACL_OFF[] = { +static const unsigned short seq_acl_off[] = { /* ACL off */ 0xc0, 0x00, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ELVSS_ON[] = { +static const unsigned short seq_elvss_on[] = { /* ELVSS on */ 0xb1, 0x0b, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ELVSS_OFF[] = { +static const unsigned short seq_elvss_off[] = { /* ELVSS off */ 0xb1, 0x0a, ENDDEF, 0x0000 }; -static const unsigned short SEQ_STAND_BY_OFF[] = { +static const unsigned short seq_stand_by_off[] = { 0x11, COMMAND_ONLY, ENDDEF, 0x0000 }; -static const unsigned short SEQ_STAND_BY_ON[] = { +static const unsigned short seq_stand_by_on[] = { 0x10, COMMAND_ONLY, ENDDEF, 0x0000 }; -static const unsigned short SEQ_DISPLAY_ON[] = { +static const unsigned short seq_display_on[] = { 0x29, COMMAND_ONLY, ENDDEF, 0x0000 @@ -406,8 +395,9 @@ static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd, ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - udelay(wbuf[i+1]*1000); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -457,12 +447,12 @@ static int s6e63m0_ldi_init(struct s6e63m0 *lcd) { int ret, i; const unsigned short *init_seq[] = { - SEQ_PANEL_CONDITION_SET, - SEQ_DISPLAY_CONDITION_SET, - SEQ_GAMMA_SETTING, - SEQ_ETC_CONDITION_SET, - SEQ_ACL_ON, - SEQ_ELVSS_ON, + seq_panel_condition_set, + seq_display_condition_set, + seq_gamma_setting, + seq_etc_condition_set, + seq_acl_on, + seq_elvss_on, }; for (i = 0; i < ARRAY_SIZE(init_seq); i++) { @@ -478,8 +468,8 @@ static int s6e63m0_ldi_enable(struct s6e63m0 *lcd) { int ret = 0, i; const unsigned short *enable_seq[] = { - SEQ_STAND_BY_OFF, - SEQ_DISPLAY_ON, + seq_stand_by_off, + seq_display_on, }; for (i = 0; i < ARRAY_SIZE(enable_seq); i++) { @@ -495,43 +485,39 @@ static int s6e63m0_ldi_disable(struct s6e63m0 *lcd) { int ret; - ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON); + ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on); return ret; } +static int s6e63m0_power_is_on(int power) +{ + return power <= FB_BLANK_NORMAL; +} + static int s6e63m0_power_on(struct s6e63m0 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; - struct backlight_device *bd = NULL; + struct lcd_platform_data *pd; + struct backlight_device *bd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } - bd = lcd->bd; - if (!bd) { - dev_err(lcd->dev, "backlight device is NULL.\n"); - return -EFAULT; - } if (!pd->power_on) { dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->power_on(lcd->ld, 1); - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); } if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = s6e63m0_ldi_init(lcd); @@ -558,14 +544,10 @@ static int s6e63m0_power_on(struct s6e63m0 *lcd) static int s6e63m0_power_off(struct s6e63m0 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } ret = s6e63m0_ldi_disable(lcd); if (ret) { @@ -573,13 +555,9 @@ static int s6e63m0_power_off(struct s6e63m0 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); - if (!pd->power_on) { - dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; - } else - pd->power_on(lcd->ld, 0); + pd->power_on(lcd->ld, 0); return 0; } @@ -588,9 +566,9 @@ static int s6e63m0_power(struct s6e63m0 *lcd, int power) { int ret = 0; - if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power)) ret = s6e63m0_power_on(lcd); - else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power)) ret = s6e63m0_power_off(lcd); if (!ret) @@ -760,7 +738,7 @@ static int s6e63m0_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL.\n"); - return -EFAULT; + return -EINVAL; } ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops); @@ -788,7 +766,7 @@ static int s6e63m0_probe(struct spi_device *spi) * know that. */ lcd->gamma_table_count = - sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int)); + sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *)); ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode); if (ret < 0) @@ -811,10 +789,11 @@ static int s6e63m0_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; s6e63m0_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n"); @@ -827,7 +806,7 @@ out_lcd_unregister: static int s6e63m0_remove(struct spi_device *spi) { - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); device_remove_file(&spi->dev, &dev_attr_gamma_table); @@ -839,44 +818,26 @@ static int s6e63m0_remove(struct spi_device *spi) } #if defined(CONFIG_PM) -static unsigned int before_power; - static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); - before_power = lcd->power; - /* * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return s6e63m0_power(lcd, FB_BLANK_POWERDOWN); } static int s6e63m0_resume(struct spi_device *spi) { - int ret = 0; - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); - - /* - * after suspended, if lcd panel status is FB_BLANK_UNBLANK - * (at that time, before_power is FB_BLANK_UNBLANK) then - * it changes that status to FB_BLANK_POWERDOWN to get lcd on. - */ - if (before_power == FB_BLANK_UNBLANK) - lcd->power = FB_BLANK_POWERDOWN; + struct s6e63m0 *lcd = spi_get_drvdata(spi); - dev_dbg(&spi->dev, "before_power = %d\n", before_power); + lcd->power = FB_BLANK_POWERDOWN; - ret = s6e63m0_power(lcd, before_power); - - return ret; + return s6e63m0_power(lcd, FB_BLANK_UNBLANK); } #else #define s6e63m0_suspend NULL @@ -886,7 +847,7 @@ static int s6e63m0_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt. */ static void s6e63m0_shutdown(struct spi_device *spi) { - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index ad2325f3d652..00162085eec0 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -390,7 +390,7 @@ static int tdo24m_probe(struct spi_device *spi) if (IS_ERR(lcd->lcd_dev)) return PTR_ERR(lcd->lcd_dev); - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); err = tdo24m_power(lcd, FB_BLANK_UNBLANK); if (err) goto out_unregister; @@ -404,7 +404,7 @@ out_unregister: static int tdo24m_remove(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); tdo24m_power(lcd, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->lcd_dev); @@ -415,14 +415,14 @@ static int tdo24m_remove(struct spi_device *spi) #ifdef CONFIG_PM static int tdo24m_suspend(struct spi_device *spi, pm_message_t state) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); return tdo24m_power(lcd, FB_BLANK_POWERDOWN); } static int tdo24m_resume(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); return tdo24m_power(lcd, FB_BLANK_UNBLANK); } @@ -434,7 +434,7 @@ static int tdo24m_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt */ static void tdo24m_shutdown(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); tdo24m_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 588682cc1614..2326fa810c59 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -54,7 +54,7 @@ static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness) static int tosa_bl_update_status(struct backlight_device *dev) { struct backlight_properties *props = &dev->props; - struct tosa_bl_data *data = dev_get_drvdata(&dev->dev); + struct tosa_bl_data *data = bl_get_data(dev); int power = max(props->power, props->fb_blank); int brightness = props->brightness; diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 96bae941585a..666fe2593ea4 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -193,7 +193,7 @@ static int tosa_lcd_probe(struct spi_device *spi) return ret; data->spi = spi; - dev_set_drvdata(&spi->dev, data); + spi_set_drvdata(spi, data); ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON, GPIOF_OUT_INIT_LOW, "tg #pwr"); @@ -220,13 +220,13 @@ static int tosa_lcd_probe(struct spi_device *spi) err_register: tosa_lcd_tg_off(data); err_gpio_tg: - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return ret; } static int tosa_lcd_remove(struct spi_device *spi) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); lcd_device_unregister(data->lcd); @@ -235,7 +235,7 @@ static int tosa_lcd_remove(struct spi_device *spi) tosa_lcd_tg_off(data); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return 0; } @@ -243,7 +243,7 @@ static int tosa_lcd_remove(struct spi_device *spi) #ifdef CONFIG_PM static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); tosa_lcd_tg_off(data); @@ -252,7 +252,7 @@ static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) static int tosa_lcd_resume(struct spi_device *spi) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); tosa_lcd_tg_init(data); if (POWER_IS_ON(data->lcd_power)) diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index 45e81b4cf8b4..84d582f591dc 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -208,12 +208,11 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd, #ifdef CONFIG_PM static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state) { - return ili9320_suspend(dev_get_drvdata(&spi->dev), state); + return ili9320_suspend(spi_get_drvdata(spi), state); } - static int vgg2432a4_resume(struct spi_device *spi) { - return ili9320_resume(dev_get_drvdata(&spi->dev)); + return ili9320_resume(spi_get_drvdata(spi)); } #else #define vgg2432a4_suspend NULL @@ -242,12 +241,12 @@ static int vgg2432a4_probe(struct spi_device *spi) static int vgg2432a4_remove(struct spi_device *spi) { - return ili9320_remove(dev_get_drvdata(&spi->dev)); + return ili9320_remove(spi_get_drvdata(spi)); } static void vgg2432a4_shutdown(struct spi_device *spi) { - ili9320_shutdown(dev_get_drvdata(&spi->dev)); + ili9320_shutdown(spi_get_drvdata(spi)); } static struct spi_driver vgg2432a4_driver = { diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 2aef9cac4d18..7d97d3fe9e89 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1269,8 +1269,16 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, if (!height || !width) return; - if (sy < vc->vc_top && vc->vc_top == logo_lines) + if (sy < vc->vc_top && vc->vc_top == logo_lines) { vc->vc_top = 0; + /* + * If the font dimensions are not an integral of the display + * dimensions then the ops->clear below won't end up clearing + * the margins. Call clear_margins here in case the logo + * bitmap stretched into the margin area. + */ + fbcon_clear_margins(vc, 0); + } /* Split blits that cross physical y_wrap boundary */ diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 57886787ead0..e78d9f2233b8 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -518,6 +518,9 @@ static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw) cyber2000_grphw(0xb9, 0x00, cfb); spin_unlock(&cfb->reg_b0_lock); + /* wait (for the PLL?) to avoid palette corruption at higher clocks */ + msleep(1000); + cfb->ramdac_ctrl = hw->ramdac; cyber2000fb_write_ramdac_ctrl(cfb); diff --git a/drivers/video/goldfishfb.c b/drivers/video/goldfishfb.c new file mode 100644 index 000000000000..f7f5c296e104 --- /dev/null +++ b/drivers/video/goldfishfb.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2012 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +enum { + FB_GET_WIDTH = 0x00, + FB_GET_HEIGHT = 0x04, + FB_INT_STATUS = 0x08, + FB_INT_ENABLE = 0x0c, + FB_SET_BASE = 0x10, + FB_SET_ROTATION = 0x14, + FB_SET_BLANK = 0x18, + FB_GET_PHYS_WIDTH = 0x1c, + FB_GET_PHYS_HEIGHT = 0x20, + + FB_INT_VSYNC = 1U << 0, + FB_INT_BASE_UPDATE_DONE = 1U << 1 +}; + +struct goldfish_fb { + void __iomem *reg_base; + int irq; + spinlock_t lock; + wait_queue_head_t wait; + int base_update_count; + int rotation; + struct fb_info fb; + u32 cmap[16]; +}; + +static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id) +{ + unsigned long irq_flags; + struct goldfish_fb *fb = dev_id; + u32 status; + + spin_lock_irqsave(&fb->lock, irq_flags); + status = readl(fb->reg_base + FB_INT_STATUS); + if (status & FB_INT_BASE_UPDATE_DONE) { + fb->base_update_count++; + wake_up(&fb->wait); + } + spin_unlock_irqrestore(&fb->lock, irq_flags); + return status ? IRQ_HANDLED : IRQ_NONE; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +static int +goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + if (regno < 16) { + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + return 0; + } else { + return 1; + } +} + +static int goldfish_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if ((var->rotate & 1) != (info->var.rotate & 1)) { + if ((var->xres != info->var.yres) || + (var->yres != info->var.xres) || + (var->xres_virtual != info->var.yres) || + (var->yres_virtual > info->var.xres * 2) || + (var->yres_virtual < info->var.xres)) { + return -EINVAL; + } + } else { + if ((var->xres != info->var.xres) || + (var->yres != info->var.yres) || + (var->xres_virtual != info->var.xres) || + (var->yres_virtual > info->var.yres * 2) || + (var->yres_virtual < info->var.yres)) { + return -EINVAL; + } + } + if ((var->xoffset != info->var.xoffset) || + (var->bits_per_pixel != info->var.bits_per_pixel) || + (var->grayscale != info->var.grayscale)) { + return -EINVAL; + } + return 0; +} + +static int goldfish_fb_set_par(struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + if (fb->rotation != fb->fb.var.rotate) { + info->fix.line_length = info->var.xres * 2; + fb->rotation = fb->fb.var.rotate; + writel(fb->rotation, fb->reg_base + FB_SET_ROTATION); + } + return 0; +} + + +static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned long irq_flags; + int base_update_count; + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + spin_lock_irqsave(&fb->lock, irq_flags); + base_update_count = fb->base_update_count; + writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, + fb->reg_base + FB_SET_BASE); + spin_unlock_irqrestore(&fb->lock, irq_flags); + wait_event_timeout(fb->wait, + fb->base_update_count != base_update_count, HZ / 15); + if (fb->base_update_count == base_update_count) + pr_err("goldfish_fb_pan_display: timeout wating for base update\n"); + return 0; +} + +static int goldfish_fb_blank(int blank, struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + switch (blank) { + case FB_BLANK_NORMAL: + writel(1, fb->reg_base + FB_SET_BLANK); + break; + case FB_BLANK_UNBLANK: + writel(0, fb->reg_base + FB_SET_BLANK); + break; + } + return 0; +} + +static struct fb_ops goldfish_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = goldfish_fb_check_var, + .fb_set_par = goldfish_fb_set_par, + .fb_setcolreg = goldfish_fb_setcolreg, + .fb_pan_display = goldfish_fb_pan_display, + .fb_blank = goldfish_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + + +static int goldfish_fb_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_fb *fb; + size_t framesize; + u32 width, height; + dma_addr_t fbpaddr; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (fb == NULL) { + ret = -ENOMEM; + goto err_fb_alloc_failed; + } + spin_lock_init(&fb->lock); + init_waitqueue_head(&fb->wait); + platform_set_drvdata(pdev, fb); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto err_no_io_base; + } + fb->reg_base = ioremap(r->start, PAGE_SIZE); + if (fb->reg_base == NULL) { + ret = -ENOMEM; + goto err_no_io_base; + } + + fb->irq = platform_get_irq(pdev, 0); + if (fb->irq <= 0) { + ret = -ENODEV; + goto err_no_irq; + } + + width = readl(fb->reg_base + FB_GET_WIDTH); + height = readl(fb->reg_base + FB_GET_HEIGHT); + + fb->fb.fbops = &goldfish_fb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + fb->fb.fix.line_length = width * 2; + fb->fb.fix.accel = FB_ACCEL_NONE; + fb->fb.fix.ypanstep = 1; + + fb->fb.var.xres = width; + fb->fb.var.yres = height; + fb->fb.var.xres_virtual = width; + fb->fb.var.yres_virtual = height * 2; + fb->fb.var.bits_per_pixel = 16; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT); + fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH); + fb->fb.var.pixclock = 10000; + + fb->fb.var.red.offset = 11; + fb->fb.var.red.length = 5; + fb->fb.var.green.offset = 5; + fb->fb.var.green.length = 6; + fb->fb.var.blue.offset = 0; + fb->fb.var.blue.length = 5; + + framesize = width * height * 2 * 2; + fb->fb.screen_base = dma_alloc_coherent(&pdev->dev, framesize, + &fbpaddr, GFP_KERNEL); + pr_debug("allocating frame buffer %d * %d, got %p\n", + width, height, fb->fb.screen_base); + if (fb->fb.screen_base == 0) { + ret = -ENOMEM; + goto err_alloc_screen_base_failed; + } + fb->fb.fix.smem_start = fbpaddr; + fb->fb.fix.smem_len = framesize; + + ret = fb_set_var(&fb->fb, &fb->fb.var); + if (ret) + goto err_fb_set_var_failed; + + ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, + pdev->name, fb); + if (ret) + goto err_request_irq_failed; + + writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE); + goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */ + + ret = register_framebuffer(&fb->fb); + if (ret) + goto err_register_framebuffer_failed; + return 0; + +err_register_framebuffer_failed: + free_irq(fb->irq, fb); +err_request_irq_failed: +err_fb_set_var_failed: + dma_free_coherent(&pdev->dev, framesize, + fb->fb.screen_base, fb->fb.fix.smem_start); +err_alloc_screen_base_failed: +err_no_irq: + iounmap(fb->reg_base); +err_no_io_base: + kfree(fb); +err_fb_alloc_failed: + return ret; +} + +static int goldfish_fb_remove(struct platform_device *pdev) +{ + size_t framesize; + struct goldfish_fb *fb = platform_get_drvdata(pdev); + + framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2; + unregister_framebuffer(&fb->fb); + free_irq(fb->irq, fb); + + dma_free_coherent(&pdev->dev, framesize, fb->fb.screen_base, + fb->fb.fix.smem_start); + iounmap(fb->reg_base); + return 0; +} + + +static struct platform_driver goldfish_fb_driver = { + .probe = goldfish_fb_probe, + .remove = goldfish_fb_remove, + .driver = { + .name = "goldfish_fb" + } +}; + +module_platform_driver(goldfish_fb_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig new file mode 100644 index 000000000000..e9ea39e13722 --- /dev/null +++ b/drivers/video/mmp/Kconfig @@ -0,0 +1,11 @@ +menuconfig MMP_DISP + tristate "Marvell MMP Display Subsystem support" + depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988 + help + Marvell Display Subsystem support. + +if MMP_DISP +source "drivers/video/mmp/hw/Kconfig" +source "drivers/video/mmp/panel/Kconfig" +source "drivers/video/mmp/fb/Kconfig" +endif diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile new file mode 100644 index 000000000000..a014cb358bf8 --- /dev/null +++ b/drivers/video/mmp/Makefile @@ -0,0 +1 @@ +obj-y += core.o hw/ panel/ fb/ diff --git a/drivers/video/mmp/core.c b/drivers/video/mmp/core.c new file mode 100644 index 000000000000..9ed83419038b --- /dev/null +++ b/drivers/video/mmp/core.c @@ -0,0 +1,258 @@ +/* + * linux/drivers/video/mmp/common.c + * This driver is a common framework for Marvell Display Controller + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/export.h> +#include <video/mmp_disp.h> + +static struct mmp_overlay *path_get_overlay(struct mmp_path *path, + int overlay_id) +{ + if (path && overlay_id < path->overlay_num) + return &path->overlays[overlay_id]; + return 0; +} + +static int path_check_status(struct mmp_path *path) +{ + int i; + for (i = 0; i < path->overlay_num; i++) + if (path->overlays[i].status) + return 1; + + return 0; +} + +/* + * Get modelist write pointer of modelist. + * It also returns modelist number + * this function fetches modelist from phy/panel: + * for HDMI/parallel or dsi to hdmi cases, get from phy + * or get from panel + */ +static int path_get_modelist(struct mmp_path *path, + struct mmp_mode **modelist) +{ + BUG_ON(!path || !modelist); + + if (path->panel && path->panel->get_modelist) + return path->panel->get_modelist(path->panel, modelist); + + return 0; +} + +/* + * panel list is used to pair panel/path when path/panel registered + * path list is used for both buffer driver and platdriver + * plat driver do path register/unregister + * panel driver do panel register/unregister + * buffer driver get registered path + */ +static LIST_HEAD(panel_list); +static LIST_HEAD(path_list); +static DEFINE_MUTEX(disp_lock); + +/* + * mmp_register_panel - register panel to panel_list and connect to path + * @p: panel to be registered + * + * this function provides interface for panel drivers to register panel + * to panel_list and connect to path which matchs panel->plat_path_name. + * no error returns when no matching path is found as path register after + * panel register is permitted. + */ +void mmp_register_panel(struct mmp_panel *panel) +{ + struct mmp_path *path; + + mutex_lock(&disp_lock); + + /* add */ + list_add_tail(&panel->node, &panel_list); + + /* try to register to path */ + list_for_each_entry(path, &path_list, node) { + if (!strcmp(panel->plat_path_name, path->name)) { + dev_info(panel->dev, "connect to path %s\n", + path->name); + path->panel = panel; + break; + } + } + + mutex_unlock(&disp_lock); +} +EXPORT_SYMBOL_GPL(mmp_register_panel); + +/* + * mmp_unregister_panel - unregister panel from panel_list and disconnect + * @p: panel to be unregistered + * + * this function provides interface for panel drivers to unregister panel + * from panel_list and disconnect from path. + */ +void mmp_unregister_panel(struct mmp_panel *panel) +{ + struct mmp_path *path; + + mutex_lock(&disp_lock); + list_del(&panel->node); + + list_for_each_entry(path, &path_list, node) { + if (path->panel && path->panel == panel) { + dev_info(panel->dev, "disconnect from path %s\n", + path->name); + path->panel = NULL; + break; + } + } + mutex_unlock(&disp_lock); +} +EXPORT_SYMBOL_GPL(mmp_unregister_panel); + +/* + * mmp_get_path - get path by name + * @p: path name + * + * this function checks path name in path_list and return matching path + * return NULL if no matching path + */ +struct mmp_path *mmp_get_path(const char *name) +{ + struct mmp_path *path; + int found = 0; + + mutex_lock(&disp_lock); + list_for_each_entry(path, &path_list, node) { + if (!strcmp(name, path->name)) { + found = 1; + break; + } + } + mutex_unlock(&disp_lock); + + return found ? path : NULL; +} +EXPORT_SYMBOL_GPL(mmp_get_path); + +/* + * mmp_register_path - init and register path by path_info + * @p: path info provided by display controller + * + * this function init by path info and register path to path_list + * this function also try to connect path with panel by name + */ +struct mmp_path *mmp_register_path(struct mmp_path_info *info) +{ + int i; + size_t size; + struct mmp_path *path = NULL; + struct mmp_panel *panel; + + size = sizeof(struct mmp_path) + + sizeof(struct mmp_overlay) * info->overlay_num; + path = kzalloc(size, GFP_KERNEL); + if (!path) + goto failed; + + /* path set */ + mutex_init(&path->access_ok); + path->dev = info->dev; + path->id = info->id; + path->name = info->name; + path->output_type = info->output_type; + path->overlay_num = info->overlay_num; + path->plat_data = info->plat_data; + path->ops.set_mode = info->set_mode; + + mutex_lock(&disp_lock); + /* get panel */ + list_for_each_entry(panel, &panel_list, node) { + if (!strcmp(info->name, panel->plat_path_name)) { + dev_info(path->dev, "get panel %s\n", panel->name); + path->panel = panel; + break; + } + } + + dev_info(path->dev, "register %s, overlay_num %d\n", + path->name, path->overlay_num); + + /* default op set: if already set by driver, never cover it */ + if (!path->ops.check_status) + path->ops.check_status = path_check_status; + if (!path->ops.get_overlay) + path->ops.get_overlay = path_get_overlay; + if (!path->ops.get_modelist) + path->ops.get_modelist = path_get_modelist; + + /* step3: init overlays */ + for (i = 0; i < path->overlay_num; i++) { + path->overlays[i].path = path; + path->overlays[i].id = i; + mutex_init(&path->overlays[i].access_ok); + path->overlays[i].ops = info->overlay_ops; + } + + /* add to pathlist */ + list_add_tail(&path->node, &path_list); + + mutex_unlock(&disp_lock); + return path; + +failed: + kfree(path); + mutex_unlock(&disp_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(mmp_register_path); + +/* + * mmp_unregister_path - unregister and destory path + * @p: path to be destoried. + * + * this function registers path and destorys it. + */ +void mmp_unregister_path(struct mmp_path *path) +{ + int i; + + if (!path) + return; + + mutex_lock(&disp_lock); + /* del from pathlist */ + list_del(&path->node); + + /* deinit overlays */ + for (i = 0; i < path->overlay_num; i++) + mutex_destroy(&path->overlays[i].access_ok); + + mutex_destroy(&path->access_ok); + + kfree(path); + mutex_unlock(&disp_lock); + + dev_info(path->dev, "de-register %s\n", path->name); +} +EXPORT_SYMBOL_GPL(mmp_unregister_path); diff --git a/drivers/video/mmp/fb/Kconfig b/drivers/video/mmp/fb/Kconfig new file mode 100644 index 000000000000..9b0141f105f5 --- /dev/null +++ b/drivers/video/mmp/fb/Kconfig @@ -0,0 +1,13 @@ +if MMP_DISP + +config MMP_FB + bool "fb driver for Marvell MMP Display Subsystem" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + help + fb driver for Marvell MMP Display Subsystem + +endif diff --git a/drivers/video/mmp/fb/Makefile b/drivers/video/mmp/fb/Makefile new file mode 100644 index 000000000000..709fd1f76abe --- /dev/null +++ b/drivers/video/mmp/fb/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MMP_FB) += mmpfb.o diff --git a/drivers/video/mmp/fb/mmpfb.c b/drivers/video/mmp/fb/mmpfb.c new file mode 100644 index 000000000000..f34a3a907f1b --- /dev/null +++ b/drivers/video/mmp/fb/mmpfb.c @@ -0,0 +1,684 @@ +/* + * linux/drivers/video/mmp/fb/mmpfb.c + * Framebuffer driver for Marvell Display controller. + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include "mmpfb.h" + +static int var_to_pixfmt(struct fb_var_screeninfo *var) +{ + /* + * Pseudocolor mode? + */ + if (var->bits_per_pixel == 8) + return PIXFMT_PSEUDOCOLOR; + + /* + * Check for YUV422PLANAR. + */ + if (var->bits_per_pixel == 16 && var->red.length == 8 && + var->green.length == 4 && var->blue.length == 4) { + if (var->green.offset >= var->blue.offset) + return PIXFMT_YUV422P; + else + return PIXFMT_YVU422P; + } + + /* + * Check for YUV420PLANAR. + */ + if (var->bits_per_pixel == 12 && var->red.length == 8 && + var->green.length == 2 && var->blue.length == 2) { + if (var->green.offset >= var->blue.offset) + return PIXFMT_YUV420P; + else + return PIXFMT_YVU420P; + } + + /* + * Check for YUV422PACK. + */ + if (var->bits_per_pixel == 16 && var->red.length == 16 && + var->green.length == 16 && var->blue.length == 16) { + if (var->red.offset == 0) + return PIXFMT_YUYV; + else if (var->green.offset >= var->blue.offset) + return PIXFMT_UYVY; + else + return PIXFMT_VYUY; + } + + /* + * Check for 565/1555. + */ + if (var->bits_per_pixel == 16 && var->red.length <= 5 && + var->green.length <= 6 && var->blue.length <= 5) { + if (var->transp.length == 0) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB565; + else + return PIXFMT_BGR565; + } + } + + /* + * Check for 888/A888. + */ + if (var->bits_per_pixel <= 32 && var->red.length <= 8 && + var->green.length <= 8 && var->blue.length <= 8) { + if (var->bits_per_pixel == 24 && var->transp.length == 0) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB888PACK; + else + return PIXFMT_BGR888PACK; + } + + if (var->bits_per_pixel == 32 && var->transp.offset == 24) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGBA888; + else + return PIXFMT_BGRA888; + } else { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB888UNPACK; + else + return PIXFMT_BGR888UNPACK; + } + + /* fall through */ + } + + return -EINVAL; +} + +static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt) +{ + switch (pix_fmt) { + case PIXFMT_RGB565: + var->bits_per_pixel = 16; + var->red.offset = 11; var->red.length = 5; + var->green.offset = 5; var->green.length = 6; + var->blue.offset = 0; var->blue.length = 5; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR565: + var->bits_per_pixel = 16; + var->red.offset = 0; var->red.length = 5; + var->green.offset = 5; var->green.length = 6; + var->blue.offset = 11; var->blue.length = 5; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_RGB888UNPACK: + var->bits_per_pixel = 32; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR888UNPACK: + var->bits_per_pixel = 32; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_RGBA888: + var->bits_per_pixel = 32; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 24; var->transp.length = 8; + break; + case PIXFMT_BGRA888: + var->bits_per_pixel = 32; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 24; var->transp.length = 8; + break; + case PIXFMT_RGB888PACK: + var->bits_per_pixel = 24; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR888PACK: + var->bits_per_pixel = 24; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUV420P: + var->bits_per_pixel = 12; + var->red.offset = 4; var->red.length = 8; + var->green.offset = 2; var->green.length = 2; + var->blue.offset = 0; var->blue.length = 2; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YVU420P: + var->bits_per_pixel = 12; + var->red.offset = 4; var->red.length = 8; + var->green.offset = 0; var->green.length = 2; + var->blue.offset = 2; var->blue.length = 2; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUV422P: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 8; + var->green.offset = 4; var->green.length = 4; + var->blue.offset = 0; var->blue.length = 4; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YVU422P: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 8; + var->green.offset = 0; var->green.length = 4; + var->blue.offset = 4; var->blue.length = 4; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_UYVY: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 16; + var->green.offset = 4; var->green.length = 16; + var->blue.offset = 0; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_VYUY: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 16; + var->green.offset = 0; var->green.length = 16; + var->blue.offset = 4; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUYV: + var->bits_per_pixel = 16; + var->red.offset = 0; var->red.length = 16; + var->green.offset = 4; var->green.length = 16; + var->blue.offset = 8; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_PSEUDOCOLOR: + var->bits_per_pixel = 8; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 0; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + } +} + +/* + * fb framework has its limitation: + * 1. input color/output color is not seprated + * 2. fb_videomode not include output color + * so for fb usage, we keep a output format which is not changed + * then it's added for mmpmode + */ +static void fbmode_to_mmpmode(struct mmp_mode *mode, + struct fb_videomode *videomode, int output_fmt) +{ + u64 div_result = 1000000000000ll; + mode->name = videomode->name; + mode->refresh = videomode->refresh; + mode->xres = videomode->xres; + mode->yres = videomode->yres; + + do_div(div_result, videomode->pixclock); + mode->pixclock_freq = (u32)div_result; + + mode->left_margin = videomode->left_margin; + mode->right_margin = videomode->right_margin; + mode->upper_margin = videomode->upper_margin; + mode->lower_margin = videomode->lower_margin; + mode->hsync_len = videomode->hsync_len; + mode->vsync_len = videomode->vsync_len; + mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT); + mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT); + /* no defined flag in fb, use vmode>>3*/ + mode->invert_pixclock = !!(videomode->vmode & 8); + mode->pix_fmt_out = output_fmt; +} + +static void mmpmode_to_fbmode(struct fb_videomode *videomode, + struct mmp_mode *mode) +{ + u64 div_result = 1000000000000ll; + + videomode->name = mode->name; + videomode->refresh = mode->refresh; + videomode->xres = mode->xres; + videomode->yres = mode->yres; + + do_div(div_result, mode->pixclock_freq); + videomode->pixclock = (u32)div_result; + + videomode->left_margin = mode->left_margin; + videomode->right_margin = mode->right_margin; + videomode->upper_margin = mode->upper_margin; + videomode->lower_margin = mode->lower_margin; + videomode->hsync_len = mode->hsync_len; + videomode->vsync_len = mode->vsync_len; + videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0) + | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0); + videomode->vmode = mode->invert_pixclock ? 8 : 0; +} + +static int mmpfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + + if (var->bits_per_pixel == 8) + return -EINVAL; + /* + * Basic geometry sanity checks. + */ + if (var->xoffset + var->xres > var->xres_virtual) + return -EINVAL; + if (var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + + /* + * Check size of framebuffer. + */ + if (var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel >> 3) > fbi->fb_size) + return -EINVAL; + + return 0; +} + +static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) +{ + return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; +} + +static u32 to_rgb(u16 red, u16 green, u16 blue) +{ + red >>= 8; + green >>= 8; + blue >>= 8; + + return (red << 16) | (green << 8) | blue; +} + +static int mmpfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int trans, struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + u32 val; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) { + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue , &info->var.blue); + fbi->pseudo_palette[regno] = val; + } + + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { + val = to_rgb(red, green, blue); + /* TODO */ + } + + return 0; +} + +static int mmpfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct mmp_addr addr; + + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8 + fbi->fb_start_dma; + mmp_overlay_set_addr(fbi->overlay, &addr); + + return 0; +} + +static int var_update(struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct fb_videomode *m; + int pix_fmt; + + /* set pix_fmt */ + pix_fmt = var_to_pixfmt(var); + if (pix_fmt < 0) + return -EINVAL; + pixfmt_to_var(var, pix_fmt); + fbi->pix_fmt = pix_fmt; + + /* set var according to best video mode*/ + m = (struct fb_videomode *)fb_match_mode(var, &info->modelist); + if (!m) { + dev_err(fbi->dev, "set par: no match mode, use best mode\n"); + m = (struct fb_videomode *)fb_find_best_mode(var, + &info->modelist); + fb_videomode_to_var(var, m); + } + memcpy(&fbi->mode, m, sizeof(struct fb_videomode)); + + /* fix to 2* yres */ + var->yres_virtual = var->yres * 2; + info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + info->fix.ypanstep = var->yres; + return 0; +} + +static int mmpfb_set_par(struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct mmp_addr addr; + struct mmp_win win; + struct mmp_mode mode; + int ret; + + ret = var_update(info); + if (ret != 0) + return ret; + + /* set window/path according to new videomode */ + fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt); + mmp_path_set_mode(fbi->path, &mode); + + memset(&win, 0, sizeof(win)); + win.xsrc = win.xdst = fbi->mode.xres; + win.ysrc = win.ydst = fbi->mode.yres; + win.pix_fmt = fbi->pix_fmt; + mmp_overlay_set_win(fbi->overlay, &win); + + /* set address always */ + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8 + fbi->fb_start_dma; + mmp_overlay_set_addr(fbi->overlay, &addr); + + return 0; +} + +static void mmpfb_power(struct mmpfb_info *fbi, int power) +{ + struct mmp_addr addr; + struct mmp_win win; + struct fb_var_screeninfo *var = &fbi->fb_info->var; + + /* for power on, always set address/window again */ + if (power) { + memset(&win, 0, sizeof(win)); + win.xsrc = win.xdst = fbi->mode.xres; + win.ysrc = win.ydst = fbi->mode.yres; + win.pix_fmt = fbi->pix_fmt; + mmp_overlay_set_win(fbi->overlay, &win); + + /* set address always */ + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = fbi->fb_start_dma + + (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8; + mmp_overlay_set_addr(fbi->overlay, &addr); + } + mmp_overlay_set_onoff(fbi->overlay, power); +} + +static int mmpfb_blank(int blank, struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + + mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK)); + + return 0; +} + +static struct fb_ops mmpfb_ops = { + .owner = THIS_MODULE, + .fb_blank = mmpfb_blank, + .fb_check_var = mmpfb_check_var, + .fb_set_par = mmpfb_set_par, + .fb_setcolreg = mmpfb_setcolreg, + .fb_pan_display = mmpfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int modes_setup(struct mmpfb_info *fbi) +{ + struct fb_videomode *videomodes; + struct mmp_mode *mmp_modes; + struct fb_info *info = fbi->fb_info; + int videomode_num, i; + + /* get videomodes from path */ + videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes); + if (!videomode_num) { + dev_warn(fbi->dev, "can't get videomode num\n"); + return 0; + } + /* put videomode list to info structure */ + videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num, + GFP_KERNEL); + if (!videomodes) { + dev_err(fbi->dev, "can't malloc video modes\n"); + return -ENOMEM; + } + for (i = 0; i < videomode_num; i++) + mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]); + fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist); + + /* set videomode[0] as default mode */ + memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode)); + fbi->output_fmt = mmp_modes[0].pix_fmt_out; + fb_videomode_to_var(&info->var, &fbi->mode); + mmp_path_set_mode(fbi->path, &mmp_modes[0]); + + kfree(videomodes); + return videomode_num; +} + +static int fb_info_setup(struct fb_info *info, + struct mmpfb_info *fbi) +{ + int ret = 0; + /* Initialise static fb parameters.*/ + info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | + FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; + info->node = -1; + strcpy(info->fix.id, fbi->name); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.type_aux = 0; + info->fix.xpanstep = 0; + info->fix.ypanstep = info->var.yres; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + info->fix.smem_start = fbi->fb_start_dma; + info->fix.smem_len = fbi->fb_size; + info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.xres_virtual * + info->var.bits_per_pixel / 8; + info->fbops = &mmpfb_ops; + info->pseudo_palette = fbi->pseudo_palette; + info->screen_base = fbi->fb_start; + info->screen_size = fbi->fb_size; + + /* For FB framework: Allocate color map and Register framebuffer*/ + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) + ret = -ENOMEM; + + return ret; +} + +static void fb_info_clear(struct fb_info *info) +{ + fb_dealloc_cmap(&info->cmap); +} + +static int mmpfb_probe(struct platform_device *pdev) +{ + struct mmp_buffer_driver_mach_info *mi; + struct fb_info *info = 0; + struct mmpfb_info *fbi = 0; + int ret, modes_num; + + mi = pdev->dev.platform_data; + if (mi == NULL) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + /* initialize fb */ + info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev); + if (info == NULL) + return -ENOMEM; + fbi = info->par; + if (!fbi) { + ret = -EINVAL; + goto failed; + } + + /* init fb */ + fbi->fb_info = info; + platform_set_drvdata(pdev, fbi); + fbi->dev = &pdev->dev; + fbi->name = mi->name; + fbi->pix_fmt = mi->default_pixfmt; + pixfmt_to_var(&info->var, fbi->pix_fmt); + mutex_init(&fbi->access_ok); + + /* get display path by name */ + fbi->path = mmp_get_path(mi->path_name); + if (!fbi->path) { + dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name); + ret = -EINVAL; + goto failed_destroy_mutex; + } + + dev_info(fbi->dev, "path %s get\n", fbi->path->name); + + /* get overlay */ + fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id); + if (!fbi->overlay) { + ret = -EINVAL; + goto failed_destroy_mutex; + } + /* set fetch used */ + mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id); + + modes_num = modes_setup(fbi); + if (modes_num < 0) { + ret = modes_num; + goto failed_destroy_mutex; + } + + /* + * if get modes success, means not hotplug panels, use caculated buffer + * or use default size + */ + if (modes_num > 0) { + /* fix to 2* yres */ + info->var.yres_virtual = info->var.yres * 2; + + /* Allocate framebuffer memory: size = modes xy *4 */ + fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual + * info->var.bits_per_pixel / 8; + } else { + fbi->fb_size = MMPFB_DEFAULT_SIZE; + } + + fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), + &fbi->fb_start_dma, GFP_KERNEL); + if (fbi->fb_start == NULL) { + dev_err(&pdev->dev, "can't alloc framebuffer\n"); + ret = -ENOMEM; + goto failed_destroy_mutex; + } + memset(fbi->fb_start, 0, fbi->fb_size); + dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024); + + /* fb power on */ + if (modes_num > 0) + mmpfb_power(fbi, 1); + + ret = fb_info_setup(info, fbi); + if (ret < 0) + goto failed_free_buff; + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register fb: %d\n", ret); + ret = -ENXIO; + goto failed_clear_info; + } + + dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n", + info->node, info->fix.id); + +#ifdef CONFIG_LOGO + if (fbi->fb_start) { + fb_prepare_logo(info, 0); + fb_show_logo(info, 0); + } +#endif + + return 0; + +failed_clear_info: + fb_info_clear(info); +failed_free_buff: + dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start, + fbi->fb_start_dma); +failed_destroy_mutex: + mutex_destroy(&fbi->access_ok); +failed: + dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n"); + platform_set_drvdata(pdev, NULL); + + framebuffer_release(info); + + return ret; +} + +static struct platform_driver mmpfb_driver = { + .driver = { + .name = "mmp-fb", + .owner = THIS_MODULE, + }, + .probe = mmpfb_probe, +}; + +static int mmpfb_init(void) +{ + return platform_driver_register(&mmpfb_driver); +} +module_init(mmpfb_init); + +MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>"); +MODULE_DESCRIPTION("Framebuffer driver for Marvell displays"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mmp/fb/mmpfb.h b/drivers/video/mmp/fb/mmpfb.h new file mode 100644 index 000000000000..88c23c10a9ec --- /dev/null +++ b/drivers/video/mmp/fb/mmpfb.h @@ -0,0 +1,54 @@ +/* + * linux/drivers/video/mmp/fb/mmpfb.h + * Framebuffer driver for Marvell Display controller. + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef _MMP_FB_H_ +#define _MMP_FB_H_ + +#include <video/mmp_disp.h> +#include <linux/fb.h> + +/* LCD controller private state. */ +struct mmpfb_info { + struct device *dev; + int id; + const char *name; + + struct fb_info *fb_info; + /* basicaly videomode is for output */ + struct fb_videomode mode; + int pix_fmt; + + void *fb_start; + int fb_size; + dma_addr_t fb_start_dma; + + struct mmp_overlay *overlay; + struct mmp_path *path; + + struct mutex access_ok; + + unsigned int pseudo_palette[16]; + int output_fmt; +}; + +#define MMPFB_DEFAULT_SIZE (PAGE_ALIGN(1920 * 1080 * 4 * 2)) +#endif /* _MMP_FB_H_ */ diff --git a/drivers/video/mmp/hw/Kconfig b/drivers/video/mmp/hw/Kconfig new file mode 100644 index 000000000000..02f109a20cd0 --- /dev/null +++ b/drivers/video/mmp/hw/Kconfig @@ -0,0 +1,20 @@ +if MMP_DISP + +config MMP_DISP_CONTROLLER + bool "mmp display controller hw support" + depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988 + default n + help + Marvell MMP display hw controller support + this controller is used on Marvell PXA910, + MMP2, MMP3, PXA988 chips + +config MMP_DISP_SPI + bool "mmp display controller spi port" + depends on MMP_DISP_CONTROLLER && SPI_MASTER + default y + help + Marvell MMP display hw controller spi port support + will register as a spi master for panel usage + +endif diff --git a/drivers/video/mmp/hw/Makefile b/drivers/video/mmp/hw/Makefile new file mode 100644 index 000000000000..0000a714fedf --- /dev/null +++ b/drivers/video/mmp/hw/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MMP_DISP_CONTROLLER) += mmp_ctrl.o +obj-$(CONFIG_MMP_DISP_SPI) += mmp_spi.o diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c new file mode 100644 index 000000000000..4bd31b2af398 --- /dev/null +++ b/drivers/video/mmp/hw/mmp_ctrl.c @@ -0,0 +1,591 @@ +/* + * linux/drivers/video/mmp/hw/mmp_ctrl.c + * Marvell MMP series Display Controller support + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> +#include <linux/kthread.h> +#include <linux/io.h> + +#include "mmp_ctrl.h" + +static irqreturn_t ctrl_handle_irq(int irq, void *dev_id) +{ + struct mmphw_ctrl *ctrl = (struct mmphw_ctrl *)dev_id; + u32 isr, imask, tmp; + + isr = readl_relaxed(ctrl->reg_base + SPU_IRQ_ISR); + imask = readl_relaxed(ctrl->reg_base + SPU_IRQ_ENA); + + do { + /* clear clock only */ + tmp = readl_relaxed(ctrl->reg_base + SPU_IRQ_ISR); + if (tmp & isr) + writel_relaxed(~isr, ctrl->reg_base + SPU_IRQ_ISR); + } while ((isr = readl(ctrl->reg_base + SPU_IRQ_ISR)) & imask); + + return IRQ_HANDLED; +} + +static u32 fmt_to_reg(struct mmp_overlay *overlay, int pix_fmt) +{ + u32 link_config = path_to_path_plat(overlay->path)->link_config; + u32 rbswap, uvswap = 0, yuvswap = 0, + csc_en = 0, val = 0, + vid = overlay_is_vid(overlay); + + switch (pix_fmt) { + case PIXFMT_RGB565: + case PIXFMT_RGB1555: + case PIXFMT_RGB888PACK: + case PIXFMT_RGB888UNPACK: + case PIXFMT_RGBA888: + rbswap = !(link_config & 0x1); + break; + case PIXFMT_VYUY: + case PIXFMT_YVU422P: + case PIXFMT_YVU420P: + rbswap = link_config & 0x1; + uvswap = 1; + break; + case PIXFMT_YUYV: + rbswap = link_config & 0x1; + yuvswap = 1; + break; + default: + rbswap = link_config & 0x1; + break; + } + + switch (pix_fmt) { + case PIXFMT_RGB565: + case PIXFMT_BGR565: + val = 0; + break; + case PIXFMT_RGB1555: + case PIXFMT_BGR1555: + val = 0x1; + break; + case PIXFMT_RGB888PACK: + case PIXFMT_BGR888PACK: + val = 0x2; + break; + case PIXFMT_RGB888UNPACK: + case PIXFMT_BGR888UNPACK: + val = 0x3; + break; + case PIXFMT_RGBA888: + case PIXFMT_BGRA888: + val = 0x4; + break; + case PIXFMT_UYVY: + case PIXFMT_VYUY: + case PIXFMT_YUYV: + val = 0x5; + csc_en = 1; + break; + case PIXFMT_YUV422P: + case PIXFMT_YVU422P: + val = 0x6; + csc_en = 1; + break; + case PIXFMT_YUV420P: + case PIXFMT_YVU420P: + val = 0x7; + csc_en = 1; + break; + default: + break; + } + + return (dma_palette(0) | dma_fmt(vid, val) | + dma_swaprb(vid, rbswap) | dma_swapuv(vid, uvswap) | + dma_swapyuv(vid, yuvswap) | dma_csc(vid, csc_en)); +} + +static void dmafetch_set_fmt(struct mmp_overlay *overlay) +{ + u32 tmp; + struct mmp_path *path = overlay->path; + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~dma_mask(overlay_is_vid(overlay)); + tmp |= fmt_to_reg(overlay, overlay->win.pix_fmt); + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); +} + +static void overlay_set_win(struct mmp_overlay *overlay, struct mmp_win *win) +{ + struct lcd_regs *regs = path_regs(overlay->path); + u32 pitch; + + /* assert win supported */ + memcpy(&overlay->win, win, sizeof(struct mmp_win)); + + mutex_lock(&overlay->access_ok); + pitch = win->xsrc * pixfmt_to_stride(win->pix_fmt); + writel_relaxed(pitch, ®s->g_pitch); + writel_relaxed((win->ysrc << 16) | win->xsrc, ®s->g_size); + writel_relaxed((win->ydst << 16) | win->xdst, ®s->g_size_z); + writel_relaxed(0, ®s->g_start); + + dmafetch_set_fmt(overlay); + mutex_unlock(&overlay->access_ok); +} + +static void dmafetch_onoff(struct mmp_overlay *overlay, int on) +{ + u32 mask = overlay_is_vid(overlay) ? CFG_GRA_ENA_MASK : + CFG_DMA_ENA_MASK; + u32 enable = overlay_is_vid(overlay) ? CFG_GRA_ENA(1) : CFG_DMA_ENA(1); + u32 tmp; + struct mmp_path *path = overlay->path; + + mutex_lock(&overlay->access_ok); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~mask; + tmp |= (on ? enable : 0); + writel(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + mutex_unlock(&overlay->access_ok); +} + +static void path_enabledisable(struct mmp_path *path, int on) +{ + u32 tmp; + mutex_lock(&path->access_ok); + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + if (on) + tmp &= ~SCLK_DISABLE; + else + tmp |= SCLK_DISABLE; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + mutex_unlock(&path->access_ok); +} + +static void path_onoff(struct mmp_path *path, int on) +{ + if (path->status == on) { + dev_info(path->dev, "path %s is already %s\n", + path->name, stat_name(path->status)); + return; + } + + if (on) { + path_enabledisable(path, 1); + + if (path->panel && path->panel->set_onoff) + path->panel->set_onoff(path->panel, 1); + } else { + if (path->panel && path->panel->set_onoff) + path->panel->set_onoff(path->panel, 0); + + path_enabledisable(path, 0); + } + path->status = on; +} + +static void overlay_set_onoff(struct mmp_overlay *overlay, int on) +{ + if (overlay->status == on) { + dev_info(overlay_to_ctrl(overlay)->dev, "overlay %s is already %s\n", + overlay->path->name, stat_name(overlay->status)); + return; + } + overlay->status = on; + dmafetch_onoff(overlay, on); + if (overlay->path->ops.check_status(overlay->path) + != overlay->path->status) + path_onoff(overlay->path, on); +} + +static void overlay_set_fetch(struct mmp_overlay *overlay, int fetch_id) +{ + overlay->dmafetch_id = fetch_id; +} + +static int overlay_set_addr(struct mmp_overlay *overlay, struct mmp_addr *addr) +{ + struct lcd_regs *regs = path_regs(overlay->path); + + /* FIXME: assert addr supported */ + memcpy(&overlay->addr, addr, sizeof(struct mmp_win)); + writel(addr->phys[0], ®s->g_0); + + return overlay->addr.phys[0]; +} + +static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode) +{ + struct lcd_regs *regs = path_regs(path); + u32 total_x, total_y, vsync_ctrl, tmp, sclk_src, sclk_div, + link_config = path_to_path_plat(path)->link_config; + + /* FIXME: assert videomode supported */ + memcpy(&path->mode, mode, sizeof(struct mmp_mode)); + + mutex_lock(&path->access_ok); + + /* polarity of timing signals */ + tmp = readl_relaxed(ctrl_regs(path) + intf_ctrl(path->id)) & 0x1; + tmp |= mode->vsync_invert ? 0 : 0x8; + tmp |= mode->hsync_invert ? 0 : 0x4; + tmp |= link_config & CFG_DUMBMODE_MASK; + tmp |= CFG_DUMB_ENA(1); + writel_relaxed(tmp, ctrl_regs(path) + intf_ctrl(path->id)); + + writel_relaxed((mode->yres << 16) | mode->xres, ®s->screen_active); + writel_relaxed((mode->left_margin << 16) | mode->right_margin, + ®s->screen_h_porch); + writel_relaxed((mode->upper_margin << 16) | mode->lower_margin, + ®s->screen_v_porch); + total_x = mode->xres + mode->left_margin + mode->right_margin + + mode->hsync_len; + total_y = mode->yres + mode->upper_margin + mode->lower_margin + + mode->vsync_len; + writel_relaxed((total_y << 16) | total_x, ®s->screen_size); + + /* vsync ctrl */ + if (path->output_type == PATH_OUT_DSI) + vsync_ctrl = 0x01330133; + else + vsync_ctrl = ((mode->xres + mode->right_margin) << 16) + | (mode->xres + mode->right_margin); + writel_relaxed(vsync_ctrl, ®s->vsync_ctrl); + + /* set pixclock div */ + sclk_src = clk_get_rate(path_to_ctrl(path)->clk); + sclk_div = sclk_src / mode->pixclock_freq; + if (sclk_div * mode->pixclock_freq < sclk_src) + sclk_div++; + + dev_info(path->dev, "%s sclk_src %d sclk_div 0x%x pclk %d\n", + __func__, sclk_src, sclk_div, mode->pixclock_freq); + + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + tmp &= ~CLK_INT_DIV_MASK; + tmp |= sclk_div; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + + mutex_unlock(&path->access_ok); +} + +static struct mmp_overlay_ops mmphw_overlay_ops = { + .set_fetch = overlay_set_fetch, + .set_onoff = overlay_set_onoff, + .set_win = overlay_set_win, + .set_addr = overlay_set_addr, +}; + +static void ctrl_set_default(struct mmphw_ctrl *ctrl) +{ + u32 tmp, irq_mask; + + /* + * LCD Global control(LCD_TOP_CTRL) should be configed before + * any other LCD registers read/write, or there maybe issues. + */ + tmp = readl_relaxed(ctrl->reg_base + LCD_TOP_CTRL); + tmp |= 0xfff0; + writel_relaxed(tmp, ctrl->reg_base + LCD_TOP_CTRL); + + + /* disable all interrupts */ + irq_mask = path_imasks(0) | err_imask(0) | + path_imasks(1) | err_imask(1); + tmp = readl_relaxed(ctrl->reg_base + SPU_IRQ_ENA); + tmp &= ~irq_mask; + tmp |= irq_mask; + writel_relaxed(tmp, ctrl->reg_base + SPU_IRQ_ENA); +} + +static void path_set_default(struct mmp_path *path) +{ + struct lcd_regs *regs = path_regs(path); + u32 dma_ctrl1, mask, tmp, path_config; + + path_config = path_to_path_plat(path)->path_config; + + /* Configure IOPAD: should be parallel only */ + if (PATH_OUT_PARALLEL == path->output_type) { + mask = CFG_IOPADMODE_MASK | CFG_BURST_MASK | CFG_BOUNDARY_MASK; + tmp = readl_relaxed(ctrl_regs(path) + SPU_IOPAD_CONTROL); + tmp &= ~mask; + tmp |= path_config; + writel_relaxed(tmp, ctrl_regs(path) + SPU_IOPAD_CONTROL); + } + + /* Select path clock source */ + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + tmp &= ~SCLK_SRC_SEL_MASK; + tmp |= path_config; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + + /* + * Configure default bits: vsync triggers DMA, + * power save enable, configure alpha registers to + * display 100% graphics, and set pixel command. + */ + dma_ctrl1 = 0x2032ff81; + + dma_ctrl1 |= CFG_VSYNC_INV_MASK; + writel_relaxed(dma_ctrl1, ctrl_regs(path) + dma_ctrl(1, path->id)); + + /* Configure default register values */ + writel_relaxed(0x00000000, ®s->blank_color); + writel_relaxed(0x00000000, ®s->g_1); + writel_relaxed(0x00000000, ®s->g_start); + + /* + * 1.enable multiple burst request in DMA AXI + * bus arbiter for faster read if not tv path; + * 2.enable horizontal smooth filter; + */ + if (PATH_PN == path->id) { + mask = CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK + | CFG_ARBFAST_ENA(1); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp |= mask; + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + } else if (PATH_TV == path->id) { + mask = CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK + | CFG_ARBFAST_ENA(1); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~mask; + tmp |= CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK; + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + } +} + +static int path_init(struct mmphw_path_plat *path_plat, + struct mmp_mach_path_config *config) +{ + struct mmphw_ctrl *ctrl = path_plat->ctrl; + struct mmp_path_info *path_info; + struct mmp_path *path = NULL; + + dev_info(ctrl->dev, "%s: %s\n", __func__, config->name); + + /* init driver data */ + path_info = kzalloc(sizeof(struct mmp_path_info), GFP_KERNEL); + if (!path_info) { + dev_err(ctrl->dev, "%s: unable to alloc path_info for %s\n", + __func__, config->name); + return 0; + } + path_info->name = config->name; + path_info->id = path_plat->id; + path_info->dev = ctrl->dev; + path_info->overlay_num = config->overlay_num; + path_info->overlay_ops = &mmphw_overlay_ops; + path_info->set_mode = path_set_mode; + path_info->plat_data = path_plat; + + /* create/register platform device */ + path = mmp_register_path(path_info); + if (!path) { + kfree(path_info); + return 0; + } + path_plat->path = path; + path_plat->path_config = config->path_config; + path_plat->link_config = config->link_config; + path_set_default(path); + + kfree(path_info); + return 1; +} + +static void path_deinit(struct mmphw_path_plat *path_plat) +{ + if (!path_plat) + return; + + if (path_plat->path) + mmp_unregister_path(path_plat->path); +} + +static int mmphw_probe(struct platform_device *pdev) +{ + struct mmp_mach_plat_info *mi; + struct resource *res; + int ret, i, size, irq; + struct mmphw_path_plat *path_plat; + struct mmphw_ctrl *ctrl = NULL; + + /* get resources from platform data */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "%s: no IO memory defined\n", __func__); + ret = -ENOENT; + goto failed; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "%s: no IRQ defined\n", __func__); + ret = -ENOENT; + goto failed; + } + + /* get configs from platform data */ + mi = pdev->dev.platform_data; + if (mi == NULL || !mi->path_num || !mi->paths) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + ret = -EINVAL; + goto failed; + } + + /* allocate */ + size = sizeof(struct mmphw_ctrl) + sizeof(struct mmphw_path_plat) * + mi->path_num; + ctrl = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!ctrl) { + ret = -ENOMEM; + goto failed; + } + + ctrl->name = mi->name; + ctrl->path_num = mi->path_num; + ctrl->dev = &pdev->dev; + ctrl->irq = irq; + platform_set_drvdata(pdev, ctrl); + mutex_init(&ctrl->access_ok); + + /* map registers.*/ + if (!devm_request_mem_region(ctrl->dev, res->start, + resource_size(res), ctrl->name)) { + dev_err(ctrl->dev, + "can't request region for resource %pR\n", res); + ret = -EINVAL; + goto failed; + } + + ctrl->reg_base = devm_ioremap_nocache(ctrl->dev, + res->start, resource_size(res)); + if (ctrl->reg_base == NULL) { + dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__, + res->start, res->end); + ret = -ENOMEM; + goto failed; + } + + /* request irq */ + ret = devm_request_irq(ctrl->dev, ctrl->irq, ctrl_handle_irq, + IRQF_SHARED, "lcd_controller", ctrl); + if (ret < 0) { + dev_err(ctrl->dev, "%s unable to request IRQ %d\n", + __func__, ctrl->irq); + ret = -ENXIO; + goto failed; + } + + /* get clock */ + ctrl->clk = devm_clk_get(ctrl->dev, mi->clk_name); + if (IS_ERR(ctrl->clk)) { + dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name); + ret = -ENOENT; + goto failed_get_clk; + } + clk_prepare_enable(ctrl->clk); + + /* init global regs */ + ctrl_set_default(ctrl); + + /* init pathes from machine info and register them */ + for (i = 0; i < ctrl->path_num; i++) { + /* get from config and machine info */ + path_plat = &ctrl->path_plats[i]; + path_plat->id = i; + path_plat->ctrl = ctrl; + + /* path init */ + if (!path_init(path_plat, &mi->paths[i])) { + ret = -EINVAL; + goto failed_path_init; + } + } + +#ifdef CONFIG_MMP_DISP_SPI + ret = lcd_spi_register(ctrl); + if (ret < 0) + goto failed_path_init; +#endif + + dev_info(ctrl->dev, "device init done\n"); + + return 0; + +failed_path_init: + for (i = 0; i < ctrl->path_num; i++) { + path_plat = &ctrl->path_plats[i]; + path_deinit(path_plat); + } + + if (ctrl->clk) { + devm_clk_put(ctrl->dev, ctrl->clk); + clk_disable_unprepare(ctrl->clk); + } +failed_get_clk: + devm_free_irq(ctrl->dev, ctrl->irq, ctrl); +failed: + if (ctrl) { + if (ctrl->reg_base) + devm_iounmap(ctrl->dev, ctrl->reg_base); + devm_release_mem_region(ctrl->dev, res->start, + resource_size(res)); + devm_kfree(ctrl->dev, ctrl); + } + + platform_set_drvdata(pdev, NULL); + dev_err(&pdev->dev, "device init failed\n"); + + return ret; +} + +static struct platform_driver mmphw_driver = { + .driver = { + .name = "mmp-disp", + .owner = THIS_MODULE, + }, + .probe = mmphw_probe, +}; + +static int mmphw_init(void) +{ + return platform_driver_register(&mmphw_driver); +} +module_init(mmphw_init); + +MODULE_AUTHOR("Li Guoqing<ligq@marvell.com>"); +MODULE_DESCRIPTION("Framebuffer driver for mmp"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mmp/hw/mmp_ctrl.h b/drivers/video/mmp/hw/mmp_ctrl.h new file mode 100644 index 000000000000..6408d8ef3abb --- /dev/null +++ b/drivers/video/mmp/hw/mmp_ctrl.h @@ -0,0 +1,1974 @@ +/* + * drivers/video/mmp/hw/mmp_ctrl.h + * + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef _MMP_CTRL_H_ +#define _MMP_CTRL_H_ + +#include <video/mmp_disp.h> + +/* ------------< LCD register >------------ */ +struct lcd_regs { +/* TV patch register for MMP2 */ +/* 32 bit TV Video Frame0 Y Starting Address */ +#define LCD_TVD_START_ADDR_Y0 (0x0000) +/* 32 bit TV Video Frame0 U Starting Address */ +#define LCD_TVD_START_ADDR_U0 (0x0004) +/* 32 bit TV Video Frame0 V Starting Address */ +#define LCD_TVD_START_ADDR_V0 (0x0008) +/* 32 bit TV Video Frame0 Command Starting Address */ +#define LCD_TVD_START_ADDR_C0 (0x000C) +/* 32 bit TV Video Frame1 Y Starting Address Register*/ +#define LCD_TVD_START_ADDR_Y1 (0x0010) +/* 32 bit TV Video Frame1 U Starting Address Register*/ +#define LCD_TVD_START_ADDR_U1 (0x0014) +/* 32 bit TV Video Frame1 V Starting Address Register*/ +#define LCD_TVD_START_ADDR_V1 (0x0018) +/* 32 bit TV Video Frame1 Command Starting Address Register*/ +#define LCD_TVD_START_ADDR_C1 (0x001C) +/* 32 bit TV Video Y andC Line Length(Pitch)Register*/ +#define LCD_TVD_PITCH_YC (0x0020) +/* 32 bit TV Video U andV Line Length(Pitch)Register*/ +#define LCD_TVD_PITCH_UV (0x0024) +/* 32 bit TV Video Starting Point on Screen Register*/ +#define LCD_TVD_OVSA_HPXL_VLN (0x0028) +/* 32 bit TV Video Source Size Register*/ +#define LCD_TVD_HPXL_VLN (0x002C) +/* 32 bit TV Video Destination Size (After Zooming)Register*/ +#define LCD_TVDZM_HPXL_VLN (0x0030) + u32 v_y0; + u32 v_u0; + u32 v_v0; + u32 v_c0; + u32 v_y1; + u32 v_u1; + u32 v_v1; + u32 v_c1; + u32 v_pitch_yc; /* Video Y and C Line Length (Pitch) */ + u32 v_pitch_uv; /* Video U and V Line Length (Pitch) */ + u32 v_start; /* Video Starting Point on Screen */ + u32 v_size; /* Video Source Size */ + u32 v_size_z; /* Video Destination Size (After Zooming) */ + +/* 32 bit TV Graphic Frame 0 Starting Address Register*/ +#define LCD_TVG_START_ADDR0 (0x0034) +/* 32 bit TV Graphic Frame 1 Starting Address Register*/ +#define LCD_TVG_START_ADDR1 (0x0038) +/* 32 bit TV Graphic Line Length(Pitch)Register*/ +#define LCD_TVG_PITCH (0x003C) +/* 32 bit TV Graphic Starting Point on Screen Register*/ +#define LCD_TVG_OVSA_HPXL_VLN (0x0040) +/* 32 bit TV Graphic Source Size Register*/ +#define LCD_TVG_HPXL_VLN (0x0044) +/* 32 bit TV Graphic Destination size (after Zooming)Register*/ +#define LCD_TVGZM_HPXL_VLN (0x0048) + u32 g_0; /* Graphic Frame 0/1 Starting Address */ + u32 g_1; + u32 g_pitch; /* Graphic Line Length (Pitch) */ + u32 g_start; /* Graphic Starting Point on Screen */ + u32 g_size; /* Graphic Source Size */ + u32 g_size_z; /* Graphic Destination Size (After Zooming) */ + +/* 32 bit TV Hardware Cursor Starting Point on screen Register*/ +#define LCD_TVC_OVSA_HPXL_VLN (0x004C) +/* 32 bit TV Hardware Cursor Size Register */ +#define LCD_TVC_HPXL_VLN (0x0050) + u32 hc_start; /* Hardware Cursor */ + u32 hc_size; /* Hardware Cursor */ + +/* 32 bit TV Total Screen Size Register*/ +#define LCD_TV_V_H_TOTAL (0x0054) +/* 32 bit TV Screen Active Size Register*/ +#define LCD_TV_V_H_ACTIVE (0x0058) +/* 32 bit TV Screen Horizontal Porch Register*/ +#define LCD_TV_H_PORCH (0x005C) +/* 32 bit TV Screen Vertical Porch Register*/ +#define LCD_TV_V_PORCH (0x0060) + u32 screen_size; /* Screen Total Size */ + u32 screen_active; /* Screen Active Size */ + u32 screen_h_porch; /* Screen Horizontal Porch */ + u32 screen_v_porch; /* Screen Vertical Porch */ + +/* 32 bit TV Screen Blank Color Register*/ +#define LCD_TV_BLANKCOLOR (0x0064) +/* 32 bit TV Hardware Cursor Color1 Register*/ +#define LCD_TV_ALPHA_COLOR1 (0x0068) +/* 32 bit TV Hardware Cursor Color2 Register*/ +#define LCD_TV_ALPHA_COLOR2 (0x006C) + u32 blank_color; /* Screen Blank Color */ + u32 hc_Alpha_color1; /* Hardware Cursor Color1 */ + u32 hc_Alpha_color2; /* Hardware Cursor Color2 */ + +/* 32 bit TV Video Y Color Key Control*/ +#define LCD_TV_COLORKEY_Y (0x0070) +/* 32 bit TV Video U Color Key Control*/ +#define LCD_TV_COLORKEY_U (0x0074) +/* 32 bit TV Video V Color Key Control*/ +#define LCD_TV_COLORKEY_V (0x0078) + u32 v_colorkey_y; /* Video Y Color Key Control */ + u32 v_colorkey_u; /* Video U Color Key Control */ + u32 v_colorkey_v; /* Video V Color Key Control */ + +/* 32 bit TV VSYNC PulsePixel Edge Control Register*/ +#define LCD_TV_SEPXLCNT (0x007C) + u32 vsync_ctrl; /* VSYNC PulsePixel Edge Control */ +}; + +#define intf_ctrl(id) ((id) ? (((id) & 1) ? LCD_TVIF_CTRL : \ + LCD_DUMB2_CTRL) : LCD_SPU_DUMB_CTRL) +#define dma_ctrl0(id) ((id) ? (((id) & 1) ? LCD_TV_CTRL0 : \ + LCD_PN2_CTRL0) : LCD_SPU_DMA_CTRL0) +#define dma_ctrl1(id) ((id) ? (((id) & 1) ? LCD_TV_CTRL1 : \ + LCD_PN2_CTRL1) : LCD_SPU_DMA_CTRL1) +#define dma_ctrl(ctrl1, id) (ctrl1 ? dma_ctrl1(id) : dma_ctrl0(id)) + +/* 32 bit TV Path DMA Control 0*/ +#define LCD_TV_CTRL0 (0x0080) +/* 32 bit TV Path DMA Control 1*/ +#define LCD_TV_CTRL1 (0x0084) +/* 32 bit TV Path Video Contrast*/ +#define LCD_TV_CONTRAST (0x0088) +/* 32 bit TV Path Video Saturation*/ +#define LCD_TV_SATURATION (0x008C) +/* 32 bit TV Path Video Hue Adjust*/ +#define LCD_TV_CBSH_HUE (0x0090) +/* 32 bit TV Path TVIF Control Register */ +#define LCD_TVIF_CTRL (0x0094) +#define TV_VBLNK_VALID_EN (1 << 12) + +/* 32 bit TV Path I/O Pad Control*/ +#define LCD_TVIOPAD_CTRL (0x0098) +/* 32 bit TV Path Cloc Divider */ +#define LCD_TCLK_DIV (0x009C) + +#define LCD_SCLK(path) ((PATH_PN == path->id) ? LCD_CFG_SCLK_DIV :\ + ((PATH_TV == path->id) ? LCD_TCLK_DIV : LCD_PN2_SCLK_DIV)) + +/* dither configure */ +#ifdef CONFIG_CPU_PXA988 +#define LCD_DITHER_CTRL (0x01EC) +#else +#define LCD_DITHER_CTRL (0x00A0) +#endif + +#define DITHER_TBL_INDEX_SEL(s) ((s) << 16) +#define DITHER_MODE2(m) ((m) << 12) +#define DITHER_MODE2_SHIFT (12) +#define DITHER_4X8_EN2 (1 << 9) +#define DITHER_4X8_EN2_SHIFT (9) +#define DITHER_EN2 (1 << 8) +#define DITHER_MODE1(m) ((m) << 4) +#define DITHER_MODE1_SHIFT (4) +#define DITHER_4X8_EN1 (1 << 1) +#define DITHER_4X8_EN1_SHIFT (1) +#define DITHER_EN1 (1) + +/* dither table data was fixed by video bpp of input and output*/ +#ifdef CONFIG_CPU_PXA988 +#define DITHER_TB_4X4_INDEX0 (0x6e4ca280) +#define DITHER_TB_4X4_INDEX1 (0x5d7f91b3) +#define DITHER_TB_4X8_INDEX0 (0xb391a280) +#define DITHER_TB_4X8_INDEX1 (0x7f5d6e4c) +#define DITHER_TB_4X8_INDEX2 (0x80a291b3) +#define DITHER_TB_4X8_INDEX3 (0x4c6e5d7f) +#define LCD_DITHER_TBL_DATA (0x01F0) +#else +#define DITHER_TB_4X4_INDEX0 (0x3b19f7d5) +#define DITHER_TB_4X4_INDEX1 (0x082ac4e6) +#define DITHER_TB_4X8_INDEX0 (0xf7d508e6) +#define DITHER_TB_4X8_INDEX1 (0x3b194c2a) +#define DITHER_TB_4X8_INDEX2 (0xc4e6d5f7) +#define DITHER_TB_4X8_INDEX3 (0x082a193b) +#define LCD_DITHER_TBL_DATA (0x00A4) +#endif + +/* Video Frame 0&1 start address registers */ +#define LCD_SPU_DMA_START_ADDR_Y0 0x00C0 +#define LCD_SPU_DMA_START_ADDR_U0 0x00C4 +#define LCD_SPU_DMA_START_ADDR_V0 0x00C8 +#define LCD_CFG_DMA_START_ADDR_0 0x00CC /* Cmd address */ +#define LCD_SPU_DMA_START_ADDR_Y1 0x00D0 +#define LCD_SPU_DMA_START_ADDR_U1 0x00D4 +#define LCD_SPU_DMA_START_ADDR_V1 0x00D8 +#define LCD_CFG_DMA_START_ADDR_1 0x00DC /* Cmd address */ + +/* YC & UV Pitch */ +#define LCD_SPU_DMA_PITCH_YC 0x00E0 +#define SPU_DMA_PITCH_C(c) ((c)<<16) +#define SPU_DMA_PITCH_Y(y) (y) +#define LCD_SPU_DMA_PITCH_UV 0x00E4 +#define SPU_DMA_PITCH_V(v) ((v)<<16) +#define SPU_DMA_PITCH_U(u) (u) + +/* Video Starting Point on Screen Register */ +#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00E8 +#define CFG_DMA_OVSA_VLN(y) ((y)<<16) /* 0~0xfff */ +#define CFG_DMA_OVSA_HPXL(x) (x) /* 0~0xfff */ + +/* Video Size Register */ +#define LCD_SPU_DMA_HPXL_VLN 0x00EC +#define CFG_DMA_VLN(y) ((y)<<16) +#define CFG_DMA_HPXL(x) (x) + +/* Video Size After zooming Register */ +#define LCD_SPU_DZM_HPXL_VLN 0x00F0 +#define CFG_DZM_VLN(y) ((y)<<16) +#define CFG_DZM_HPXL(x) (x) + +/* Graphic Frame 0&1 Starting Address Register */ +#define LCD_CFG_GRA_START_ADDR0 0x00F4 +#define LCD_CFG_GRA_START_ADDR1 0x00F8 + +/* Graphic Frame Pitch */ +#define LCD_CFG_GRA_PITCH 0x00FC + +/* Graphic Starting Point on Screen Register */ +#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100 +#define CFG_GRA_OVSA_VLN(y) ((y)<<16) +#define CFG_GRA_OVSA_HPXL(x) (x) + +/* Graphic Size Register */ +#define LCD_SPU_GRA_HPXL_VLN 0x0104 +#define CFG_GRA_VLN(y) ((y)<<16) +#define CFG_GRA_HPXL(x) (x) + +/* Graphic Size after Zooming Register */ +#define LCD_SPU_GZM_HPXL_VLN 0x0108 +#define CFG_GZM_VLN(y) ((y)<<16) +#define CFG_GZM_HPXL(x) (x) + +/* HW Cursor Starting Point on Screen Register */ +#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010C +#define CFG_HWC_OVSA_VLN(y) ((y)<<16) +#define CFG_HWC_OVSA_HPXL(x) (x) + +/* HW Cursor Size */ +#define LCD_SPU_HWC_HPXL_VLN 0x0110 +#define CFG_HWC_VLN(y) ((y)<<16) +#define CFG_HWC_HPXL(x) (x) + +/* Total Screen Size Register */ +#define LCD_SPUT_V_H_TOTAL 0x0114 +#define CFG_V_TOTAL(y) ((y)<<16) +#define CFG_H_TOTAL(x) (x) + +/* Total Screen Active Size Register */ +#define LCD_SPU_V_H_ACTIVE 0x0118 +#define CFG_V_ACTIVE(y) ((y)<<16) +#define CFG_H_ACTIVE(x) (x) + +/* Screen H&V Porch Register */ +#define LCD_SPU_H_PORCH 0x011C +#define CFG_H_BACK_PORCH(b) ((b)<<16) +#define CFG_H_FRONT_PORCH(f) (f) +#define LCD_SPU_V_PORCH 0x0120 +#define CFG_V_BACK_PORCH(b) ((b)<<16) +#define CFG_V_FRONT_PORCH(f) (f) + +/* Screen Blank Color Register */ +#define LCD_SPU_BLANKCOLOR 0x0124 +#define CFG_BLANKCOLOR_MASK 0x00FFFFFF +#define CFG_BLANKCOLOR_R_MASK 0x000000FF +#define CFG_BLANKCOLOR_G_MASK 0x0000FF00 +#define CFG_BLANKCOLOR_B_MASK 0x00FF0000 + +/* HW Cursor Color 1&2 Register */ +#define LCD_SPU_ALPHA_COLOR1 0x0128 +#define CFG_HWC_COLOR1 0x00FFFFFF +#define CFG_HWC_COLOR1_R(red) ((red)<<16) +#define CFG_HWC_COLOR1_G(green) ((green)<<8) +#define CFG_HWC_COLOR1_B(blue) (blue) +#define CFG_HWC_COLOR1_R_MASK 0x000000FF +#define CFG_HWC_COLOR1_G_MASK 0x0000FF00 +#define CFG_HWC_COLOR1_B_MASK 0x00FF0000 +#define LCD_SPU_ALPHA_COLOR2 0x012C +#define CFG_HWC_COLOR2 0x00FFFFFF +#define CFG_HWC_COLOR2_R_MASK 0x000000FF +#define CFG_HWC_COLOR2_G_MASK 0x0000FF00 +#define CFG_HWC_COLOR2_B_MASK 0x00FF0000 + +/* Video YUV Color Key Control */ +#define LCD_SPU_COLORKEY_Y 0x0130 +#define CFG_CKEY_Y2(y2) ((y2)<<24) +#define CFG_CKEY_Y2_MASK 0xFF000000 +#define CFG_CKEY_Y1(y1) ((y1)<<16) +#define CFG_CKEY_Y1_MASK 0x00FF0000 +#define CFG_CKEY_Y(y) ((y)<<8) +#define CFG_CKEY_Y_MASK 0x0000FF00 +#define CFG_ALPHA_Y(y) (y) +#define CFG_ALPHA_Y_MASK 0x000000FF +#define LCD_SPU_COLORKEY_U 0x0134 +#define CFG_CKEY_U2(u2) ((u2)<<24) +#define CFG_CKEY_U2_MASK 0xFF000000 +#define CFG_CKEY_U1(u1) ((u1)<<16) +#define CFG_CKEY_U1_MASK 0x00FF0000 +#define CFG_CKEY_U(u) ((u)<<8) +#define CFG_CKEY_U_MASK 0x0000FF00 +#define CFG_ALPHA_U(u) (u) +#define CFG_ALPHA_U_MASK 0x000000FF +#define LCD_SPU_COLORKEY_V 0x0138 +#define CFG_CKEY_V2(v2) ((v2)<<24) +#define CFG_CKEY_V2_MASK 0xFF000000 +#define CFG_CKEY_V1(v1) ((v1)<<16) +#define CFG_CKEY_V1_MASK 0x00FF0000 +#define CFG_CKEY_V(v) ((v)<<8) +#define CFG_CKEY_V_MASK 0x0000FF00 +#define CFG_ALPHA_V(v) (v) +#define CFG_ALPHA_V_MASK 0x000000FF + +/* Graphics/Video DMA color key enable bits in LCD_TV_CTRL1 */ +#define CFG_CKEY_GRA 0x2 +#define CFG_CKEY_DMA 0x1 + +/* Interlace mode enable bits in LCD_TV_CTRL1 */ +#define CFG_TV_INTERLACE_EN (1 << 22) +#define CFG_TV_NIB (1 << 0) + +#define LCD_PN_SEPXLCNT 0x013c /* MMP2 */ + +/* SPI Read Data Register */ +#define LCD_SPU_SPI_RXDATA 0x0140 + +/* Smart Panel Read Data Register */ +#define LCD_SPU_ISA_RSDATA 0x0144 +#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000FF +#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000FF00 +#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00FF0000 +#define ISA_RXDATA_16BIT_4_DATA_MASK 0xFF000000 +#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00FFFFFF + +#define LCD_SPU_DBG_ISA (0x0148) /* TTC */ +#define LCD_SPU_DMAVLD_YC (0x014C) +#define LCD_SPU_DMAVLD_UV (0x0150) +#define LCD_SPU_DMAVLD_UVSPU_GRAVLD (0x0154) + +#define LCD_READ_IOPAD (0x0148) /* MMP2*/ +#define LCD_DMAVLD_YC (0x014C) +#define LCD_DMAVLD_UV (0x0150) +#define LCD_TVGGRAVLD_HLEN (0x0154) + +/* HWC SRAM Read Data Register */ +#define LCD_SPU_HWC_RDDAT 0x0158 + +/* Gamma Table SRAM Read Data Register */ +#define LCD_SPU_GAMMA_RDDAT 0x015c +#define CFG_GAMMA_RDDAT_MASK 0x000000FF + +/* Palette Table SRAM Read Data Register */ +#define LCD_SPU_PALETTE_RDDAT 0x0160 +#define CFG_PALETTE_RDDAT_MASK 0x00FFFFFF + +#define LCD_SPU_DBG_DMATOP (0x0164) /* TTC */ +#define LCD_SPU_DBG_GRATOP (0x0168) +#define LCD_SPU_DBG_TXCTRL (0x016C) +#define LCD_SPU_DBG_SLVTOP (0x0170) +#define LCD_SPU_DBG_MUXTOP (0x0174) + +#define LCD_SLV_DBG (0x0164) /* MMP2 */ +#define LCD_TVDVLD_YC (0x0168) +#define LCD_TVDVLD_UV (0x016C) +#define LCD_TVC_RDDAT (0x0170) +#define LCD_TV_GAMMA_RDDAT (0x0174) + +/* I/O Pads Input Read Only Register */ +#define LCD_SPU_IOPAD_IN 0x0178 +#define CFG_IOPAD_IN_MASK 0x0FFFFFFF + +#define LCD_TV_PALETTE_RDDAT (0x0178) /* MMP2 */ + +/* Reserved Read Only Registers */ +#define LCD_CFG_RDREG5F 0x017C +#define IRE_FRAME_CNT_MASK 0x000000C0 +#define IPE_FRAME_CNT_MASK 0x00000030 +#define GRA_FRAME_CNT_MASK 0x0000000C /* Graphic */ +#define DMA_FRAME_CNT_MASK 0x00000003 /* Video */ + +#define LCD_FRAME_CNT (0x017C) /* MMP2 */ + +/* SPI Control Register. */ +#define LCD_SPU_SPI_CTRL 0x0180 +#define CFG_SCLKCNT(div) ((div)<<24) /* 0xFF~0x2 */ +#define CFG_SCLKCNT_MASK 0xFF000000 +#define CFG_RXBITS(rx) (((rx) - 1)<<16) /* 0x1F~0x1 */ +#define CFG_RXBITS_MASK 0x00FF0000 +#define CFG_TXBITS(tx) (((tx) - 1)<<8) /* 0x1F~0x1 */ +#define CFG_TXBITS_MASK 0x0000FF00 +#define CFG_CLKINV(clk) ((clk)<<7) +#define CFG_CLKINV_MASK 0x00000080 +#define CFG_KEEPXFER(transfer) ((transfer)<<6) +#define CFG_KEEPXFER_MASK 0x00000040 +#define CFG_RXBITSTO0(rx) ((rx)<<5) +#define CFG_RXBITSTO0_MASK 0x00000020 +#define CFG_TXBITSTO0(tx) ((tx)<<4) +#define CFG_TXBITSTO0_MASK 0x00000010 +#define CFG_SPI_ENA(spi) ((spi)<<3) +#define CFG_SPI_ENA_MASK 0x00000008 +#define CFG_SPI_SEL(spi) ((spi)<<2) +#define CFG_SPI_SEL_MASK 0x00000004 +#define CFG_SPI_3W4WB(wire) ((wire)<<1) +#define CFG_SPI_3W4WB_MASK 0x00000002 +#define CFG_SPI_START(start) (start) +#define CFG_SPI_START_MASK 0x00000001 + +/* SPI Tx Data Register */ +#define LCD_SPU_SPI_TXDATA 0x0184 + +/* + 1. Smart Pannel 8-bit Bus Control Register. + 2. AHB Slave Path Data Port Register +*/ +#define LCD_SPU_SMPN_CTRL 0x0188 + +/* DMA Control 0 Register */ +#define LCD_SPU_DMA_CTRL0 0x0190 +#define CFG_NOBLENDING(nb) ((nb)<<31) +#define CFG_NOBLENDING_MASK 0x80000000 +#define CFG_GAMMA_ENA(gn) ((gn)<<30) +#define CFG_GAMMA_ENA_MASK 0x40000000 +#define CFG_CBSH_ENA(cn) ((cn)<<29) +#define CFG_CBSH_ENA_MASK 0x20000000 +#define CFG_PALETTE_ENA(pn) ((pn)<<28) +#define CFG_PALETTE_ENA_MASK 0x10000000 +#define CFG_ARBFAST_ENA(an) ((an)<<27) +#define CFG_ARBFAST_ENA_MASK 0x08000000 +#define CFG_HWC_1BITMOD(mode) ((mode)<<26) +#define CFG_HWC_1BITMOD_MASK 0x04000000 +#define CFG_HWC_1BITENA(mn) ((mn)<<25) +#define CFG_HWC_1BITENA_MASK 0x02000000 +#define CFG_HWC_ENA(cn) ((cn)<<24) +#define CFG_HWC_ENA_MASK 0x01000000 +#define CFG_DMAFORMAT(dmaformat) ((dmaformat)<<20) +#define CFG_DMAFORMAT_MASK 0x00F00000 +#define CFG_GRAFORMAT(graformat) ((graformat)<<16) +#define CFG_GRAFORMAT_MASK 0x000F0000 +/* for graphic part */ +#define CFG_GRA_FTOGGLE(toggle) ((toggle)<<15) +#define CFG_GRA_FTOGGLE_MASK 0x00008000 +#define CFG_GRA_HSMOOTH(smooth) ((smooth)<<14) +#define CFG_GRA_HSMOOTH_MASK 0x00004000 +#define CFG_GRA_TSTMODE(test) ((test)<<13) +#define CFG_GRA_TSTMODE_MASK 0x00002000 +#define CFG_GRA_SWAPRB(swap) ((swap)<<12) +#define CFG_GRA_SWAPRB_MASK 0x00001000 +#define CFG_GRA_SWAPUV(swap) ((swap)<<11) +#define CFG_GRA_SWAPUV_MASK 0x00000800 +#define CFG_GRA_SWAPYU(swap) ((swap)<<10) +#define CFG_GRA_SWAPYU_MASK 0x00000400 +#define CFG_GRA_SWAP_MASK 0x00001C00 +#define CFG_YUV2RGB_GRA(cvrt) ((cvrt)<<9) +#define CFG_YUV2RGB_GRA_MASK 0x00000200 +#define CFG_GRA_ENA(gra) ((gra)<<8) +#define CFG_GRA_ENA_MASK 0x00000100 +#define dma0_gfx_masks (CFG_GRAFORMAT_MASK | CFG_GRA_FTOGGLE_MASK | \ + CFG_GRA_HSMOOTH_MASK | CFG_GRA_TSTMODE_MASK | CFG_GRA_SWAP_MASK | \ + CFG_YUV2RGB_GRA_MASK | CFG_GRA_ENA_MASK) +/* for video part */ +#define CFG_DMA_FTOGGLE(toggle) ((toggle)<<7) +#define CFG_DMA_FTOGGLE_MASK 0x00000080 +#define CFG_DMA_HSMOOTH(smooth) ((smooth)<<6) +#define CFG_DMA_HSMOOTH_MASK 0x00000040 +#define CFG_DMA_TSTMODE(test) ((test)<<5) +#define CFG_DMA_TSTMODE_MASK 0x00000020 +#define CFG_DMA_SWAPRB(swap) ((swap)<<4) +#define CFG_DMA_SWAPRB_MASK 0x00000010 +#define CFG_DMA_SWAPUV(swap) ((swap)<<3) +#define CFG_DMA_SWAPUV_MASK 0x00000008 +#define CFG_DMA_SWAPYU(swap) ((swap)<<2) +#define CFG_DMA_SWAPYU_MASK 0x00000004 +#define CFG_DMA_SWAP_MASK 0x0000001C +#define CFG_YUV2RGB_DMA(cvrt) ((cvrt)<<1) +#define CFG_YUV2RGB_DMA_MASK 0x00000002 +#define CFG_DMA_ENA(video) (video) +#define CFG_DMA_ENA_MASK 0x00000001 +#define dma0_vid_masks (CFG_DMAFORMAT_MASK | CFG_DMA_FTOGGLE_MASK | \ + CFG_DMA_HSMOOTH_MASK | CFG_DMA_TSTMODE_MASK | CFG_DMA_SWAP_MASK | \ + CFG_YUV2RGB_DMA_MASK | CFG_DMA_ENA_MASK) +#define dma_palette(val) ((val ? 1 : 0) << 28) +#define dma_fmt(vid, val) ((val & 0xf) << ((vid) ? 20 : 16)) +#define dma_swaprb(vid, val) ((val ? 1 : 0) << ((vid) ? 4 : 12)) +#define dma_swapuv(vid, val) ((val ? 1 : 0) << ((vid) ? 3 : 11)) +#define dma_swapyuv(vid, val) ((val ? 1 : 0) << ((vid) ? 2 : 10)) +#define dma_csc(vid, val) ((val ? 1 : 0) << ((vid) ? 1 : 9)) +#define dma_hsmooth(vid, val) ((val ? 1 : 0) << ((vid) ? 6 : 14)) +#define dma_mask(vid) (dma_palette(1) | dma_fmt(vid, 0xf) | dma_csc(vid, 1) \ + | dma_swaprb(vid, 1) | dma_swapuv(vid, 1) | dma_swapyuv(vid, 1)) + +/* DMA Control 1 Register */ +#define LCD_SPU_DMA_CTRL1 0x0194 +#define CFG_FRAME_TRIG(trig) ((trig)<<31) +#define CFG_FRAME_TRIG_MASK 0x80000000 +#define CFG_VSYNC_TRIG(trig) ((trig)<<28) +#define CFG_VSYNC_TRIG_MASK 0x70000000 +#define CFG_VSYNC_INV(inv) ((inv)<<27) +#define CFG_VSYNC_INV_MASK 0x08000000 +#define CFG_COLOR_KEY_MODE(cmode) ((cmode)<<24) +#define CFG_COLOR_KEY_MASK 0x07000000 +#define CFG_CARRY(carry) ((carry)<<23) +#define CFG_CARRY_MASK 0x00800000 +#define CFG_LNBUF_ENA(lnbuf) ((lnbuf)<<22) +#define CFG_LNBUF_ENA_MASK 0x00400000 +#define CFG_GATED_ENA(gated) ((gated)<<21) +#define CFG_GATED_ENA_MASK 0x00200000 +#define CFG_PWRDN_ENA(power) ((power)<<20) +#define CFG_PWRDN_ENA_MASK 0x00100000 +#define CFG_DSCALE(dscale) ((dscale)<<18) +#define CFG_DSCALE_MASK 0x000C0000 +#define CFG_ALPHA_MODE(amode) ((amode)<<16) +#define CFG_ALPHA_MODE_MASK 0x00030000 +#define CFG_ALPHA(alpha) ((alpha)<<8) +#define CFG_ALPHA_MASK 0x0000FF00 +#define CFG_PXLCMD(pxlcmd) (pxlcmd) +#define CFG_PXLCMD_MASK 0x000000FF + +/* SRAM Control Register */ +#define LCD_SPU_SRAM_CTRL 0x0198 +#define CFG_SRAM_INIT_WR_RD(mode) ((mode)<<14) +#define CFG_SRAM_INIT_WR_RD_MASK 0x0000C000 +#define CFG_SRAM_ADDR_LCDID(id) ((id)<<8) +#define CFG_SRAM_ADDR_LCDID_MASK 0x00000F00 +#define CFG_SRAM_ADDR(addr) (addr) +#define CFG_SRAM_ADDR_MASK 0x000000FF + +/* SRAM Write Data Register */ +#define LCD_SPU_SRAM_WRDAT 0x019C + +/* SRAM RTC/WTC Control Register */ +#define LCD_SPU_SRAM_PARA0 0x01A0 + +/* SRAM Power Down Control Register */ +#define LCD_SPU_SRAM_PARA1 0x01A4 +#define CFG_CSB_256x32(hwc) ((hwc)<<15) /* HWC */ +#define CFG_CSB_256x32_MASK 0x00008000 +#define CFG_CSB_256x24(palette) ((palette)<<14) /* Palette */ +#define CFG_CSB_256x24_MASK 0x00004000 +#define CFG_CSB_256x8(gamma) ((gamma)<<13) /* Gamma */ +#define CFG_CSB_256x8_MASK 0x00002000 +#define CFG_PDWN256x32(pdwn) ((pdwn)<<7) /* HWC */ +#define CFG_PDWN256x32_MASK 0x00000080 +#define CFG_PDWN256x24(pdwn) ((pdwn)<<6) /* Palette */ +#define CFG_PDWN256x24_MASK 0x00000040 +#define CFG_PDWN256x8(pdwn) ((pdwn)<<5) /* Gamma */ +#define CFG_PDWN256x8_MASK 0x00000020 +#define CFG_PDWN32x32(pdwn) ((pdwn)<<3) +#define CFG_PDWN32x32_MASK 0x00000008 +#define CFG_PDWN16x66(pdwn) ((pdwn)<<2) +#define CFG_PDWN16x66_MASK 0x00000004 +#define CFG_PDWN32x66(pdwn) ((pdwn)<<1) +#define CFG_PDWN32x66_MASK 0x00000002 +#define CFG_PDWN64x66(pdwn) (pdwn) +#define CFG_PDWN64x66_MASK 0x00000001 + +/* Smart or Dumb Panel Clock Divider */ +#define LCD_CFG_SCLK_DIV 0x01A8 +#define SCLK_SRC_SEL(src) ((src)<<31) +#define SCLK_SRC_SEL_MASK 0x80000000 +#define SCLK_DISABLE (1<<28) +#define CLK_FRACDIV(frac) ((frac)<<16) +#define CLK_FRACDIV_MASK 0x0FFF0000 +#define DSI1_BITCLK_DIV(div) (div<<8) +#define DSI1_BITCLK_DIV_MASK 0x00000F00 +#define CLK_INT_DIV(div) (div) +#define CLK_INT_DIV_MASK 0x000000FF + +/* Video Contrast Register */ +#define LCD_SPU_CONTRAST 0x01AC +#define CFG_BRIGHTNESS(bright) ((bright)<<16) +#define CFG_BRIGHTNESS_MASK 0xFFFF0000 +#define CFG_CONTRAST(contrast) (contrast) +#define CFG_CONTRAST_MASK 0x0000FFFF + +/* Video Saturation Register */ +#define LCD_SPU_SATURATION 0x01B0 +#define CFG_C_MULTS(mult) ((mult)<<16) +#define CFG_C_MULTS_MASK 0xFFFF0000 +#define CFG_SATURATION(sat) (sat) +#define CFG_SATURATION_MASK 0x0000FFFF + +/* Video Hue Adjust Register */ +#define LCD_SPU_CBSH_HUE 0x01B4 +#define CFG_SIN0(sin0) ((sin0)<<16) +#define CFG_SIN0_MASK 0xFFFF0000 +#define CFG_COS0(con0) (con0) +#define CFG_COS0_MASK 0x0000FFFF + +/* Dump LCD Panel Control Register */ +#define LCD_SPU_DUMB_CTRL 0x01B8 +#define CFG_DUMBMODE(mode) ((mode)<<28) +#define CFG_DUMBMODE_MASK 0xF0000000 +#define CFG_LCDGPIO_O(data) ((data)<<20) +#define CFG_LCDGPIO_O_MASK 0x0FF00000 +#define CFG_LCDGPIO_ENA(gpio) ((gpio)<<12) +#define CFG_LCDGPIO_ENA_MASK 0x000FF000 +#define CFG_BIAS_OUT(bias) ((bias)<<8) +#define CFG_BIAS_OUT_MASK 0x00000100 +#define CFG_REVERSE_RGB(RGB) ((RGB)<<7) +#define CFG_REVERSE_RGB_MASK 0x00000080 +#define CFG_INV_COMPBLANK(blank) ((blank)<<6) +#define CFG_INV_COMPBLANK_MASK 0x00000040 +#define CFG_INV_COMPSYNC(sync) ((sync)<<5) +#define CFG_INV_COMPSYNC_MASK 0x00000020 +#define CFG_INV_HENA(hena) ((hena)<<4) +#define CFG_INV_HENA_MASK 0x00000010 +#define CFG_INV_VSYNC(vsync) ((vsync)<<3) +#define CFG_INV_VSYNC_MASK 0x00000008 +#define CFG_INV_HSYNC(hsync) ((hsync)<<2) +#define CFG_INV_HSYNC_MASK 0x00000004 +#define CFG_INV_PCLK(pclk) ((pclk)<<1) +#define CFG_INV_PCLK_MASK 0x00000002 +#define CFG_DUMB_ENA(dumb) (dumb) +#define CFG_DUMB_ENA_MASK 0x00000001 + +/* LCD I/O Pads Control Register */ +#define SPU_IOPAD_CONTROL 0x01BC +#define CFG_GRA_VM_ENA(vm) ((vm)<<15) +#define CFG_GRA_VM_ENA_MASK 0x00008000 +#define CFG_DMA_VM_ENA(vm) ((vm)<<13) +#define CFG_DMA_VM_ENA_MASK 0x00002000 +#define CFG_CMD_VM_ENA(vm) ((vm)<<12) +#define CFG_CMD_VM_ENA_MASK 0x00001000 +#define CFG_CSC(csc) ((csc)<<8) +#define CFG_CSC_MASK 0x00000300 +#define CFG_BOUNDARY(size) ((size)<<5) +#define CFG_BOUNDARY_MASK 0x00000020 +#define CFG_BURST(len) ((len)<<4) +#define CFG_BURST_MASK 0x00000010 +#define CFG_IOPADMODE(iopad) (iopad) +#define CFG_IOPADMODE_MASK 0x0000000F + +/* LCD Interrupt Control Register */ +#define SPU_IRQ_ENA 0x01C0 +#define DMA_FRAME_IRQ0_ENA(irq) ((irq)<<31) +#define DMA_FRAME_IRQ0_ENA_MASK 0x80000000 +#define DMA_FRAME_IRQ1_ENA(irq) ((irq)<<30) +#define DMA_FRAME_IRQ1_ENA_MASK 0x40000000 +#define DMA_FF_UNDERFLOW_ENA(ff) ((ff)<<29) +#define DMA_FF_UNDERFLOW_ENA_MASK 0x20000000 +#define AXI_BUS_ERROR_IRQ_ENA(irq) ((irq)<<28) +#define AXI_BUS_ERROR_IRQ_ENA_MASK 0x10000000 +#define GRA_FRAME_IRQ0_ENA(irq) ((irq)<<27) +#define GRA_FRAME_IRQ0_ENA_MASK 0x08000000 +#define GRA_FRAME_IRQ1_ENA(irq) ((irq)<<26) +#define GRA_FRAME_IRQ1_ENA_MASK 0x04000000 +#define GRA_FF_UNDERFLOW_ENA(ff) ((ff)<<25) +#define GRA_FF_UNDERFLOW_ENA_MASK 0x02000000 +#define VSYNC_IRQ_ENA(vsync_irq) ((vsync_irq)<<23) +#define VSYNC_IRQ_ENA_MASK 0x00800000 +#define DUMB_FRAMEDONE_ENA(fdone) ((fdone)<<22) +#define DUMB_FRAMEDONE_ENA_MASK 0x00400000 +#define TWC_FRAMEDONE_ENA(fdone) ((fdone)<<21) +#define TWC_FRAMEDONE_ENA_MASK 0x00200000 +#define HWC_FRAMEDONE_ENA(fdone) ((fdone)<<20) +#define HWC_FRAMEDONE_ENA_MASK 0x00100000 +#define SLV_IRQ_ENA(irq) ((irq)<<19) +#define SLV_IRQ_ENA_MASK 0x00080000 +#define SPI_IRQ_ENA(irq) ((irq)<<18) +#define SPI_IRQ_ENA_MASK 0x00040000 +#define PWRDN_IRQ_ENA(irq) ((irq)<<17) +#define PWRDN_IRQ_ENA_MASK 0x00020000 +#define AXI_LATENCY_TOO_LONG_IRQ_ENA(irq) ((irq)<<16) +#define AXI_LATENCY_TOO_LONG_IRQ_ENA_MASK 0x00010000 +#define CLEAN_SPU_IRQ_ISR(irq) (irq) +#define CLEAN_SPU_IRQ_ISR_MASK 0x0000FFFF +#define TV_DMA_FRAME_IRQ0_ENA(irq) ((irq)<<15) +#define TV_DMA_FRAME_IRQ0_ENA_MASK 0x00008000 +#define TV_DMA_FRAME_IRQ1_ENA(irq) ((irq)<<14) +#define TV_DMA_FRAME_IRQ1_ENA_MASK 0x00004000 +#define TV_DMA_FF_UNDERFLOW_ENA(unerrun) ((unerrun)<<13) +#define TV_DMA_FF_UNDERFLOW_ENA_MASK 0x00002000 +#define TVSYNC_IRQ_ENA(irq) ((irq)<<12) +#define TVSYNC_IRQ_ENA_MASK 0x00001000 +#define TV_FRAME_IRQ0_ENA(irq) ((irq)<<11) +#define TV_FRAME_IRQ0_ENA_MASK 0x00000800 +#define TV_FRAME_IRQ1_ENA(irq) ((irq)<<10) +#define TV_FRAME_IRQ1_ENA_MASK 0x00000400 +#define TV_GRA_FF_UNDERFLOW_ENA(unerrun) ((unerrun)<<9) +#define TV_GRA_FF_UNDERFLOW_ENA_MASK 0x00000200 +#define TV_FRAMEDONE_ENA(irq) ((irq)<<8) +#define TV_FRAMEDONE_ENA_MASK 0x00000100 + +/* FIXME - JUST GUESS */ +#define PN2_DMA_FRAME_IRQ0_ENA(irq) ((irq)<<7) +#define PN2_DMA_FRAME_IRQ0_ENA_MASK 0x00000080 +#define PN2_DMA_FRAME_IRQ1_ENA(irq) ((irq)<<6) +#define PN2_DMA_FRAME_IRQ1_ENA_MASK 0x00000040 +#define PN2_DMA_FF_UNDERFLOW_ENA(ff) ((ff)<<5) +#define PN2_DMA_FF_UNDERFLOW_ENA_MASK 0x00000020 +#define PN2_GRA_FRAME_IRQ0_ENA(irq) ((irq)<<3) +#define PN2_GRA_FRAME_IRQ0_ENA_MASK 0x00000008 +#define PN2_GRA_FRAME_IRQ1_ENA(irq) ((irq)<<2) +#define PN2_GRA_FRAME_IRQ1_ENA_MASK 0x04000004 +#define PN2_GRA_FF_UNDERFLOW_ENA(ff) ((ff)<<1) +#define PN2_GRA_FF_UNDERFLOW_ENA_MASK 0x00000002 +#define PN2_VSYNC_IRQ_ENA(irq) ((irq)<<0) +#define PN2_SYNC_IRQ_ENA_MASK 0x00000001 + +#define gf0_imask(id) ((id) ? (((id) & 1) ? TV_FRAME_IRQ0_ENA_MASK \ + : PN2_GRA_FRAME_IRQ0_ENA_MASK) : GRA_FRAME_IRQ0_ENA_MASK) +#define gf1_imask(id) ((id) ? (((id) & 1) ? TV_FRAME_IRQ1_ENA_MASK \ + : PN2_GRA_FRAME_IRQ1_ENA_MASK) : GRA_FRAME_IRQ1_ENA_MASK) +#define vsync_imask(id) ((id) ? (((id) & 1) ? TVSYNC_IRQ_ENA_MASK \ + : PN2_SYNC_IRQ_ENA_MASK) : VSYNC_IRQ_ENA_MASK) +#define vsync_imasks (vsync_imask(0) | vsync_imask(1)) + +#define display_done_imask(id) ((id) ? (((id) & 1) ? TV_FRAMEDONE_ENA_MASK\ + : (PN2_DMA_FRAME_IRQ0_ENA_MASK | PN2_DMA_FRAME_IRQ1_ENA_MASK))\ + : DUMB_FRAMEDONE_ENA_MASK) + +#define display_done_imasks (display_done_imask(0) | display_done_imask(1)) + +#define vf0_imask(id) ((id) ? (((id) & 1) ? TV_DMA_FRAME_IRQ0_ENA_MASK \ + : PN2_DMA_FRAME_IRQ0_ENA_MASK) : DMA_FRAME_IRQ0_ENA_MASK) +#define vf1_imask(id) ((id) ? (((id) & 1) ? TV_DMA_FRAME_IRQ1_ENA_MASK \ + : PN2_DMA_FRAME_IRQ1_ENA_MASK) : DMA_FRAME_IRQ1_ENA_MASK) + +#define gfx_imasks (gf0_imask(0) | gf1_imask(0) | gf0_imask(1) | \ + gf1_imask(1)) +#define vid_imasks (vf0_imask(0) | vf1_imask(0) | vf0_imask(1) | \ + vf1_imask(1)) +#define vid_imask(id) (display_done_imask(id)) + +#define pn1_imasks (gf0_imask(0) | gf1_imask(0) | vsync_imask(0) | \ + display_done_imask(0) | vf0_imask(0) | vf1_imask(0)) +#define tv_imasks (gf0_imask(1) | gf1_imask(1) | vsync_imask(1) | \ + display_done_imask(1) | vf0_imask(1) | vf1_imask(1)) +#define path_imasks(id) ((id) ? (tv_imasks) : (pn1_imasks)) + +/* error indications */ +#define vid_udflow_imask(id) ((id) ? (((id) & 1) ? \ + (TV_DMA_FF_UNDERFLOW_ENA_MASK) : (PN2_DMA_FF_UNDERFLOW_ENA_MASK)) : \ + (DMA_FF_UNDERFLOW_ENA_MASK)) +#define gfx_udflow_imask(id) ((id) ? (((id) & 1) ? \ + (TV_GRA_FF_UNDERFLOW_ENA_MASK) : (PN2_GRA_FF_UNDERFLOW_ENA_MASK)) : \ + (GRA_FF_UNDERFLOW_ENA_MASK)) + +#define err_imask(id) (vid_udflow_imask(id) | gfx_udflow_imask(id) | \ + AXI_BUS_ERROR_IRQ_ENA_MASK | AXI_LATENCY_TOO_LONG_IRQ_ENA_MASK) +#define err_imasks (err_imask(0) | err_imask(1) | err_imask(2)) +/* LCD Interrupt Status Register */ +#define SPU_IRQ_ISR 0x01C4 +#define DMA_FRAME_IRQ0(irq) ((irq)<<31) +#define DMA_FRAME_IRQ0_MASK 0x80000000 +#define DMA_FRAME_IRQ1(irq) ((irq)<<30) +#define DMA_FRAME_IRQ1_MASK 0x40000000 +#define DMA_FF_UNDERFLOW(ff) ((ff)<<29) +#define DMA_FF_UNDERFLOW_MASK 0x20000000 +#define AXI_BUS_ERROR_IRQ(irq) ((irq)<<28) +#define AXI_BUS_ERROR_IRQ_MASK 0x10000000 +#define GRA_FRAME_IRQ0(irq) ((irq)<<27) +#define GRA_FRAME_IRQ0_MASK 0x08000000 +#define GRA_FRAME_IRQ1(irq) ((irq)<<26) +#define GRA_FRAME_IRQ1_MASK 0x04000000 +#define GRA_FF_UNDERFLOW(ff) ((ff)<<25) +#define GRA_FF_UNDERFLOW_MASK 0x02000000 +#define VSYNC_IRQ(vsync_irq) ((vsync_irq)<<23) +#define VSYNC_IRQ_MASK 0x00800000 +#define DUMB_FRAMEDONE(fdone) ((fdone)<<22) +#define DUMB_FRAMEDONE_MASK 0x00400000 +#define TWC_FRAMEDONE(fdone) ((fdone)<<21) +#define TWC_FRAMEDONE_MASK 0x00200000 +#define HWC_FRAMEDONE(fdone) ((fdone)<<20) +#define HWC_FRAMEDONE_MASK 0x00100000 +#define SLV_IRQ(irq) ((irq)<<19) +#define SLV_IRQ_MASK 0x00080000 +#define SPI_IRQ(irq) ((irq)<<18) +#define SPI_IRQ_MASK 0x00040000 +#define PWRDN_IRQ(irq) ((irq)<<17) +#define PWRDN_IRQ_MASK 0x00020000 +#define AXI_LATENCY_TOO_LONGR_IRQ(irq) ((irq)<<16) +#define AXI_LATENCY_TOO_LONGR_IRQ_MASK 0x00010000 +#define TV_DMA_FRAME_IRQ0(irq) ((irq)<<15) +#define TV_DMA_FRAME_IRQ0_MASK 0x00008000 +#define TV_DMA_FRAME_IRQ1(irq) ((irq)<<14) +#define TV_DMA_FRAME_IRQ1_MASK 0x00004000 +#define TV_DMA_FF_UNDERFLOW(unerrun) ((unerrun)<<13) +#define TV_DMA_FF_UNDERFLOW_MASK 0x00002000 +#define TVSYNC_IRQ(irq) ((irq)<<12) +#define TVSYNC_IRQ_MASK 0x00001000 +#define TV_FRAME_IRQ0(irq) ((irq)<<11) +#define TV_FRAME_IRQ0_MASK 0x00000800 +#define TV_FRAME_IRQ1(irq) ((irq)<<10) +#define TV_FRAME_IRQ1_MASK 0x00000400 +#define TV_GRA_FF_UNDERFLOW(unerrun) ((unerrun)<<9) +#define TV_GRA_FF_UNDERFLOW_MASK 0x00000200 +#define PN2_DMA_FRAME_IRQ0(irq) ((irq)<<7) +#define PN2_DMA_FRAME_IRQ0_MASK 0x00000080 +#define PN2_DMA_FRAME_IRQ1(irq) ((irq)<<6) +#define PN2_DMA_FRAME_IRQ1_MASK 0x00000040 +#define PN2_DMA_FF_UNDERFLOW(ff) ((ff)<<5) +#define PN2_DMA_FF_UNDERFLOW_MASK 0x00000020 +#define PN2_GRA_FRAME_IRQ0(irq) ((irq)<<3) +#define PN2_GRA_FRAME_IRQ0_MASK 0x00000008 +#define PN2_GRA_FRAME_IRQ1(irq) ((irq)<<2) +#define PN2_GRA_FRAME_IRQ1_MASK 0x04000004 +#define PN2_GRA_FF_UNDERFLOW(ff) ((ff)<<1) +#define PN2_GRA_FF_UNDERFLOW_MASK 0x00000002 +#define PN2_VSYNC_IRQ(irq) ((irq)<<0) +#define PN2_SYNC_IRQ_MASK 0x00000001 + +/* LCD FIFO Depth register */ +#define LCD_FIFO_DEPTH 0x01c8 +#define VIDEO_FIFO(fi) ((fi) << 0) +#define VIDEO_FIFO_MASK 0x00000003 +#define GRAPHIC_FIFO(fi) ((fi) << 2) +#define GRAPHIC_FIFO_MASK 0x0000000c + +/* read-only */ +#define DMA_FRAME_IRQ0_LEVEL_MASK 0x00008000 +#define DMA_FRAME_IRQ1_LEVEL_MASK 0x00004000 +#define DMA_FRAME_CNT_ISR_MASK 0x00003000 +#define GRA_FRAME_IRQ0_LEVEL_MASK 0x00000800 +#define GRA_FRAME_IRQ1_LEVEL_MASK 0x00000400 +#define GRA_FRAME_CNT_ISR_MASK 0x00000300 +#define VSYNC_IRQ_LEVEL_MASK 0x00000080 +#define DUMB_FRAMEDONE_LEVEL_MASK 0x00000040 +#define TWC_FRAMEDONE_LEVEL_MASK 0x00000020 +#define HWC_FRAMEDONE_LEVEL_MASK 0x00000010 +#define SLV_FF_EMPTY_MASK 0x00000008 +#define DMA_FF_ALLEMPTY_MASK 0x00000004 +#define GRA_FF_ALLEMPTY_MASK 0x00000002 +#define PWRDN_IRQ_LEVEL_MASK 0x00000001 + +/* 32 bit LCD Interrupt Reset Status*/ +#define SPU_IRQ_RSR (0x01C8) +/* 32 bit Panel Path Graphic Partial Display Horizontal Control Register*/ +#define LCD_GRA_CUTHPXL (0x01CC) +/* 32 bit Panel Path Graphic Partial Display Vertical Control Register*/ +#define LCD_GRA_CUTVLN (0x01D0) +/* 32 bit TV Path Graphic Partial Display Horizontal Control Register*/ +#define LCD_TVG_CUTHPXL (0x01D4) +/* 32 bit TV Path Graphic Partial Display Vertical Control Register*/ +#define LCD_TVG_CUTVLN (0x01D8) +/* 32 bit LCD Global Control Register*/ +#define LCD_TOP_CTRL (0x01DC) +/* 32 bit LCD SQU Line Buffer Control Register 1*/ +#define LCD_SQULN1_CTRL (0x01E0) +/* 32 bit LCD SQU Line Buffer Control Register 2*/ +#define LCD_SQULN2_CTRL (0x01E4) +#define squln_ctrl(id) ((id) ? (((id) & 1) ? LCD_SQULN2_CTRL : \ + LCD_PN2_SQULN1_CTRL) : LCD_SQULN1_CTRL) + +/* 32 bit LCD Mixed Overlay Control Register */ +#define LCD_AFA_ALL2ONE (0x01E8) + +#define LCD_PN2_SCLK_DIV (0x01EC) +#define LCD_PN2_TCLK_DIV (0x01F0) +#define LCD_LVDS_SCLK_DIV_WR (0x01F4) +#define LCD_LVDS_SCLK_DIV_RD (0x01FC) +#define PN2_LCD_DMA_START_ADDR_Y0 (0x0200) +#define PN2_LCD_DMA_START_ADDR_U0 (0x0204) +#define PN2_LCD_DMA_START_ADDR_V0 (0x0208) +#define PN2_LCD_DMA_START_ADDR_C0 (0x020C) +#define PN2_LCD_DMA_START_ADDR_Y1 (0x0210) +#define PN2_LCD_DMA_START_ADDR_U1 (0x0214) +#define PN2_LCD_DMA_START_ADDR_V1 (0x0218) +#define PN2_LCD_DMA_START_ADDR_C1 (0x021C) +#define PN2_LCD_DMA_PITCH_YC (0x0220) +#define PN2_LCD_DMA_PITCH_UV (0x0224) +#define PN2_LCD_DMA_OVSA_HPXL_VLN (0x0228) +#define PN2_LCD_DMA_HPXL_VLN (0x022C) +#define PN2_LCD_DMAZM_HPXL_VLN (0x0230) +#define PN2_LCD_GRA_START_ADDR0 (0x0234) +#define PN2_LCD_GRA_START_ADDR1 (0x0238) +#define PN2_LCD_GRA_PITCH (0x023C) +#define PN2_LCD_GRA_OVSA_HPXL_VLN (0x0240) +#define PN2_LCD_GRA_HPXL_VLN (0x0244) +#define PN2_LCD_GRAZM_HPXL_VLN (0x0248) +#define PN2_LCD_HWC_OVSA_HPXL_VLN (0x024C) +#define PN2_LCD_HWC_HPXL_VLN (0x0250) +#define LCD_PN2_V_H_TOTAL (0x0254) +#define LCD_PN2_V_H_ACTIVE (0x0258) +#define LCD_PN2_H_PORCH (0x025C) +#define LCD_PN2_V_PORCH (0x0260) +#define LCD_PN2_BLANKCOLOR (0x0264) +#define LCD_PN2_ALPHA_COLOR1 (0x0268) +#define LCD_PN2_ALPHA_COLOR2 (0x026C) +#define LCD_PN2_COLORKEY_Y (0x0270) +#define LCD_PN2_COLORKEY_U (0x0274) +#define LCD_PN2_COLORKEY_V (0x0278) +#define LCD_PN2_SEPXLCNT (0x027C) +#define LCD_TV_V_H_TOTAL_FLD (0x0280) +#define LCD_TV_V_PORCH_FLD (0x0284) +#define LCD_TV_SEPXLCNT_FLD (0x0288) + +#define LCD_2ND_ALPHA (0x0294) +#define LCD_PN2_CONTRAST (0x0298) +#define LCD_PN2_SATURATION (0x029c) +#define LCD_PN2_CBSH_HUE (0x02a0) +#define LCD_TIMING_EXT (0x02C0) +#define LCD_PN2_LAYER_ALPHA_SEL1 (0x02c4) +#define LCD_PN2_CTRL0 (0x02C8) +#define TV_LAYER_ALPHA_SEL1 (0x02cc) +#define LCD_SMPN2_CTRL (0x02D0) +#define LCD_IO_OVERL_MAP_CTRL (0x02D4) +#define LCD_DUMB2_CTRL (0x02d8) +#define LCD_PN2_CTRL1 (0x02DC) +#define PN2_IOPAD_CONTROL (0x02E0) +#define LCD_PN2_SQULN1_CTRL (0x02E4) +#define PN2_LCD_GRA_CUTHPXL (0x02e8) +#define PN2_LCD_GRA_CUTVLN (0x02ec) +#define LCD_PN2_SQULN2_CTRL (0x02F0) +#define ALL_LAYER_ALPHA_SEL (0x02F4) + +/* pxa988 has different MASTER_CTRL from MMP3/MMP2 */ +#ifdef CONFIG_CPU_PXA988 +#define TIMING_MASTER_CONTROL (0x01F4) +#define MASTER_ENH(id) (1 << ((id) + 5)) +#define MASTER_ENV(id) (1 << ((id) + 6)) +#else +#define TIMING_MASTER_CONTROL (0x02F8) +#define MASTER_ENH(id) (1 << (id)) +#define MASTER_ENV(id) (1 << ((id) + 4)) +#endif + +#define DSI_START_SEL_SHIFT(id) (((id) << 1) + 8) +#define timing_master_config(path, dsi_id, lcd_id) \ + (MASTER_ENH(path) | MASTER_ENV(path) | \ + (((lcd_id) + ((dsi_id) << 1)) << DSI_START_SEL_SHIFT(path))) + +#define LCD_2ND_BLD_CTL (0x02Fc) +#define LVDS_SRC_MASK (3 << 30) +#define LVDS_SRC_SHIFT (30) +#define LVDS_FMT_MASK (1 << 28) +#define LVDS_FMT_SHIFT (28) + +#define CLK_SCLK (1 << 0) +#define CLK_LVDS_RD (1 << 1) +#define CLK_LVDS_WR (1 << 2) + +#define gra_partdisp_ctrl_hor(id) ((id) ? (((id) & 1) ? \ + LCD_TVG_CUTHPXL : PN2_LCD_GRA_CUTHPXL) : LCD_GRA_CUTHPXL) +#define gra_partdisp_ctrl_ver(id) ((id) ? (((id) & 1) ? \ + LCD_TVG_CUTVLN : PN2_LCD_GRA_CUTVLN) : LCD_GRA_CUTVLN) + +/* + * defined Video Memory Color format for DMA control 0 register + * DMA0 bit[23:20] + */ +#define VMODE_RGB565 0x0 +#define VMODE_RGB1555 0x1 +#define VMODE_RGB888PACKED 0x2 +#define VMODE_RGB888UNPACKED 0x3 +#define VMODE_RGBA888 0x4 +#define VMODE_YUV422PACKED 0x5 +#define VMODE_YUV422PLANAR 0x6 +#define VMODE_YUV420PLANAR 0x7 +#define VMODE_SMPNCMD 0x8 +#define VMODE_PALETTE4BIT 0x9 +#define VMODE_PALETTE8BIT 0xa +#define VMODE_RESERVED 0xb + +/* + * defined Graphic Memory Color format for DMA control 0 register + * DMA0 bit[19:16] + */ +#define GMODE_RGB565 0x0 +#define GMODE_RGB1555 0x1 +#define GMODE_RGB888PACKED 0x2 +#define GMODE_RGB888UNPACKED 0x3 +#define GMODE_RGBA888 0x4 +#define GMODE_YUV422PACKED 0x5 +#define GMODE_YUV422PLANAR 0x6 +#define GMODE_YUV420PLANAR 0x7 +#define GMODE_SMPNCMD 0x8 +#define GMODE_PALETTE4BIT 0x9 +#define GMODE_PALETTE8BIT 0xa +#define GMODE_RESERVED 0xb + +/* + * define for DMA control 1 register + */ +#define DMA1_FRAME_TRIG 31 /* bit location */ +#define DMA1_VSYNC_MODE 28 +#define DMA1_VSYNC_INV 27 +#define DMA1_CKEY 24 +#define DMA1_CARRY 23 +#define DMA1_LNBUF_ENA 22 +#define DMA1_GATED_ENA 21 +#define DMA1_PWRDN_ENA 20 +#define DMA1_DSCALE 18 +#define DMA1_ALPHA_MODE 16 +#define DMA1_ALPHA 08 +#define DMA1_PXLCMD 00 + +/* + * defined for Configure Dumb Mode + * DUMB LCD Panel bit[31:28] + */ +#define DUMB16_RGB565_0 0x0 +#define DUMB16_RGB565_1 0x1 +#define DUMB18_RGB666_0 0x2 +#define DUMB18_RGB666_1 0x3 +#define DUMB12_RGB444_0 0x4 +#define DUMB12_RGB444_1 0x5 +#define DUMB24_RGB888_0 0x6 +#define DUMB_BLANK 0x7 + +/* + * defined for Configure I/O Pin Allocation Mode + * LCD LCD I/O Pads control register bit[3:0] + */ +#define IOPAD_DUMB24 0x0 +#define IOPAD_DUMB18SPI 0x1 +#define IOPAD_DUMB18GPIO 0x2 +#define IOPAD_DUMB16SPI 0x3 +#define IOPAD_DUMB16GPIO 0x4 +#define IOPAD_DUMB12 0x5 +#define IOPAD_SMART18SPI 0x6 +#define IOPAD_SMART16SPI 0x7 +#define IOPAD_SMART8BOTH 0x8 +#define IOPAD_DUMB18_SMART8 0x9 +#define IOPAD_DUMB16_SMART8SPI 0xa +#define IOPAD_DUMB16_SMART8GPIO 0xb +#define IOPAD_DUMB16_DUMB16 0xc +#define IOPAD_SMART8_SMART8 0xc + +/* + *defined for indicating boundary and cycle burst length + */ +#define CFG_BOUNDARY_1KB (1<<5) +#define CFG_BOUNDARY_4KB (0<<5) +#define CFG_CYC_BURST_LEN16 (1<<4) +#define CFG_CYC_BURST_LEN8 (0<<4) + +/* + * defined Dumb Panel Clock Divider register + * SCLK_Source bit[31] + */ + /* 0: PLL clock select*/ +#define AXI_BUS_SEL 0x80000000 +#define CCD_CLK_SEL 0x40000000 +#define DCON_CLK_SEL 0x20000000 +#define ENA_CLK_INT_DIV CONFIG_FB_DOVE_CLCD_SCLK_DIV +#define IDLE_CLK_INT_DIV 0x1 /* idle Integer Divider */ +#define DIS_CLK_INT_DIV 0x0 /* Disable Integer Divider */ + +/* SRAM ID */ +#define SRAMID_GAMMA_YR 0x0 +#define SRAMID_GAMMA_UG 0x1 +#define SRAMID_GAMMA_VB 0x2 +#define SRAMID_PALATTE 0x3 +#define SRAMID_HWC 0xf + +/* SRAM INIT Read/Write */ +#define SRAMID_INIT_READ 0x0 +#define SRAMID_INIT_WRITE 0x2 +#define SRAMID_INIT_DEFAULT 0x3 + +/* + * defined VSYNC selection mode for DMA control 1 register + * DMA1 bit[30:28] + */ +#define VMODE_SMPN 0x0 +#define VMODE_SMPNIRQ 0x1 +#define VMODE_DUMB 0x2 +#define VMODE_IPE 0x3 +#define VMODE_IRE 0x4 + +/* + * defined Configure Alpha and Alpha mode for DMA control 1 register + * DMA1 bit[15:08](alpha) / bit[17:16](alpha mode) + */ +/* ALPHA mode */ +#define MODE_ALPHA_DMA 0x0 +#define MODE_ALPHA_GRA 0x1 +#define MODE_ALPHA_CFG 0x2 + +/* alpha value */ +#define ALPHA_NOGRAPHIC 0xFF /* all video, no graphic */ +#define ALPHA_NOVIDEO 0x00 /* all graphic, no video */ +#define ALPHA_GRAPHNVIDEO 0x0F /* Selects graphic & video */ + +/* + * defined Pixel Command for DMA control 1 register + * DMA1 bit[07:00] + */ +#define PIXEL_CMD 0x81 + +/* DSI */ +/* DSI1 - 4 Lane Controller base */ +#define DSI1_REGS_PHYSICAL_BASE 0xD420B800 +/* DSI2 - 3 Lane Controller base */ +#define DSI2_REGS_PHYSICAL_BASE 0xD420BA00 + +/* DSI Controller Registers */ +struct dsi_lcd_regs { +#define DSI_LCD1_CTRL_0 0x100 /* DSI Active Panel 1 Control register 0 */ +#define DSI_LCD1_CTRL_1 0x104 /* DSI Active Panel 1 Control register 1 */ + u32 ctrl0; + u32 ctrl1; + u32 reserved1[2]; + +#define DSI_LCD1_TIMING_0 0x110 /* Timing register 0 */ +#define DSI_LCD1_TIMING_1 0x114 /* Timing register 1 */ +#define DSI_LCD1_TIMING_2 0x118 /* Timing register 2 */ +#define DSI_LCD1_TIMING_3 0x11C /* Timing register 3 */ +#define DSI_LCD1_WC_0 0x120 /* Word Count register 0 */ +#define DSI_LCD1_WC_1 0x124 /* Word Count register 1 */ +#define DSI_LCD1_WC_2 0x128 /* Word Count register 2 */ + u32 timing0; + u32 timing1; + u32 timing2; + u32 timing3; + u32 wc0; + u32 wc1; + u32 wc2; + u32 reserved2[1]; + u32 slot_cnt0; + u32 slot_cnt1; + u32 reserved3[2]; + u32 status_0; + u32 status_1; + u32 status_2; + u32 status_3; + u32 status_4; +}; + +struct dsi_regs { +#define DSI_CTRL_0 0x000 /* DSI control register 0 */ +#define DSI_CTRL_1 0x004 /* DSI control register 1 */ + u32 ctrl0; + u32 ctrl1; + u32 reserved1[2]; + u32 irq_status; + u32 irq_mask; + u32 reserved2[2]; + +#define DSI_CPU_CMD_0 0x020 /* DSI CPU packet command register 0 */ +#define DSI_CPU_CMD_1 0x024 /* DSU CPU Packet Command Register 1 */ +#define DSI_CPU_CMD_3 0x02C /* DSU CPU Packet Command Register 3 */ +#define DSI_CPU_WDAT_0 0x030 /* DSI CUP */ + u32 cmd0; + u32 cmd1; + u32 cmd2; + u32 cmd3; + u32 dat0; + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 reserved3[2]; + + u32 smt_cmd; + u32 smt_ctrl0; + u32 smt_ctrl1; + u32 reserved4[1]; + + u32 rx0_status; + +/* Rx Packet Header - data from slave device */ +#define DSI_RX_PKT_HDR_0 0x064 + u32 rx0_header; + u32 rx1_status; + u32 rx1_header; + u32 rx_ctrl; + u32 rx_ctrl1; + u32 rx2_status; + u32 rx2_header; + u32 reserved5[1]; + + u32 phy_ctrl1; +#define DSI_PHY_CTRL_2 0x088 /* DSI DPHI Control Register 2 */ +#define DSI_PHY_CTRL_3 0x08C /* DPHY Control Register 3 */ + u32 phy_ctrl2; + u32 phy_ctrl3; + u32 phy_status0; + u32 phy_status1; + u32 reserved6[5]; + u32 phy_status2; + +#define DSI_PHY_RCOMP_0 0x0B0 /* DPHY Rcomp Control Register */ + u32 phy_rcomp0; + u32 reserved7[3]; +#define DSI_PHY_TIME_0 0x0C0 /* DPHY Timing Control Register 0 */ +#define DSI_PHY_TIME_1 0x0C4 /* DPHY Timing Control Register 1 */ +#define DSI_PHY_TIME_2 0x0C8 /* DPHY Timing Control Register 2 */ +#define DSI_PHY_TIME_3 0x0CC /* DPHY Timing Control Register 3 */ +#define DSI_PHY_TIME_4 0x0D0 /* DPHY Timing Control Register 4 */ +#define DSI_PHY_TIME_5 0x0D4 /* DPHY Timing Control Register 5 */ + u32 phy_timing0; + u32 phy_timing1; + u32 phy_timing2; + u32 phy_timing3; + u32 phy_code_0; + u32 phy_code_1; + u32 reserved8[2]; + u32 mem_ctrl; + u32 tx_timer; + u32 rx_timer; + u32 turn_timer; + u32 reserved9[4]; + +#define DSI_LCD1_CTRL_0 0x100 /* DSI Active Panel 1 Control register 0 */ +#define DSI_LCD1_CTRL_1 0x104 /* DSI Active Panel 1 Control register 1 */ +#define DSI_LCD1_TIMING_0 0x110 /* Timing register 0 */ +#define DSI_LCD1_TIMING_1 0x114 /* Timing register 1 */ +#define DSI_LCD1_TIMING_2 0x118 /* Timing register 2 */ +#define DSI_LCD1_TIMING_3 0x11C /* Timing register 3 */ +#define DSI_LCD1_WC_0 0x120 /* Word Count register 0 */ +#define DSI_LCD1_WC_1 0x124 /* Word Count register 1 */ +#define DSI_LCD1_WC_2 0x128 /* Word Count register 2 */ + struct dsi_lcd_regs lcd1; + u32 reserved10[11]; + struct dsi_lcd_regs lcd2; +}; + +#define DSI_LCD2_CTRL_0 0x180 /* DSI Active Panel 2 Control register 0 */ +#define DSI_LCD2_CTRL_1 0x184 /* DSI Active Panel 2 Control register 1 */ +#define DSI_LCD2_TIMING_0 0x190 /* Timing register 0 */ +#define DSI_LCD2_TIMING_1 0x194 /* Timing register 1 */ +#define DSI_LCD2_TIMING_2 0x198 /* Timing register 2 */ +#define DSI_LCD2_TIMING_3 0x19C /* Timing register 3 */ +#define DSI_LCD2_WC_0 0x1A0 /* Word Count register 0 */ +#define DSI_LCD2_WC_1 0x1A4 /* Word Count register 1 */ +#define DSI_LCD2_WC_2 0x1A8 /* Word Count register 2 */ + +/* DSI_CTRL_0 0x0000 DSI Control Register 0 */ +#define DSI_CTRL_0_CFG_SOFT_RST (1<<31) +#define DSI_CTRL_0_CFG_SOFT_RST_REG (1<<30) +#define DSI_CTRL_0_CFG_LCD1_TX_EN (1<<8) +#define DSI_CTRL_0_CFG_LCD1_SLV (1<<4) +#define DSI_CTRL_0_CFG_LCD1_EN (1<<0) + +/* DSI_CTRL_1 0x0004 DSI Control Register 1 */ +#define DSI_CTRL_1_CFG_EOTP (1<<8) +#define DSI_CTRL_1_CFG_RSVD (2<<4) +#define DSI_CTRL_1_CFG_LCD2_VCH_NO_MASK (3<<2) +#define DSI_CTRL_1_CFG_LCD2_VCH_NO_SHIFT 2 +#define DSI_CTRL_1_CFG_LCD1_VCH_NO_MASK (3<<0) +#define DSI_CTRL_1_CFG_LCD1_VCH_NO_SHIFT 0 + +/* DSI_LCD1_CTRL_1 0x0104 DSI Active Panel 1 Control Register 1 */ +/* LCD 1 Vsync Reset Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_VSYNC_RST_EN (1<<31) +/* LCD 1 2K Pixel Buffer Mode Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_M2K_EN (1<<30) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_29_23 reserved */ +/* Long Blanking Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HLP_PKT_EN (1<<22) +/* Extra Long Blanking Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HEX_PKT_EN (1<<21) +/* Front Porch Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HFP_PKT_EN (1<<20) +/* hact Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HACT_PKT_EN (1<<19) +/* Back Porch Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HBP_PKT_EN (1<<18) +/* hse Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HSE_PKT_EN (1<<17) +/* hsa Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HSA_PKT_EN (1<<16) +/* All Item Enable after Pixel Data */ +#define DSI_LCD1_CTRL_1_CFG_L1_ALL_SLOT_EN (1<<15) +/* Extra Long Packet Enable after Pixel Data */ +#define DSI_LCD1_CTRL_1_CFG_L1_HEX_SLOT_EN (1<<14) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_13_11 reserved */ +/* Turn Around Bus at Last h Line */ +#define DSI_LCD1_CTRL_1_CFG_L1_LAST_LINE_TURN (1<<10) +/* Go to Low Power Every Frame */ +#define DSI_LCD1_CTRL_1_CFG_L1_LPM_FRAME_EN (1<<9) +/* Go to Low Power Every Line */ +#define DSI_LCD1_CTRL_1_CFG_L1_LPM_LINE_EN (1<<8) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_7_4 reserved */ +/* DSI Transmission Mode for LCD 1 */ +#define DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_SHIFT 2 +#define DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_MASK (3<<2) +/* LCD 1 Input Data RGB Mode for LCD 1 */ +#define DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_SHIFT 0 +#define DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_MASK (3<<2) + +/* DSI_PHY_CTRL_2 0x0088 DPHY Control Register 2 */ +/* Bit(s) DSI_PHY_CTRL_2_RSRV_31_12 reserved */ +/* DPHY LP Receiver Enable */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_RESC_EN_MASK (0xf<<8) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_RESC_EN_SHIFT 8 +/* DPHY Data Lane Enable */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_MASK (0xf<<4) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT 4 +/* DPHY Bus Turn Around */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_TURN_MASK (0xf) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_TURN_SHIFT 0 + +/* DSI_CPU_CMD_1 0x0024 DSI CPU Packet Command Register 1 */ +/* Bit(s) DSI_CPU_CMD_1_RSRV_31_24 reserved */ +/* LPDT TX Enable */ +#define DSI_CPU_CMD_1_CFG_TXLP_LPDT_MASK (0xf<<20) +#define DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT 20 +/* ULPS TX Enable */ +#define DSI_CPU_CMD_1_CFG_TXLP_ULPS_MASK (0xf<<16) +#define DSI_CPU_CMD_1_CFG_TXLP_ULPS_SHIFT 16 +/* Low Power TX Trigger Code */ +#define DSI_CPU_CMD_1_CFG_TXLP_TRIGGER_CODE_MASK (0xffff) +#define DSI_CPU_CMD_1_CFG_TXLP_TRIGGER_CODE_SHIFT 0 + +/* DSI_PHY_TIME_0 0x00c0 DPHY Timing Control Register 0 */ +/* Length of HS Exit Period in tx_clk_esc Cycles */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_MASK (0xff<<24) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_SHIFT 24 +/* DPHY HS Trail Period Length */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_MASK (0xff<<16) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_SHIFT 16 +/* DPHY HS Zero State Length */ +#define DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_MASK (0xff<<8) +#define DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_SHIFT 8 +/* DPHY HS Prepare State Length */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_PREP_MASK (0xff) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_PREP_SHIFT 0 + +/* DSI_PHY_TIME_1 0x00c4 DPHY Timing Control Register 1 */ +/* Time to Drive LP-00 by New Transmitter */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_MASK (0xff<<24) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_SHIFT 24 +/* Time to Drive LP-00 after Turn Request */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_MASK (0xff<<16) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_SHIFT 16 +/* DPHY HS Wakeup Period Length */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_WAKEUP_MASK (0xffff) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_WAKEUP_SHIFT 0 + +/* DSI_PHY_TIME_2 0x00c8 DPHY Timing Control Register 2 */ +/* DPHY CLK Exit Period Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_MASK (0xff<<24) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_SHIFT 24 +/* DPHY CLK Trail Period Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_MASK (0xff<<16) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_SHIFT 16 +/* DPHY CLK Zero State Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_MASK (0xff<<8) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_SHIFT 8 +/* DPHY CLK LP Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_LPX_MASK (0xff) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_LPX_SHIFT 0 + +/* DSI_PHY_TIME_3 0x00cc DPHY Timing Control Register 3 */ +/* Bit(s) DSI_PHY_TIME_3_RSRV_31_16 reserved */ +/* DPHY LP Length */ +#define DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_MASK (0xff<<8) +#define DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_SHIFT 8 +/* DPHY HS req to rdy Length */ +#define DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_MASK (0xff) +#define DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_SHIFT 0 + +/* + * DSI timings + * PXA988 has diffrent ESC CLK with MMP2/MMP3 + * it will be used in dsi_set_dphy() in pxa688_phy.c + * as low power mode clock. + */ +#ifdef CONFIG_CPU_PXA988 +#define DSI_ESC_CLK 52 /* Unit: Mhz */ +#define DSI_ESC_CLK_T 19 /* Unit: ns */ +#else +#define DSI_ESC_CLK 66 /* Unit: Mhz */ +#define DSI_ESC_CLK_T 15 /* Unit: ns */ +#endif + +/* LVDS */ +/* LVDS_PHY_CTRL */ +#define LVDS_PHY_CTL 0x2A4 +#define LVDS_PLL_LOCK (1 << 31) +#define LVDS_PHY_EXT_MASK (7 << 28) +#define LVDS_PHY_EXT_SHIFT (28) +#define LVDS_CLK_PHASE_MASK (0x7f << 16) +#define LVDS_CLK_PHASE_SHIFT (16) +#define LVDS_SSC_RESET_EXT (1 << 13) +#define LVDS_SSC_MODE_DOWN_SPREAD (1 << 12) +#define LVDS_SSC_EN (1 << 11) +#define LVDS_PU_PLL (1 << 10) +#define LVDS_PU_TX (1 << 9) +#define LVDS_PU_IVREF (1 << 8) +#define LVDS_CLK_SEL (1 << 7) +#define LVDS_CLK_SEL_LVDS_PCLK (1 << 7) +#define LVDS_PD_CH_MASK (0x3f << 1) +#define LVDS_PD_CH(ch) ((ch) << 1) +#define LVDS_RST (1 << 0) + +#define LVDS_PHY_CTL_EXT 0x2A8 + +/* LVDS_PHY_CTRL_EXT1 */ +#define LVDS_SSC_RNGE_MASK (0x7ff << 16) +#define LVDS_SSC_RNGE_SHIFT (16) +#define LVDS_RESERVE_IN_MASK (0xf << 12) +#define LVDS_RESERVE_IN_SHIFT (12) +#define LVDS_TEST_MON_MASK (0x7 << 8) +#define LVDS_TEST_MON_SHIFT (8) +#define LVDS_POL_SWAP_MASK (0x3f << 0) +#define LVDS_POL_SWAP_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT2 */ +#define LVDS_TX_DIF_AMP_MASK (0xf << 24) +#define LVDS_TX_DIF_AMP_SHIFT (24) +#define LVDS_TX_DIF_CM_MASK (0x3 << 22) +#define LVDS_TX_DIF_CM_SHIFT (22) +#define LVDS_SELLV_TXCLK_MASK (0x1f << 16) +#define LVDS_SELLV_TXCLK_SHIFT (16) +#define LVDS_TX_CMFB_EN (0x1 << 15) +#define LVDS_TX_TERM_EN (0x1 << 14) +#define LVDS_SELLV_TXDATA_MASK (0x1f << 8) +#define LVDS_SELLV_TXDATA_SHIFT (8) +#define LVDS_SELLV_OP7_MASK (0x3 << 6) +#define LVDS_SELLV_OP7_SHIFT (6) +#define LVDS_SELLV_OP6_MASK (0x3 << 4) +#define LVDS_SELLV_OP6_SHIFT (4) +#define LVDS_SELLV_OP9_MASK (0x3 << 2) +#define LVDS_SELLV_OP9_SHIFT (2) +#define LVDS_STRESSTST_EN (0x1 << 0) + +/* LVDS_PHY_CTRL_EXT3 */ +#define LVDS_KVCO_MASK (0xf << 28) +#define LVDS_KVCO_SHIFT (28) +#define LVDS_CTUNE_MASK (0x3 << 26) +#define LVDS_CTUNE_SHIFT (26) +#define LVDS_VREG_IVREF_MASK (0x3 << 24) +#define LVDS_VREG_IVREF_SHIFT (24) +#define LVDS_VDDL_MASK (0xf << 20) +#define LVDS_VDDL_SHIFT (20) +#define LVDS_VDDM_MASK (0x3 << 18) +#define LVDS_VDDM_SHIFT (18) +#define LVDS_FBDIV_MASK (0xf << 8) +#define LVDS_FBDIV_SHIFT (8) +#define LVDS_REFDIV_MASK (0x7f << 0) +#define LVDS_REFDIV_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT4 */ +#define LVDS_SSC_FREQ_DIV_MASK (0xffff << 16) +#define LVDS_SSC_FREQ_DIV_SHIFT (16) +#define LVDS_INTPI_MASK (0xf << 12) +#define LVDS_INTPI_SHIFT (12) +#define LVDS_VCODIV_SEL_SE_MASK (0xf << 8) +#define LVDS_VCODIV_SEL_SE_SHIFT (8) +#define LVDS_RESET_INTP_EXT (0x1 << 7) +#define LVDS_VCO_VRNG_MASK (0x7 << 4) +#define LVDS_VCO_VRNG_SHIFT (4) +#define LVDS_PI_EN (0x1 << 3) +#define LVDS_ICP_MASK (0x7 << 0) +#define LVDS_ICP_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT5 */ +#define LVDS_FREQ_OFFSET_MASK (0x1ffff << 15) +#define LVDS_FREQ_OFFSET_SHIFT (15) +#define LVDS_FREQ_OFFSET_VALID (0x1 << 2) +#define LVDS_FREQ_OFFSET_MODE_CK_DIV4_OUT (0x1 << 1) +#define LVDS_FREQ_OFFSET_MODE_EN (0x1 << 0) + +/* VDMA */ +struct vdma_ch_regs { +#define VDMA_DC_SADDR_1 0x320 +#define VDMA_DC_SADDR_2 0x3A0 +#define VDMA_DC_SZ_1 0x324 +#define VDMA_DC_SZ_2 0x3A4 +#define VDMA_CTRL_1 0x328 +#define VDMA_CTRL_2 0x3A8 +#define VDMA_SRC_SZ_1 0x32C +#define VDMA_SRC_SZ_2 0x3AC +#define VDMA_SA_1 0x330 +#define VDMA_SA_2 0x3B0 +#define VDMA_DA_1 0x334 +#define VDMA_DA_2 0x3B4 +#define VDMA_SZ_1 0x338 +#define VDMA_SZ_2 0x3B8 + u32 dc_saddr; + u32 dc_size; + u32 ctrl; + u32 src_size; + u32 src_addr; + u32 dst_addr; + u32 dst_size; +#define VDMA_PITCH_1 0x33C +#define VDMA_PITCH_2 0x3BC +#define VDMA_ROT_CTRL_1 0x340 +#define VDMA_ROT_CTRL_2 0x3C0 +#define VDMA_RAM_CTRL0_1 0x344 +#define VDMA_RAM_CTRL0_2 0x3C4 +#define VDMA_RAM_CTRL1_1 0x348 +#define VDMA_RAM_CTRL1_2 0x3C8 + u32 pitch; + u32 rot_ctrl; + u32 ram_ctrl0; + u32 ram_ctrl1; + +}; +struct vdma_regs { +#define VDMA_ARBR_CTRL 0x300 +#define VDMA_IRQR 0x304 +#define VDMA_IRQM 0x308 +#define VDMA_IRQS 0x30C +#define VDMA_MDMA_ARBR_CTRL 0x310 + u32 arbr_ctr; + u32 irq_raw; + u32 irq_mask; + u32 irq_status; + u32 mdma_arbr_ctrl; + u32 reserved[3]; + + struct vdma_ch_regs ch1; + u32 reserved2[21]; + struct vdma_ch_regs ch2; +}; + +/* CMU */ +#define CMU_PIP_DE_H_CFG 0x0008 +#define CMU_PRI1_H_CFG 0x000C +#define CMU_PRI2_H_CFG 0x0010 +#define CMU_ACE_MAIN_DE1_H_CFG 0x0014 +#define CMU_ACE_MAIN_DE2_H_CFG 0x0018 +#define CMU_ACE_PIP_DE1_H_CFG 0x001C +#define CMU_ACE_PIP_DE2_H_CFG 0x0020 +#define CMU_PIP_DE_V_CFG 0x0024 +#define CMU_PRI_V_CFG 0x0028 +#define CMU_ACE_MAIN_DE_V_CFG 0x002C +#define CMU_ACE_PIP_DE_V_CFG 0x0030 +#define CMU_BAR_0_CFG 0x0034 +#define CMU_BAR_1_CFG 0x0038 +#define CMU_BAR_2_CFG 0x003C +#define CMU_BAR_3_CFG 0x0040 +#define CMU_BAR_4_CFG 0x0044 +#define CMU_BAR_5_CFG 0x0048 +#define CMU_BAR_6_CFG 0x004C +#define CMU_BAR_7_CFG 0x0050 +#define CMU_BAR_8_CFG 0x0054 +#define CMU_BAR_9_CFG 0x0058 +#define CMU_BAR_10_CFG 0x005C +#define CMU_BAR_11_CFG 0x0060 +#define CMU_BAR_12_CFG 0x0064 +#define CMU_BAR_13_CFG 0x0068 +#define CMU_BAR_14_CFG 0x006C +#define CMU_BAR_15_CFG 0x0070 +#define CMU_BAR_CTRL 0x0074 +#define PATTERN_TOTAL 0x0078 +#define PATTERN_ACTIVE 0x007C +#define PATTERN_FRONT_PORCH 0x0080 +#define PATTERN_BACK_PORCH 0x0084 +#define CMU_CLK_CTRL 0x0088 + +#define CMU_ICSC_M_C0_L 0x0900 +#define CMU_ICSC_M_C0_H 0x0901 +#define CMU_ICSC_M_C1_L 0x0902 +#define CMU_ICSC_M_C1_H 0x0903 +#define CMU_ICSC_M_C2_L 0x0904 +#define CMU_ICSC_M_C2_H 0x0905 +#define CMU_ICSC_M_C3_L 0x0906 +#define CMU_ICSC_M_C3_H 0x0907 +#define CMU_ICSC_M_C4_L 0x0908 +#define CMU_ICSC_M_C4_H 0x0909 +#define CMU_ICSC_M_C5_L 0x090A +#define CMU_ICSC_M_C5_H 0x090B +#define CMU_ICSC_M_C6_L 0x090C +#define CMU_ICSC_M_C6_H 0x090D +#define CMU_ICSC_M_C7_L 0x090E +#define CMU_ICSC_M_C7_H 0x090F +#define CMU_ICSC_M_C8_L 0x0910 +#define CMU_ICSC_M_C8_H 0x0911 +#define CMU_ICSC_M_O1_0 0x0914 +#define CMU_ICSC_M_O1_1 0x0915 +#define CMU_ICSC_M_O1_2 0x0916 +#define CMU_ICSC_M_O2_0 0x0918 +#define CMU_ICSC_M_O2_1 0x0919 +#define CMU_ICSC_M_O2_2 0x091A +#define CMU_ICSC_M_O3_0 0x091C +#define CMU_ICSC_M_O3_1 0x091D +#define CMU_ICSC_M_O3_2 0x091E +#define CMU_ICSC_P_C0_L 0x0920 +#define CMU_ICSC_P_C0_H 0x0921 +#define CMU_ICSC_P_C1_L 0x0922 +#define CMU_ICSC_P_C1_H 0x0923 +#define CMU_ICSC_P_C2_L 0x0924 +#define CMU_ICSC_P_C2_H 0x0925 +#define CMU_ICSC_P_C3_L 0x0926 +#define CMU_ICSC_P_C3_H 0x0927 +#define CMU_ICSC_P_C4_L 0x0928 +#define CMU_ICSC_P_C4_H 0x0929 +#define CMU_ICSC_P_C5_L 0x092A +#define CMU_ICSC_P_C5_H 0x092B +#define CMU_ICSC_P_C6_L 0x092C +#define CMU_ICSC_P_C6_H 0x092D +#define CMU_ICSC_P_C7_L 0x092E +#define CMU_ICSC_P_C7_H 0x092F +#define CMU_ICSC_P_C8_L 0x0930 +#define CMU_ICSC_P_C8_H 0x0931 +#define CMU_ICSC_P_O1_0 0x0934 +#define CMU_ICSC_P_O1_1 0x0935 +#define CMU_ICSC_P_O1_2 0x0936 +#define CMU_ICSC_P_O2_0 0x0938 +#define CMU_ICSC_P_O2_1 0x0939 +#define CMU_ICSC_P_O2_2 0x093A +#define CMU_ICSC_P_O3_0 0x093C +#define CMU_ICSC_P_O3_1 0x093D +#define CMU_ICSC_P_O3_2 0x093E +#define CMU_BR_M_EN 0x0940 +#define CMU_BR_M_TH1_L 0x0942 +#define CMU_BR_M_TH1_H 0x0943 +#define CMU_BR_M_TH2_L 0x0944 +#define CMU_BR_M_TH2_H 0x0945 +#define CMU_ACE_M_EN 0x0950 +#define CMU_ACE_M_WFG1 0x0951 +#define CMU_ACE_M_WFG2 0x0952 +#define CMU_ACE_M_WFG3 0x0953 +#define CMU_ACE_M_TH0 0x0954 +#define CMU_ACE_M_TH1 0x0955 +#define CMU_ACE_M_TH2 0x0956 +#define CMU_ACE_M_TH3 0x0957 +#define CMU_ACE_M_TH4 0x0958 +#define CMU_ACE_M_TH5 0x0959 +#define CMU_ACE_M_OP0_L 0x095A +#define CMU_ACE_M_OP0_H 0x095B +#define CMU_ACE_M_OP5_L 0x095C +#define CMU_ACE_M_OP5_H 0x095D +#define CMU_ACE_M_GB2 0x095E +#define CMU_ACE_M_GB3 0x095F +#define CMU_ACE_M_MS1 0x0960 +#define CMU_ACE_M_MS2 0x0961 +#define CMU_ACE_M_MS3 0x0962 +#define CMU_BR_P_EN 0x0970 +#define CMU_BR_P_TH1_L 0x0972 +#define CMU_BR_P_TH1_H 0x0973 +#define CMU_BR_P_TH2_L 0x0974 +#define CMU_BR_P_TH2_H 0x0975 +#define CMU_ACE_P_EN 0x0980 +#define CMU_ACE_P_WFG1 0x0981 +#define CMU_ACE_P_WFG2 0x0982 +#define CMU_ACE_P_WFG3 0x0983 +#define CMU_ACE_P_TH0 0x0984 +#define CMU_ACE_P_TH1 0x0985 +#define CMU_ACE_P_TH2 0x0986 +#define CMU_ACE_P_TH3 0x0987 +#define CMU_ACE_P_TH4 0x0988 +#define CMU_ACE_P_TH5 0x0989 +#define CMU_ACE_P_OP0_L 0x098A +#define CMU_ACE_P_OP0_H 0x098B +#define CMU_ACE_P_OP5_L 0x098C +#define CMU_ACE_P_OP5_H 0x098D +#define CMU_ACE_P_GB2 0x098E +#define CMU_ACE_P_GB3 0x098F +#define CMU_ACE_P_MS1 0x0990 +#define CMU_ACE_P_MS2 0x0991 +#define CMU_ACE_P_MS3 0x0992 +#define CMU_FTDC_M_EN 0x09A0 +#define CMU_FTDC_P_EN 0x09A1 +#define CMU_FTDC_INLOW_L 0x09A2 +#define CMU_FTDC_INLOW_H 0x09A3 +#define CMU_FTDC_INHIGH_L 0x09A4 +#define CMU_FTDC_INHIGH_H 0x09A5 +#define CMU_FTDC_OUTLOW_L 0x09A6 +#define CMU_FTDC_OUTLOW_H 0x09A7 +#define CMU_FTDC_OUTHIGH_L 0x09A8 +#define CMU_FTDC_OUTHIGH_H 0x09A9 +#define CMU_FTDC_YLOW 0x09AA +#define CMU_FTDC_YHIGH 0x09AB +#define CMU_FTDC_CH1 0x09AC +#define CMU_FTDC_CH2_L 0x09AE +#define CMU_FTDC_CH2_H 0x09AF +#define CMU_FTDC_CH3_L 0x09B0 +#define CMU_FTDC_CH3_H 0x09B1 +#define CMU_FTDC_1_C00_6 0x09B2 +#define CMU_FTDC_1_C01_6 0x09B8 +#define CMU_FTDC_1_C11_6 0x09BE +#define CMU_FTDC_1_C10_6 0x09C4 +#define CMU_FTDC_1_OFF00_6 0x09CA +#define CMU_FTDC_1_OFF10_6 0x09D0 +#define CMU_HS_M_EN 0x0A00 +#define CMU_HS_M_AX1_L 0x0A02 +#define CMU_HS_M_AX1_H 0x0A03 +#define CMU_HS_M_AX2_L 0x0A04 +#define CMU_HS_M_AX2_H 0x0A05 +#define CMU_HS_M_AX3_L 0x0A06 +#define CMU_HS_M_AX3_H 0x0A07 +#define CMU_HS_M_AX4_L 0x0A08 +#define CMU_HS_M_AX4_H 0x0A09 +#define CMU_HS_M_AX5_L 0x0A0A +#define CMU_HS_M_AX5_H 0x0A0B +#define CMU_HS_M_AX6_L 0x0A0C +#define CMU_HS_M_AX6_H 0x0A0D +#define CMU_HS_M_AX7_L 0x0A0E +#define CMU_HS_M_AX7_H 0x0A0F +#define CMU_HS_M_AX8_L 0x0A10 +#define CMU_HS_M_AX8_H 0x0A11 +#define CMU_HS_M_AX9_L 0x0A12 +#define CMU_HS_M_AX9_H 0x0A13 +#define CMU_HS_M_AX10_L 0x0A14 +#define CMU_HS_M_AX10_H 0x0A15 +#define CMU_HS_M_AX11_L 0x0A16 +#define CMU_HS_M_AX11_H 0x0A17 +#define CMU_HS_M_AX12_L 0x0A18 +#define CMU_HS_M_AX12_H 0x0A19 +#define CMU_HS_M_AX13_L 0x0A1A +#define CMU_HS_M_AX13_H 0x0A1B +#define CMU_HS_M_AX14_L 0x0A1C +#define CMU_HS_M_AX14_H 0x0A1D +#define CMU_HS_M_H1_H14 0x0A1E +#define CMU_HS_M_S1_S14 0x0A2C +#define CMU_HS_M_GL 0x0A3A +#define CMU_HS_M_MAXSAT_RGB_Y_L 0x0A3C +#define CMU_HS_M_MAXSAT_RGB_Y_H 0x0A3D +#define CMU_HS_M_MAXSAT_RCR_L 0x0A3E +#define CMU_HS_M_MAXSAT_RCR_H 0x0A3F +#define CMU_HS_M_MAXSAT_RCB_L 0x0A40 +#define CMU_HS_M_MAXSAT_RCB_H 0x0A41 +#define CMU_HS_M_MAXSAT_GCR_L 0x0A42 +#define CMU_HS_M_MAXSAT_GCR_H 0x0A43 +#define CMU_HS_M_MAXSAT_GCB_L 0x0A44 +#define CMU_HS_M_MAXSAT_GCB_H 0x0A45 +#define CMU_HS_M_MAXSAT_BCR_L 0x0A46 +#define CMU_HS_M_MAXSAT_BCR_H 0x0A47 +#define CMU_HS_M_MAXSAT_BCB_L 0x0A48 +#define CMU_HS_M_MAXSAT_BCB_H 0x0A49 +#define CMU_HS_M_ROFF_L 0x0A4A +#define CMU_HS_M_ROFF_H 0x0A4B +#define CMU_HS_M_GOFF_L 0x0A4C +#define CMU_HS_M_GOFF_H 0x0A4D +#define CMU_HS_M_BOFF_L 0x0A4E +#define CMU_HS_M_BOFF_H 0x0A4F +#define CMU_HS_P_EN 0x0A50 +#define CMU_HS_P_AX1_L 0x0A52 +#define CMU_HS_P_AX1_H 0x0A53 +#define CMU_HS_P_AX2_L 0x0A54 +#define CMU_HS_P_AX2_H 0x0A55 +#define CMU_HS_P_AX3_L 0x0A56 +#define CMU_HS_P_AX3_H 0x0A57 +#define CMU_HS_P_AX4_L 0x0A58 +#define CMU_HS_P_AX4_H 0x0A59 +#define CMU_HS_P_AX5_L 0x0A5A +#define CMU_HS_P_AX5_H 0x0A5B +#define CMU_HS_P_AX6_L 0x0A5C +#define CMU_HS_P_AX6_H 0x0A5D +#define CMU_HS_P_AX7_L 0x0A5E +#define CMU_HS_P_AX7_H 0x0A5F +#define CMU_HS_P_AX8_L 0x0A60 +#define CMU_HS_P_AX8_H 0x0A61 +#define CMU_HS_P_AX9_L 0x0A62 +#define CMU_HS_P_AX9_H 0x0A63 +#define CMU_HS_P_AX10_L 0x0A64 +#define CMU_HS_P_AX10_H 0x0A65 +#define CMU_HS_P_AX11_L 0x0A66 +#define CMU_HS_P_AX11_H 0x0A67 +#define CMU_HS_P_AX12_L 0x0A68 +#define CMU_HS_P_AX12_H 0x0A69 +#define CMU_HS_P_AX13_L 0x0A6A +#define CMU_HS_P_AX13_H 0x0A6B +#define CMU_HS_P_AX14_L 0x0A6C +#define CMU_HS_P_AX14_H 0x0A6D +#define CMU_HS_P_H1_H14 0x0A6E +#define CMU_HS_P_S1_S14 0x0A7C +#define CMU_HS_P_GL 0x0A8A +#define CMU_HS_P_MAXSAT_RGB_Y_L 0x0A8C +#define CMU_HS_P_MAXSAT_RGB_Y_H 0x0A8D +#define CMU_HS_P_MAXSAT_RCR_L 0x0A8E +#define CMU_HS_P_MAXSAT_RCR_H 0x0A8F +#define CMU_HS_P_MAXSAT_RCB_L 0x0A90 +#define CMU_HS_P_MAXSAT_RCB_H 0x0A91 +#define CMU_HS_P_MAXSAT_GCR_L 0x0A92 +#define CMU_HS_P_MAXSAT_GCR_H 0x0A93 +#define CMU_HS_P_MAXSAT_GCB_L 0x0A94 +#define CMU_HS_P_MAXSAT_GCB_H 0x0A95 +#define CMU_HS_P_MAXSAT_BCR_L 0x0A96 +#define CMU_HS_P_MAXSAT_BCR_H 0x0A97 +#define CMU_HS_P_MAXSAT_BCB_L 0x0A98 +#define CMU_HS_P_MAXSAT_BCB_H 0x0A99 +#define CMU_HS_P_ROFF_L 0x0A9A +#define CMU_HS_P_ROFF_H 0x0A9B +#define CMU_HS_P_GOFF_L 0x0A9C +#define CMU_HS_P_GOFF_H 0x0A9D +#define CMU_HS_P_BOFF_L 0x0A9E +#define CMU_HS_P_BOFF_H 0x0A9F +#define CMU_GLCSC_M_C0_L 0x0AA0 +#define CMU_GLCSC_M_C0_H 0x0AA1 +#define CMU_GLCSC_M_C1_L 0x0AA2 +#define CMU_GLCSC_M_C1_H 0x0AA3 +#define CMU_GLCSC_M_C2_L 0x0AA4 +#define CMU_GLCSC_M_C2_H 0x0AA5 +#define CMU_GLCSC_M_C3_L 0x0AA6 +#define CMU_GLCSC_M_C3_H 0x0AA7 +#define CMU_GLCSC_M_C4_L 0x0AA8 +#define CMU_GLCSC_M_C4_H 0x0AA9 +#define CMU_GLCSC_M_C5_L 0x0AAA +#define CMU_GLCSC_M_C5_H 0x0AAB +#define CMU_GLCSC_M_C6_L 0x0AAC +#define CMU_GLCSC_M_C6_H 0x0AAD +#define CMU_GLCSC_M_C7_L 0x0AAE +#define CMU_GLCSC_M_C7_H 0x0AAF +#define CMU_GLCSC_M_C8_L 0x0AB0 +#define CMU_GLCSC_M_C8_H 0x0AB1 +#define CMU_GLCSC_M_O1_1 0x0AB4 +#define CMU_GLCSC_M_O1_2 0x0AB5 +#define CMU_GLCSC_M_O1_3 0x0AB6 +#define CMU_GLCSC_M_O2_1 0x0AB8 +#define CMU_GLCSC_M_O2_2 0x0AB9 +#define CMU_GLCSC_M_O2_3 0x0ABA +#define CMU_GLCSC_M_O3_1 0x0ABC +#define CMU_GLCSC_M_O3_2 0x0ABD +#define CMU_GLCSC_M_O3_3 0x0ABE +#define CMU_GLCSC_P_C0_L 0x0AC0 +#define CMU_GLCSC_P_C0_H 0x0AC1 +#define CMU_GLCSC_P_C1_L 0x0AC2 +#define CMU_GLCSC_P_C1_H 0x0AC3 +#define CMU_GLCSC_P_C2_L 0x0AC4 +#define CMU_GLCSC_P_C2_H 0x0AC5 +#define CMU_GLCSC_P_C3_L 0x0AC6 +#define CMU_GLCSC_P_C3_H 0x0AC7 +#define CMU_GLCSC_P_C4_L 0x0AC8 +#define CMU_GLCSC_P_C4_H 0x0AC9 +#define CMU_GLCSC_P_C5_L 0x0ACA +#define CMU_GLCSC_P_C5_H 0x0ACB +#define CMU_GLCSC_P_C6_L 0x0ACC +#define CMU_GLCSC_P_C6_H 0x0ACD +#define CMU_GLCSC_P_C7_L 0x0ACE +#define CMU_GLCSC_P_C7_H 0x0ACF +#define CMU_GLCSC_P_C8_L 0x0AD0 +#define CMU_GLCSC_P_C8_H 0x0AD1 +#define CMU_GLCSC_P_O1_1 0x0AD4 +#define CMU_GLCSC_P_O1_2 0x0AD5 +#define CMU_GLCSC_P_O1_3 0x0AD6 +#define CMU_GLCSC_P_O2_1 0x0AD8 +#define CMU_GLCSC_P_O2_2 0x0AD9 +#define CMU_GLCSC_P_O2_3 0x0ADA +#define CMU_GLCSC_P_O3_1 0x0ADC +#define CMU_GLCSC_P_O3_2 0x0ADD +#define CMU_GLCSC_P_O3_3 0x0ADE +#define CMU_PIXVAL_M_EN 0x0AE0 +#define CMU_PIXVAL_P_EN 0x0AE1 + +#define CMU_CLK_CTRL_TCLK 0x0 +#define CMU_CLK_CTRL_SCLK 0x2 +#define CMU_CLK_CTRL_MSK 0x2 +#define CMU_CLK_CTRL_ENABLE 0x1 + +#define LCD_TOP_CTRL_TV 0x2 +#define LCD_TOP_CTRL_PN 0x0 +#define LCD_TOP_CTRL_SEL_MSK 0x2 +#define LCD_IO_CMU_IN_SEL_MSK (0x3 << 20) +#define LCD_IO_CMU_IN_SEL_TV 0 +#define LCD_IO_CMU_IN_SEL_PN 1 +#define LCD_IO_CMU_IN_SEL_PN2 2 +#define LCD_IO_TV_OUT_SEL_MSK (0x3 << 26) +#define LCD_IO_PN_OUT_SEL_MSK (0x3 << 24) +#define LCD_IO_PN2_OUT_SEL_MSK (0x3 << 28) +#define LCD_IO_TV_OUT_SEL_NON 3 +#define LCD_IO_PN_OUT_SEL_NON 3 +#define LCD_IO_PN2_OUT_SEL_NON 3 +#define LCD_TOP_CTRL_CMU_ENABLE 0x1 +#define LCD_IO_OVERL_MSK 0xC00000 +#define LCD_IO_OVERL_TV 0x0 +#define LCD_IO_OVERL_LCD1 0x400000 +#define LCD_IO_OVERL_LCD2 0xC00000 +#define HINVERT_MSK 0x4 +#define VINVERT_MSK 0x8 +#define HINVERT_LEN 0x2 +#define VINVERT_LEN 0x3 + +#define CMU_CTRL 0x88 +#define CMU_CTRL_A0_MSK 0x6 +#define CMU_CTRL_A0_TV 0x0 +#define CMU_CTRL_A0_LCD1 0x1 +#define CMU_CTRL_A0_LCD2 0x2 +#define CMU_CTRL_A0_HDMI 0x3 + +#define ICR_DRV_ROUTE_OFF 0x0 +#define ICR_DRV_ROUTE_TV 0x1 +#define ICR_DRV_ROUTE_LCD1 0x2 +#define ICR_DRV_ROUTE_LCD2 0x3 + +enum { + PATH_PN = 0, + PATH_TV, + PATH_P2, +}; + +/* + * mmp path describes part of mmp path related info: + * which is hiden in display driver and not exported to buffer driver + */ +struct mmphw_ctrl; +struct mmphw_path_plat { + int id; + struct mmphw_ctrl *ctrl; + struct mmp_path *path; + u32 path_config; + u32 link_config; +}; + +/* mmp ctrl describes mmp controller related info */ +struct mmphw_ctrl { + /* platform related, get from config */ + const char *name; + int irq; + void *reg_base; + struct clk *clk; + + /* sys info */ + struct device *dev; + + /* state */ + int open_count; + int status; + struct mutex access_ok; + + /*pathes*/ + int path_num; + struct mmphw_path_plat path_plats[0]; +}; + +static inline int overlay_is_vid(struct mmp_overlay *overlay) +{ + return overlay->dmafetch_id & 1; +} + +static inline struct mmphw_path_plat *path_to_path_plat(struct mmp_path *path) +{ + return (struct mmphw_path_plat *)path->plat_data; +} + +static inline struct mmphw_ctrl *path_to_ctrl(struct mmp_path *path) +{ + return path_to_path_plat(path)->ctrl; +} + +static inline struct mmphw_ctrl *overlay_to_ctrl(struct mmp_overlay *overlay) +{ + return path_to_ctrl(overlay->path); +} + +static inline void *ctrl_regs(struct mmp_path *path) +{ + return path_to_ctrl(path)->reg_base; +} + +/* path regs, for regs symmetrical for both pathes */ +static inline struct lcd_regs *path_regs(struct mmp_path *path) +{ + if (path->id == PATH_PN) + return (struct lcd_regs *)(ctrl_regs(path) + 0xc0); + else if (path->id == PATH_TV) + return (struct lcd_regs *)ctrl_regs(path); + else if (path->id == PATH_P2) + return (struct lcd_regs *)(ctrl_regs(path) + 0x200); + else { + dev_err(path->dev, "path id %d invalid\n", path->id); + BUG_ON(1); + return NULL; + } +} + +#ifdef CONFIG_MMP_DISP_SPI +extern int lcd_spi_register(struct mmphw_ctrl *ctrl); +#endif +#endif /* _MMP_CTRL_H_ */ diff --git a/drivers/video/mmp/hw/mmp_spi.c b/drivers/video/mmp/hw/mmp_spi.c new file mode 100644 index 000000000000..e62ca7bf0d5e --- /dev/null +++ b/drivers/video/mmp/hw/mmp_spi.c @@ -0,0 +1,180 @@ +/* + * linux/drivers/video/mmp/hw/mmp_spi.c + * using the spi in LCD controler for commands send + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/spi/spi.h> +#include "mmp_ctrl.h" + +/** + * spi_write - write command to the SPI port + * @data: can be 8/16/32-bit, MSB justified data to write. + * @len: data length. + * + * Wait bus transfer complete IRQ. + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred + * 0 success + */ +static inline int lcd_spi_write(struct spi_device *spi, u32 data) +{ + int timeout = 100000, isr, ret = 0; + u32 tmp; + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + + /* clear ISR */ + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + switch (spi->bits_per_word) { + case 8: + writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 16: + writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 32: + writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + + /* SPI start to send command */ + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + while (!(isr & SPI_IRQ_ENA_MASK)) { + udelay(100); + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + if (!--timeout) { + ret = -ETIMEDOUT; + dev_err(&spi->dev, "spi cmd send time out\n"); + break; + } + } + + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(0); + writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL); + + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + return ret; +} + +static int lcd_spi_setup(struct spi_device *spi) +{ + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + u32 tmp; + + tmp = CFG_SCLKCNT(16) | + CFG_TXBITS(spi->bits_per_word) | + CFG_SPI_SEL(1) | CFG_SPI_ENA(1) | + CFG_SPI_3W4WB(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + /* + * After set mode it need a time to pull up the spi singals, + * or it would cause the wrong waveform when send spi command, + * especially on pxa910h + */ + tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL); + if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI) + writel_relaxed(IOPAD_DUMB18SPI | + (tmp & ~CFG_IOPADMODE_MASK), + reg_base + SPU_IOPAD_CONTROL); + udelay(20); + return 0; +} + +static int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_transfer *t; + int i; + + list_for_each_entry(t, &m->transfers, transfer_list) { + switch (spi->bits_per_word) { + case 8: + for (i = 0; i < t->len; i++) + lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]); + break; + case 16: + for (i = 0; i < t->len/2; i++) + lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]); + break; + case 32: + for (i = 0; i < t->len/4; i++) + lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + } + + m->status = 0; + if (m->complete) + m->complete(m->context); + return 0; +} + +int lcd_spi_register(struct mmphw_ctrl *ctrl) +{ + struct spi_master *master; + void **p_regbase; + int err; + + master = spi_alloc_master(ctrl->dev, sizeof(void *)); + if (!master) { + dev_err(ctrl->dev, "unable to allocate SPI master\n"); + return -ENOMEM; + } + p_regbase = spi_master_get_devdata(master); + *p_regbase = ctrl->reg_base; + + /* set bus num to 5 to avoid conflict with other spi hosts */ + master->bus_num = 5; + master->num_chipselect = 1; + master->setup = lcd_spi_setup; + master->transfer = lcd_spi_one_transfer; + + err = spi_register_master(master); + if (err < 0) { + dev_err(ctrl->dev, "unable to register SPI master\n"); + spi_master_put(master); + return err; + } + + dev_info(&master->dev, "registered\n"); + + return 0; +} diff --git a/drivers/video/mmp/panel/Kconfig b/drivers/video/mmp/panel/Kconfig new file mode 100644 index 000000000000..4b2c4f457b11 --- /dev/null +++ b/drivers/video/mmp/panel/Kconfig @@ -0,0 +1,6 @@ +config MMP_PANEL_TPOHVGA + bool "tpohvga panel TJ032MD01BW support" + depends on SPI_MASTER + default n + help + tpohvga panel support diff --git a/drivers/video/mmp/panel/Makefile b/drivers/video/mmp/panel/Makefile new file mode 100644 index 000000000000..2f91611c7e5e --- /dev/null +++ b/drivers/video/mmp/panel/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MMP_PANEL_TPOHVGA) += tpo_tj032md01bw.o diff --git a/drivers/video/mmp/panel/tpo_tj032md01bw.c b/drivers/video/mmp/panel/tpo_tj032md01bw.c new file mode 100644 index 000000000000..998978b08f5e --- /dev/null +++ b/drivers/video/mmp/panel/tpo_tj032md01bw.c @@ -0,0 +1,186 @@ +/* + * linux/drivers/video/mmp/panel/tpo_tj032md01bw.c + * active panel using spi interface to do init + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.com> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <video/mmp_disp.h> + +static u16 init[] = { + 0x0801, + 0x0800, + 0x0200, + 0x0304, + 0x040e, + 0x0903, + 0x0b18, + 0x0c53, + 0x0d01, + 0x0ee0, + 0x0f01, + 0x1058, + 0x201e, + 0x210a, + 0x220a, + 0x231e, + 0x2400, + 0x2532, + 0x2600, + 0x27ac, + 0x2904, + 0x2aa2, + 0x2b45, + 0x2c45, + 0x2d15, + 0x2e5a, + 0x2fff, + 0x306b, + 0x310d, + 0x3248, + 0x3382, + 0x34bd, + 0x35e7, + 0x3618, + 0x3794, + 0x3801, + 0x395d, + 0x3aae, + 0x3bff, + 0x07c9, +}; + +static u16 poweroff[] = { + 0x07d9, +}; + +struct tpohvga_plat_data { + void (*plat_onoff)(int status); + struct spi_device *spi; +}; + +static void tpohvga_onoff(struct mmp_panel *panel, int status) +{ + struct tpohvga_plat_data *plat = panel->plat_data; + int ret; + + if (status) { + plat->plat_onoff(1); + + ret = spi_write(plat->spi, init, sizeof(init)); + if (ret < 0) + dev_warn(panel->dev, "init cmd failed(%d)\n", ret); + } else { + ret = spi_write(plat->spi, poweroff, sizeof(poweroff)); + if (ret < 0) + dev_warn(panel->dev, "poweroff cmd failed(%d)\n", ret); + + plat->plat_onoff(0); + } +} + +static struct mmp_mode mmp_modes_tpohvga[] = { + [0] = { + .pixclock_freq = 10394400, + .refresh = 60, + .xres = 320, + .yres = 480, + .hsync_len = 10, + .left_margin = 15, + .right_margin = 10, + .vsync_len = 2, + .upper_margin = 4, + .lower_margin = 2, + .invert_pixclock = 1, + .pix_fmt_out = PIXFMT_RGB565, + }, +}; + +static int tpohvga_get_modelist(struct mmp_panel *panel, + struct mmp_mode **modelist) +{ + *modelist = mmp_modes_tpohvga; + return 1; +} + +static struct mmp_panel panel_tpohvga = { + .name = "tpohvga", + .panel_type = PANELTYPE_ACTIVE, + .get_modelist = tpohvga_get_modelist, + .set_onoff = tpohvga_onoff, +}; + +static int tpohvga_probe(struct spi_device *spi) +{ + struct mmp_mach_panel_info *mi; + int ret; + struct tpohvga_plat_data *plat_data; + + /* get configs from platform data */ + mi = spi->dev.platform_data; + if (mi == NULL) { + dev_err(&spi->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + + /* setup spi related info */ + spi->bits_per_word = 16; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed %d", ret); + return ret; + } + + plat_data = kzalloc(sizeof(*plat_data), GFP_KERNEL); + if (plat_data == NULL) + return -ENOMEM; + + plat_data->spi = spi; + plat_data->plat_onoff = mi->plat_set_onoff; + panel_tpohvga.plat_data = plat_data; + panel_tpohvga.plat_path_name = mi->plat_path_name; + panel_tpohvga.dev = &spi->dev; + + mmp_register_panel(&panel_tpohvga); + + return 0; +} + +static struct spi_driver panel_tpohvga_driver = { + .driver = { + .name = "tpo-hvga", + .owner = THIS_MODULE, + }, + .probe = tpohvga_probe, +}; +module_spi_driver(panel_tpohvga_driver); + +MODULE_AUTHOR("Lisa Du<cldu@marvell.com>"); +MODULE_DESCRIPTION("Panel driver for tpohvga"); +MODULE_LICENSE("GPL"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 67526690acbc..762561fbabbf 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -17,11 +17,16 @@ config W1_SLAVE_SMEM simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. config W1_SLAVE_DS2408 - tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" - help - Say Y here if you want to use a 1-wire + tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" + help + Say Y here if you want to use a 1-wire + DS2408 8-Channel Addressable Switch device support - DS2408 8-Channel Addressable Switch device support +config W1_SLAVE_DS2413 + tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" + help + Say Y here if you want to use a 1-wire + DS2413 Dual Channel Addressable Switch device support config W1_SLAVE_DS2423 tristate "Counter 1-wire device (DS2423)" diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 05188f6aab5a..06529f3157ab 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o -obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c new file mode 100644 index 000000000000..829786252c6b --- /dev/null +++ b/drivers/w1/slaves/w1_ds2413.c @@ -0,0 +1,177 @@ +/* + * w1_ds2413.c - w1 family 3a (DS2413) driver + * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); +MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); + +#define W1_F3A_RETRIES 3 +#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 +#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A +#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA + +static ssize_t w1_f3a_read_state( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + dev_dbg(&sl->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->bus_mutex); + return -EIO; + } + + w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + + /* check for correct complement */ + if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) + return -EIO; + else + return 1; +} + +static ssize_t w1_f3a_write_output( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[3]; + unsigned int retries = W1_F3A_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + /* according to the DS2413 datasheet the most significant 6 bits + should be set to "1"s, so do it now */ + *buf = *buf | 0xFC; + + while (retries--) { + w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + return -EIO; +} + +#define NB_SYSFS_BIN_FILES 2 +static struct bin_attribute w1_f3a_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { + { + .attr = { + .name = "state", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f3a_read_state, + }, + { + .attr = { + .name = "output", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = 1, + .write = w1_f3a_write_output, + } +}; + +static int w1_f3a_add_slave(struct w1_slave *sl) +{ + int err = 0; + int i; + + for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) + err = sysfs_create_bin_file( + &sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); + if (err) + while (--i >= 0) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); + return err; +} + +static void w1_f3a_remove_slave(struct w1_slave *sl) +{ + int i; + for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); +} + +static struct w1_family_ops w1_f3a_fops = { + .add_slave = w1_f3a_add_slave, + .remove_slave = w1_f3a_remove_slave, +}; + +static struct w1_family w1_family_3a = { + .fid = W1_FAMILY_DS2413, + .fops = &w1_f3a_fops, +}; + +static int __init w1_f3a_init(void) +{ + return w1_register_family(&w1_family_3a); +} + +static void __exit w1_f3a_exit(void) +{ + w1_unregister_family(&w1_family_3a); +} + +module_init(w1_f3a_init); +module_exit(w1_f3a_exit); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index a1f0ce151d53..625dd08f775f 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -39,6 +39,7 @@ #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 #define W1_FAMILY_DS2780 0x32 +#define W1_FAMILY_DS2413 0x3A #define W1_THERM_DS1825 0x3B #define W1_FAMILY_DS2781 0x3D #define W1_THERM_DS28EA00 0x42 |