diff options
Diffstat (limited to 'drivers')
404 files changed, 22934 insertions, 16875 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index e1d5ea6d5e40..71a7d07c28c9 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -886,6 +886,58 @@ static ssize_t revision_show(struct device *dev, } static DEVICE_ATTR_RO(revision); +static ssize_t hw_error_scrub_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); + + return sprintf(buf, "%d\n", acpi_desc->scrub_mode); +} + +/* + * The 'hw_error_scrub' attribute can have the following values written to it: + * '0': Switch to the default mode where an exception will only insert + * the address of the memory error into the poison and badblocks lists. + * '1': Enable a full scrub to happen if an exception for a memory error is + * received. + */ +static ssize_t hw_error_scrub_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct nvdimm_bus_descriptor *nd_desc; + ssize_t rc; + long val; + + rc = kstrtol(buf, 0, &val); + if (rc) + return rc; + + device_lock(dev); + nd_desc = dev_get_drvdata(dev); + if (nd_desc) { + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); + + switch (val) { + case HW_ERROR_SCRUB_ON: + acpi_desc->scrub_mode = HW_ERROR_SCRUB_ON; + break; + case HW_ERROR_SCRUB_OFF: + acpi_desc->scrub_mode = HW_ERROR_SCRUB_OFF; + break; + default: + rc = -EINVAL; + break; + } + } + device_unlock(dev); + if (rc) + return rc; + return size; +} +static DEVICE_ATTR_RW(hw_error_scrub); + /* * This shows the number of full Address Range Scrubs that have been * completed since driver load time. Userspace can wait on this using @@ -958,6 +1010,7 @@ static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n) static struct attribute *acpi_nfit_attributes[] = { &dev_attr_revision.attr, &dev_attr_scrub.attr, + &dev_attr_hw_error_scrub.attr, NULL, }; @@ -1256,6 +1309,44 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc, return NULL; } +void __acpi_nvdimm_notify(struct device *dev, u32 event) +{ + struct nfit_mem *nfit_mem; + struct acpi_nfit_desc *acpi_desc; + + dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__, + event); + + if (event != NFIT_NOTIFY_DIMM_HEALTH) { + dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev), + event); + return; + } + + acpi_desc = dev_get_drvdata(dev->parent); + if (!acpi_desc) + return; + + /* + * If we successfully retrieved acpi_desc, then we know nfit_mem data + * is still valid. + */ + nfit_mem = dev_get_drvdata(dev); + if (nfit_mem && nfit_mem->flags_attr) + sysfs_notify_dirent(nfit_mem->flags_attr); +} +EXPORT_SYMBOL_GPL(__acpi_nvdimm_notify); + +static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_device *adev = data; + struct device *dev = &adev->dev; + + device_lock(dev->parent); + __acpi_nvdimm_notify(dev, event); + device_unlock(dev->parent); +} + static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, struct nfit_mem *nfit_mem, u32 device_handle) { @@ -1280,6 +1371,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, return force_enable_dimms ? 0 : -ENODEV; } + if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle, + ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) { + dev_err(dev, "%s: notification registration failed\n", + dev_name(&adev_dimm->dev)); + return -ENXIO; + } + /* * Until standardization materializes we need to consider 4 * different command sets. Note, that checking for function0 (bit0) @@ -1318,18 +1416,41 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, return 0; } +static void shutdown_dimm_notify(void *data) +{ + struct acpi_nfit_desc *acpi_desc = data; + struct nfit_mem *nfit_mem; + + mutex_lock(&acpi_desc->init_mutex); + /* + * Clear out the nfit_mem->flags_attr and shut down dimm event + * notifications. + */ + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { + struct acpi_device *adev_dimm = nfit_mem->adev; + + if (nfit_mem->flags_attr) { + sysfs_put(nfit_mem->flags_attr); + nfit_mem->flags_attr = NULL; + } + if (adev_dimm) + acpi_remove_notify_handler(adev_dimm->handle, + ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify); + } + mutex_unlock(&acpi_desc->init_mutex); +} + static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) { struct nfit_mem *nfit_mem; - int dimm_count = 0; + int dimm_count = 0, rc; + struct nvdimm *nvdimm; list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { struct acpi_nfit_flush_address *flush; unsigned long flags = 0, cmd_mask; - struct nvdimm *nvdimm; u32 device_handle; u16 mem_flags; - int rc; device_handle = __to_nfit_memdev(nfit_mem)->device_handle; nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle); @@ -1382,7 +1503,30 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) } - return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count); + rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count); + if (rc) + return rc; + + /* + * Now that dimms are successfully registered, and async registration + * is flushed, attempt to enable event notification. + */ + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { + struct kernfs_node *nfit_kernfs; + + nvdimm = nfit_mem->nvdimm; + nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); + if (nfit_kernfs) + nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, + "flags"); + sysfs_put(nfit_kernfs); + if (!nfit_mem->flags_attr) + dev_warn(acpi_desc->dev, "%s: notifications disabled\n", + nvdimm_name(nvdimm)); + } + + return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify, + acpi_desc); } static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) @@ -1491,9 +1635,9 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, if (!info) return -ENOMEM; for (i = 0; i < nr; i++) { - struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; struct nfit_set_info_map *map = &info->mapping[i]; - struct nvdimm *nvdimm = nd_mapping->nvdimm; + struct nvdimm *nvdimm = mapping->nvdimm; struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_nfit_memory_map *memdev = memdev_from_spa(acpi_desc, spa->range_index, i); @@ -1917,7 +2061,7 @@ static int acpi_nfit_insert_resource(struct acpi_nfit_desc *acpi_desc, } static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, - struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc, + struct nd_mapping_desc *mapping, struct nd_region_desc *ndr_desc, struct acpi_nfit_memory_map *memdev, struct nfit_spa *nfit_spa) { @@ -1934,12 +2078,12 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, return -ENODEV; } - nd_mapping->nvdimm = nvdimm; + mapping->nvdimm = nvdimm; switch (nfit_spa_type(spa)) { case NFIT_SPA_PM: case NFIT_SPA_VOLATILE: - nd_mapping->start = memdev->address; - nd_mapping->size = memdev->region_size; + mapping->start = memdev->address; + mapping->size = memdev->region_size; break; case NFIT_SPA_DCR: nfit_mem = nvdimm_provider_data(nvdimm); @@ -1947,13 +2091,13 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, dev_dbg(acpi_desc->dev, "spa%d %s missing bdw\n", spa->range_index, nvdimm_name(nvdimm)); } else { - nd_mapping->size = nfit_mem->bdw->capacity; - nd_mapping->start = nfit_mem->bdw->start_address; + mapping->size = nfit_mem->bdw->capacity; + mapping->start = nfit_mem->bdw->start_address; ndr_desc->num_lanes = nfit_mem->bdw->windows; blk_valid = 1; } - ndr_desc->nd_mapping = nd_mapping; + ndr_desc->mapping = mapping; ndr_desc->num_mappings = blk_valid; ndbr_desc = to_blk_region_desc(ndr_desc); ndbr_desc->enable = acpi_nfit_blk_region_enable; @@ -1979,7 +2123,7 @@ static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa) static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa) { - static struct nd_mapping nd_mappings[ND_MAX_MAPPINGS]; + static struct nd_mapping_desc mappings[ND_MAX_MAPPINGS]; struct acpi_nfit_system_address *spa = nfit_spa->spa; struct nd_blk_region_desc ndbr_desc; struct nd_region_desc *ndr_desc; @@ -1998,7 +2142,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, } memset(&res, 0, sizeof(res)); - memset(&nd_mappings, 0, sizeof(nd_mappings)); + memset(&mappings, 0, sizeof(mappings)); memset(&ndbr_desc, 0, sizeof(ndbr_desc)); res.start = spa->address; res.end = res.start + spa->length - 1; @@ -2014,7 +2158,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev; - struct nd_mapping *nd_mapping; + struct nd_mapping_desc *mapping; if (memdev->range_index != spa->range_index) continue; @@ -2023,14 +2167,14 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, spa->range_index, ND_MAX_MAPPINGS); return -ENXIO; } - nd_mapping = &nd_mappings[count++]; - rc = acpi_nfit_init_mapping(acpi_desc, nd_mapping, ndr_desc, + mapping = &mappings[count++]; + rc = acpi_nfit_init_mapping(acpi_desc, mapping, ndr_desc, memdev, nfit_spa); if (rc) goto out; } - ndr_desc->nd_mapping = nd_mappings; + ndr_desc->mapping = mappings; ndr_desc->num_mappings = count; rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa); if (rc) @@ -2678,29 +2822,30 @@ static int acpi_nfit_remove(struct acpi_device *adev) return 0; } -static void acpi_nfit_notify(struct acpi_device *adev, u32 event) +void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event) { - struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev); + struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; - struct device *dev = &adev->dev; union acpi_object *obj; acpi_status status; int ret; dev_dbg(dev, "%s: event: %d\n", __func__, event); - device_lock(dev); + if (event != NFIT_NOTIFY_UPDATE) + return; + if (!dev->driver) { /* dev->driver may be null if we're being removed */ dev_dbg(dev, "%s: no driver found for dev\n", __func__); - goto out_unlock; + return; } if (!acpi_desc) { acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); if (!acpi_desc) - goto out_unlock; - acpi_nfit_desc_init(acpi_desc, &adev->dev); + return; + acpi_nfit_desc_init(acpi_desc, dev); } else { /* * Finish previous registration before considering new @@ -2710,10 +2855,10 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) } /* Evaluate _FIT */ - status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); + status = acpi_evaluate_object(handle, "_FIT", NULL, &buf); if (ACPI_FAILURE(status)) { dev_err(dev, "failed to evaluate _FIT\n"); - goto out_unlock; + return; } obj = buf.pointer; @@ -2725,9 +2870,14 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) } else dev_err(dev, "Invalid _FIT\n"); kfree(buf.pointer); +} +EXPORT_SYMBOL_GPL(__acpi_nfit_notify); - out_unlock: - device_unlock(dev); +static void acpi_nfit_notify(struct acpi_device *adev, u32 event) +{ + device_lock(&adev->dev); + __acpi_nfit_notify(&adev->dev, adev->handle, event); + device_unlock(&adev->dev); } static const struct acpi_device_id acpi_nfit_ids[] = { diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c index 161f91539ae6..e5ce81c38eed 100644 --- a/drivers/acpi/nfit/mce.c +++ b/drivers/acpi/nfit/mce.c @@ -14,6 +14,7 @@ */ #include <linux/notifier.h> #include <linux/acpi.h> +#include <linux/nd.h> #include <asm/mce.h> #include "nfit.h" @@ -62,12 +63,25 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val, } mutex_unlock(&acpi_desc->init_mutex); - /* - * We can ignore an -EBUSY here because if an ARS is already - * in progress, just let that be the last authoritative one - */ - if (found_match) + if (!found_match) + continue; + + /* If this fails due to an -ENOMEM, there is little we can do */ + nvdimm_bus_add_poison(acpi_desc->nvdimm_bus, + ALIGN(mce->addr, L1_CACHE_BYTES), + L1_CACHE_BYTES); + nvdimm_region_notify(nfit_spa->nd_region, + NVDIMM_REVALIDATE_POISON); + + if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) { + /* + * We can ignore an -EBUSY here because if an ARS is + * already in progress, just let that be the last + * authoritative one + */ acpi_nfit_ars_rescan(acpi_desc); + } + break; } mutex_unlock(&acpi_desc_lock); diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index e894ded24d99..14296f5267c8 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -78,6 +78,14 @@ enum { NFIT_ARS_TIMEOUT = 90, }; +enum nfit_root_notifiers { + NFIT_NOTIFY_UPDATE = 0x80, +}; + +enum nfit_dimm_notifiers { + NFIT_NOTIFY_DIMM_HEALTH = 0x81, +}; + struct nfit_spa { struct list_head list; struct nd_region *nd_region; @@ -124,6 +132,7 @@ struct nfit_mem { struct acpi_nfit_system_address *spa_bdw; struct acpi_nfit_interleave *idt_dcr; struct acpi_nfit_interleave *idt_bdw; + struct kernfs_node *flags_attr; struct nfit_flush *nfit_flush; struct list_head list; struct acpi_device *adev; @@ -152,6 +161,7 @@ struct acpi_nfit_desc { struct list_head list; struct kernfs_node *scrub_count_state; unsigned int scrub_count; + unsigned int scrub_mode; unsigned int cancel:1; unsigned long dimm_cmd_force_en; unsigned long bus_cmd_force_en; @@ -159,6 +169,11 @@ struct acpi_nfit_desc { void *iobuf, u64 len, int rw); }; +enum scrub_mode { + HW_ERROR_SCRUB_OFF, + HW_ERROR_SCRUB_ON, +}; + enum nd_blk_mmio_selector { BDW, DCR, @@ -223,5 +238,7 @@ static inline struct acpi_nfit_desc *to_acpi_desc( const u8 *to_nfit_uuid(enum nfit_uuids id); int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *nfit, acpi_size sz); +void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event); +void __acpi_nvdimm_notify(struct device *dev, u32 event); void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev); #endif /* __NFIT_H__ */ diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig index cedab7572de3..daadd20aa936 100644 --- a/drivers/dax/Kconfig +++ b/drivers/dax/Kconfig @@ -23,4 +23,9 @@ config DEV_DAX_PMEM Say Y if unsure +config NR_DEV_DAX + int "Maximum number of Device-DAX instances" + default 32768 + range 256 2147483647 + endif diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c index 29f600f2c447..0e499bfca41c 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c @@ -13,15 +13,25 @@ #include <linux/pagemap.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/mount.h> #include <linux/pfn_t.h> +#include <linux/hash.h> +#include <linux/cdev.h> #include <linux/slab.h> #include <linux/dax.h> #include <linux/fs.h> #include <linux/mm.h> +#include "dax.h" -static int dax_major; +static dev_t dax_devt; static struct class *dax_class; static DEFINE_IDA(dax_minor_ida); +static int nr_dax = CONFIG_NR_DEV_DAX; +module_param(nr_dax, int, S_IRUGO); +static struct vfsmount *dax_mnt; +static struct kmem_cache *dax_cache __read_mostly; +static struct super_block *dax_superblock __read_mostly; +MODULE_PARM_DESC(nr_dax, "max number of device-dax instances"); /** * struct dax_region - mapping infrastructure for dax devices @@ -48,7 +58,7 @@ struct dax_region { * struct dax_dev - subdivision of a dax region * @region - parent region * @dev - device backing the character device - * @kref - enable this data to be tracked in filp->private_data + * @cdev - core chardev data * @alive - !alive + rcu grace period == no new mappings can be established * @id - child id in the region * @num_resources - number of physical address extents in this device @@ -56,41 +66,139 @@ struct dax_region { */ struct dax_dev { struct dax_region *region; - struct device *dev; - struct kref kref; + struct inode *inode; + struct device dev; + struct cdev cdev; bool alive; int id; int num_resources; struct resource res[0]; }; -static void dax_region_free(struct kref *kref) +static struct inode *dax_alloc_inode(struct super_block *sb) { - struct dax_region *dax_region; + return kmem_cache_alloc(dax_cache, GFP_KERNEL); +} - dax_region = container_of(kref, struct dax_region, kref); - kfree(dax_region); +static void dax_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + + kmem_cache_free(dax_cache, inode); } -void dax_region_put(struct dax_region *dax_region) +static void dax_destroy_inode(struct inode *inode) { - kref_put(&dax_region->kref, dax_region_free); + call_rcu(&inode->i_rcu, dax_i_callback); } -EXPORT_SYMBOL_GPL(dax_region_put); -static void dax_dev_free(struct kref *kref) +static const struct super_operations dax_sops = { + .statfs = simple_statfs, + .alloc_inode = dax_alloc_inode, + .destroy_inode = dax_destroy_inode, + .drop_inode = generic_delete_inode, +}; + +static struct dentry *dax_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - struct dax_dev *dax_dev; + return mount_pseudo(fs_type, "dax:", &dax_sops, NULL, DAXFS_MAGIC); +} - dax_dev = container_of(kref, struct dax_dev, kref); - dax_region_put(dax_dev->region); - kfree(dax_dev); +static struct file_system_type dax_type = { + .name = "dax", + .mount = dax_mount, + .kill_sb = kill_anon_super, +}; + +static int dax_test(struct inode *inode, void *data) +{ + return inode->i_cdev == data; +} + +static int dax_set(struct inode *inode, void *data) +{ + inode->i_cdev = data; + return 0; +} + +static struct inode *dax_inode_get(struct cdev *cdev, dev_t devt) +{ + struct inode *inode; + + inode = iget5_locked(dax_superblock, hash_32(devt + DAXFS_MAGIC, 31), + dax_test, dax_set, cdev); + + if (!inode) + return NULL; + + if (inode->i_state & I_NEW) { + inode->i_mode = S_IFCHR; + inode->i_flags = S_DAX; + inode->i_rdev = devt; + mapping_set_gfp_mask(&inode->i_data, GFP_USER); + unlock_new_inode(inode); + } + return inode; +} + +static void init_once(void *inode) +{ + inode_init_once(inode); +} + +static int dax_inode_init(void) +{ + int rc; + + dax_cache = kmem_cache_create("dax_cache", sizeof(struct inode), 0, + (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD|SLAB_ACCOUNT), + init_once); + if (!dax_cache) + return -ENOMEM; + + rc = register_filesystem(&dax_type); + if (rc) + goto err_register_fs; + + dax_mnt = kern_mount(&dax_type); + if (IS_ERR(dax_mnt)) { + rc = PTR_ERR(dax_mnt); + goto err_mount; + } + dax_superblock = dax_mnt->mnt_sb; + + return 0; + + err_mount: + unregister_filesystem(&dax_type); + err_register_fs: + kmem_cache_destroy(dax_cache); + + return rc; } -static void dax_dev_put(struct dax_dev *dax_dev) +static void dax_inode_exit(void) +{ + kern_unmount(dax_mnt); + unregister_filesystem(&dax_type); + kmem_cache_destroy(dax_cache); +} + +static void dax_region_free(struct kref *kref) +{ + struct dax_region *dax_region; + + dax_region = container_of(kref, struct dax_region, kref); + kfree(dax_region); +} + +void dax_region_put(struct dax_region *dax_region) { - kref_put(&dax_dev->kref, dax_dev_free); + kref_put(&dax_region->kref, dax_region_free); } +EXPORT_SYMBOL_GPL(dax_region_put); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, void *addr, @@ -98,8 +206,11 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, { struct dax_region *dax_region; - dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); + if (!IS_ALIGNED(res->start, align) + || !IS_ALIGNED(resource_size(res), align)) + return NULL; + dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); if (!dax_region) return NULL; @@ -116,10 +227,15 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, } EXPORT_SYMBOL_GPL(alloc_dax_region); +static struct dax_dev *to_dax_dev(struct device *dev) +{ + return container_of(dev, struct dax_dev, dev); +} + static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dax_dev *dax_dev = dev_get_drvdata(dev); + struct dax_dev *dax_dev = to_dax_dev(dev); unsigned long long size = 0; int i; @@ -144,180 +260,11 @@ static const struct attribute_group *dax_attribute_groups[] = { NULL, }; -static void unregister_dax_dev(void *_dev) -{ - struct device *dev = _dev; - struct dax_dev *dax_dev = dev_get_drvdata(dev); - struct dax_region *dax_region = dax_dev->region; - - dev_dbg(dev, "%s\n", __func__); - - /* - * Note, rcu is not protecting the liveness of dax_dev, rcu is - * ensuring that any fault handlers that might have seen - * dax_dev->alive == true, have completed. Any fault handlers - * that start after synchronize_rcu() has started will abort - * upon seeing dax_dev->alive == false. - */ - dax_dev->alive = false; - synchronize_rcu(); - - get_device(dev); - device_unregister(dev); - ida_simple_remove(&dax_region->ida, dax_dev->id); - ida_simple_remove(&dax_minor_ida, MINOR(dev->devt)); - put_device(dev); - dax_dev_put(dax_dev); -} - -int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res, - int count) -{ - struct device *parent = dax_region->dev; - struct dax_dev *dax_dev; - struct device *dev; - int rc, minor; - dev_t dev_t; - - dax_dev = kzalloc(sizeof(*dax_dev) + sizeof(*res) * count, GFP_KERNEL); - if (!dax_dev) - return -ENOMEM; - memcpy(dax_dev->res, res, sizeof(*res) * count); - dax_dev->num_resources = count; - kref_init(&dax_dev->kref); - dax_dev->alive = true; - dax_dev->region = dax_region; - kref_get(&dax_region->kref); - - dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); - if (dax_dev->id < 0) { - rc = dax_dev->id; - goto err_id; - } - - minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL); - if (minor < 0) { - rc = minor; - goto err_minor; - } - - dev_t = MKDEV(dax_major, minor); - dev = device_create_with_groups(dax_class, parent, dev_t, dax_dev, - dax_attribute_groups, "dax%d.%d", dax_region->id, - dax_dev->id); - if (IS_ERR(dev)) { - rc = PTR_ERR(dev); - goto err_create; - } - dax_dev->dev = dev; - - rc = devm_add_action_or_reset(dax_region->dev, unregister_dax_dev, dev); - if (rc) - return rc; - - return 0; - - err_create: - ida_simple_remove(&dax_minor_ida, minor); - err_minor: - ida_simple_remove(&dax_region->ida, dax_dev->id); - err_id: - dax_dev_put(dax_dev); - - return rc; -} -EXPORT_SYMBOL_GPL(devm_create_dax_dev); - -/* return an unmapped area aligned to the dax region specified alignment */ -static unsigned long dax_dev_get_unmapped_area(struct file *filp, - unsigned long addr, unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - unsigned long off, off_end, off_align, len_align, addr_align, align; - struct dax_dev *dax_dev = filp ? filp->private_data : NULL; - struct dax_region *dax_region; - - if (!dax_dev || addr) - goto out; - - dax_region = dax_dev->region; - align = dax_region->align; - off = pgoff << PAGE_SHIFT; - off_end = off + len; - off_align = round_up(off, align); - - if ((off_end <= off_align) || ((off_end - off_align) < align)) - goto out; - - len_align = len + align; - if ((off + len_align) < off) - goto out; - - addr_align = current->mm->get_unmapped_area(filp, addr, len_align, - pgoff, flags); - if (!IS_ERR_VALUE(addr_align)) { - addr_align += (off - addr_align) & (align - 1); - return addr_align; - } - out: - return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags); -} - -static int __match_devt(struct device *dev, const void *data) -{ - const dev_t *devt = data; - - return dev->devt == *devt; -} - -static struct device *dax_dev_find(dev_t dev_t) -{ - return class_find_device(dax_class, NULL, &dev_t, __match_devt); -} - -static int dax_dev_open(struct inode *inode, struct file *filp) -{ - struct dax_dev *dax_dev = NULL; - struct device *dev; - - dev = dax_dev_find(inode->i_rdev); - if (!dev) - return -ENXIO; - - device_lock(dev); - dax_dev = dev_get_drvdata(dev); - if (dax_dev) { - dev_dbg(dev, "%s\n", __func__); - filp->private_data = dax_dev; - kref_get(&dax_dev->kref); - inode->i_flags = S_DAX; - } - device_unlock(dev); - - if (!dax_dev) { - put_device(dev); - return -ENXIO; - } - return 0; -} - -static int dax_dev_release(struct inode *inode, struct file *filp) -{ - struct dax_dev *dax_dev = filp->private_data; - struct device *dev = dax_dev->dev; - - dev_dbg(dax_dev->dev, "%s\n", __func__); - dax_dev_put(dax_dev); - put_device(dev); - - return 0; -} - static int check_vma(struct dax_dev *dax_dev, struct vm_area_struct *vma, const char *func) { struct dax_region *dax_region = dax_dev->region; - struct device *dev = dax_dev->dev; + struct device *dev = &dax_dev->dev; unsigned long mask; if (!dax_dev->alive) @@ -382,7 +329,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma, struct vm_fault *vmf) { unsigned long vaddr = (unsigned long) vmf->virtual_address; - struct device *dev = dax_dev->dev; + struct device *dev = &dax_dev->dev; struct dax_region *dax_region; int rc = VM_FAULT_SIGBUS; phys_addr_t phys; @@ -422,7 +369,7 @@ static int dax_dev_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct file *filp = vma->vm_file; struct dax_dev *dax_dev = filp->private_data; - dev_dbg(dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__, + dev_dbg(&dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__, current->comm, (vmf->flags & FAULT_FLAG_WRITE) ? "write" : "read", vma->vm_start, vma->vm_end); rcu_read_lock(); @@ -437,7 +384,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, unsigned int flags) { unsigned long pmd_addr = addr & PMD_MASK; - struct device *dev = dax_dev->dev; + struct device *dev = &dax_dev->dev; struct dax_region *dax_region; phys_addr_t phys; pgoff_t pgoff; @@ -479,7 +426,7 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr, struct file *filp = vma->vm_file; struct dax_dev *dax_dev = filp->private_data; - dev_dbg(dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__, + dev_dbg(&dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__, current->comm, (flags & FAULT_FLAG_WRITE) ? "write" : "read", vma->vm_start, vma->vm_end); @@ -490,81 +437,257 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr, return rc; } -static void dax_dev_vm_open(struct vm_area_struct *vma) -{ - struct file *filp = vma->vm_file; - struct dax_dev *dax_dev = filp->private_data; - - dev_dbg(dax_dev->dev, "%s\n", __func__); - kref_get(&dax_dev->kref); -} - -static void dax_dev_vm_close(struct vm_area_struct *vma) -{ - struct file *filp = vma->vm_file; - struct dax_dev *dax_dev = filp->private_data; - - dev_dbg(dax_dev->dev, "%s\n", __func__); - dax_dev_put(dax_dev); -} - static const struct vm_operations_struct dax_dev_vm_ops = { .fault = dax_dev_fault, .pmd_fault = dax_dev_pmd_fault, - .open = dax_dev_vm_open, - .close = dax_dev_vm_close, }; -static int dax_dev_mmap(struct file *filp, struct vm_area_struct *vma) +static int dax_mmap(struct file *filp, struct vm_area_struct *vma) { struct dax_dev *dax_dev = filp->private_data; int rc; - dev_dbg(dax_dev->dev, "%s\n", __func__); + dev_dbg(&dax_dev->dev, "%s\n", __func__); rc = check_vma(dax_dev, vma, __func__); if (rc) return rc; - kref_get(&dax_dev->kref); vma->vm_ops = &dax_dev_vm_ops; vma->vm_flags |= VM_MIXEDMAP | VM_HUGEPAGE; return 0; +} + +/* return an unmapped area aligned to the dax region specified alignment */ +static unsigned long dax_get_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + unsigned long off, off_end, off_align, len_align, addr_align, align; + struct dax_dev *dax_dev = filp ? filp->private_data : NULL; + struct dax_region *dax_region; + + if (!dax_dev || addr) + goto out; + + dax_region = dax_dev->region; + align = dax_region->align; + off = pgoff << PAGE_SHIFT; + off_end = off + len; + off_align = round_up(off, align); + + if ((off_end <= off_align) || ((off_end - off_align) < align)) + goto out; + + len_align = len + align; + if ((off + len_align) < off) + goto out; + addr_align = current->mm->get_unmapped_area(filp, addr, len_align, + pgoff, flags); + if (!IS_ERR_VALUE(addr_align)) { + addr_align += (off - addr_align) & (align - 1); + return addr_align; + } + out: + return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags); +} + +static int dax_open(struct inode *inode, struct file *filp) +{ + struct dax_dev *dax_dev; + + dax_dev = container_of(inode->i_cdev, struct dax_dev, cdev); + dev_dbg(&dax_dev->dev, "%s\n", __func__); + inode->i_mapping = dax_dev->inode->i_mapping; + inode->i_mapping->host = dax_dev->inode; + filp->f_mapping = inode->i_mapping; + filp->private_data = dax_dev; + inode->i_flags = S_DAX; + + return 0; +} + +static int dax_release(struct inode *inode, struct file *filp) +{ + struct dax_dev *dax_dev = filp->private_data; + + dev_dbg(&dax_dev->dev, "%s\n", __func__); + return 0; } static const struct file_operations dax_fops = { .llseek = noop_llseek, .owner = THIS_MODULE, - .open = dax_dev_open, - .release = dax_dev_release, - .get_unmapped_area = dax_dev_get_unmapped_area, - .mmap = dax_dev_mmap, + .open = dax_open, + .release = dax_release, + .get_unmapped_area = dax_get_unmapped_area, + .mmap = dax_mmap, }; +static void dax_dev_release(struct device *dev) +{ + struct dax_dev *dax_dev = to_dax_dev(dev); + struct dax_region *dax_region = dax_dev->region; + + ida_simple_remove(&dax_region->ida, dax_dev->id); + ida_simple_remove(&dax_minor_ida, MINOR(dev->devt)); + dax_region_put(dax_region); + iput(dax_dev->inode); + kfree(dax_dev); +} + +static void unregister_dax_dev(void *dev) +{ + struct dax_dev *dax_dev = to_dax_dev(dev); + struct cdev *cdev = &dax_dev->cdev; + + dev_dbg(dev, "%s\n", __func__); + + /* + * Note, rcu is not protecting the liveness of dax_dev, rcu is + * ensuring that any fault handlers that might have seen + * dax_dev->alive == true, have completed. Any fault handlers + * that start after synchronize_rcu() has started will abort + * upon seeing dax_dev->alive == false. + */ + dax_dev->alive = false; + synchronize_rcu(); + unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1); + cdev_del(cdev); + device_unregister(dev); +} + +struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region, + struct resource *res, int count) +{ + struct device *parent = dax_region->dev; + struct dax_dev *dax_dev; + int rc = 0, minor, i; + struct device *dev; + struct cdev *cdev; + dev_t dev_t; + + dax_dev = kzalloc(sizeof(*dax_dev) + sizeof(*res) * count, GFP_KERNEL); + if (!dax_dev) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < count; i++) { + if (!IS_ALIGNED(res[i].start, dax_region->align) + || !IS_ALIGNED(resource_size(&res[i]), + dax_region->align)) { + rc = -EINVAL; + break; + } + dax_dev->res[i].start = res[i].start; + dax_dev->res[i].end = res[i].end; + } + + if (i < count) + goto err_id; + + dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); + if (dax_dev->id < 0) { + rc = dax_dev->id; + goto err_id; + } + + minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL); + if (minor < 0) { + rc = minor; + goto err_minor; + } + + dev_t = MKDEV(MAJOR(dax_devt), minor); + dev = &dax_dev->dev; + dax_dev->inode = dax_inode_get(&dax_dev->cdev, dev_t); + if (!dax_dev->inode) { + rc = -ENOMEM; + goto err_inode; + } + + /* device_initialize() so cdev can reference kobj parent */ + device_initialize(dev); + + cdev = &dax_dev->cdev; + cdev_init(cdev, &dax_fops); + cdev->owner = parent->driver->owner; + cdev->kobj.parent = &dev->kobj; + rc = cdev_add(&dax_dev->cdev, dev_t, 1); + if (rc) + goto err_cdev; + + /* from here on we're committed to teardown via dax_dev_release() */ + dax_dev->num_resources = count; + dax_dev->alive = true; + dax_dev->region = dax_region; + kref_get(&dax_region->kref); + + dev->devt = dev_t; + dev->class = dax_class; + dev->parent = parent; + dev->groups = dax_attribute_groups; + dev->release = dax_dev_release; + dev_set_name(dev, "dax%d.%d", dax_region->id, dax_dev->id); + rc = device_add(dev); + if (rc) { + put_device(dev); + return ERR_PTR(rc); + } + + rc = devm_add_action_or_reset(dax_region->dev, unregister_dax_dev, dev); + if (rc) + return ERR_PTR(rc); + + return dax_dev; + + err_cdev: + iput(dax_dev->inode); + err_inode: + ida_simple_remove(&dax_minor_ida, minor); + err_minor: + ida_simple_remove(&dax_region->ida, dax_dev->id); + err_id: + kfree(dax_dev); + + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(devm_create_dax_dev); + static int __init dax_init(void) { int rc; - rc = register_chrdev(0, "dax", &dax_fops); - if (rc < 0) + rc = dax_inode_init(); + if (rc) return rc; - dax_major = rc; + + nr_dax = max(nr_dax, 256); + rc = alloc_chrdev_region(&dax_devt, 0, nr_dax, "dax"); + if (rc) + goto err_chrdev; dax_class = class_create(THIS_MODULE, "dax"); if (IS_ERR(dax_class)) { - unregister_chrdev(dax_major, "dax"); - return PTR_ERR(dax_class); + rc = PTR_ERR(dax_class); + goto err_class; } return 0; + + err_class: + unregister_chrdev_region(dax_devt, nr_dax); + err_chrdev: + dax_inode_exit(); + return rc; } static void __exit dax_exit(void) { class_destroy(dax_class); - unregister_chrdev(dax_major, "dax"); + unregister_chrdev_region(dax_devt, nr_dax); ida_destroy(&dax_minor_ida); + dax_inode_exit(); } MODULE_AUTHOR("Intel Corporation"); diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h index d8b8f1f25054..ddd829ab58c0 100644 --- a/drivers/dax/dax.h +++ b/drivers/dax/dax.h @@ -13,12 +13,13 @@ #ifndef __DAX_H__ #define __DAX_H__ struct device; +struct dax_dev; struct resource; struct dax_region; void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, void *addr, unsigned long flags); -int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res, - int count); +struct dax_dev *devm_create_dax_dev(struct dax_region *dax_region, + struct resource *res, int count); #endif /* __DAX_H__ */ diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 1f01e98c83c7..9630d8837ba9 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -24,7 +24,7 @@ struct dax_pmem { struct completion cmp; }; -struct dax_pmem *to_dax_pmem(struct percpu_ref *ref) +static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref) { return container_of(ref, struct dax_pmem, ref); } @@ -61,6 +61,7 @@ static int dax_pmem_probe(struct device *dev) int rc; void *addr; struct resource res; + struct dax_dev *dax_dev; struct nd_pfn_sb *pfn_sb; struct dax_pmem *dax_pmem; struct nd_region *nd_region; @@ -126,12 +127,12 @@ static int dax_pmem_probe(struct device *dev) return -ENOMEM; /* TODO: support for subdividing a dax region... */ - rc = devm_create_dax_dev(dax_region, &res, 1); + dax_dev = devm_create_dax_dev(dax_region, &res, 1); /* child dax_dev instances now own the lifetime of the dax_region */ dax_region_put(dax_region); - return rc; + return PTR_ERR_OR_ZERO(dax_dev); } static struct nd_device_driver dax_pmem_driver = { diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 83f61c513b7e..465d344f3391 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -38,7 +38,6 @@ config DRM_EXYNOS7_DECON config DRM_EXYNOS_MIXER bool "Mixer" - depends on !VIDEO_SAMSUNG_S5P_TV help Choose this option if you want to use Exynos Mixer for DRM. @@ -77,7 +76,7 @@ config DRM_EXYNOS_DP config DRM_EXYNOS_HDMI bool "HDMI" - depends on !VIDEO_SAMSUNG_S5P_TV && (DRM_EXYNOS_MIXER || DRM_EXYNOS5433_DECON) + depends on DRM_EXYNOS_MIXER || DRM_EXYNOS5433_DECON help Choose this option if you want to use Exynos HDMI for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h index c8de4913fdbe..87f6b5672e11 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h @@ -66,7 +66,7 @@ static inline int __exynos_iommu_create_mapping(struct exynos_drm_private *priv, if (ret) goto free_domain; - ret = iommu_dma_init_domain(domain, start, size); + ret = iommu_dma_init_domain(domain, start, size, NULL); if (ret) goto put_cookie; diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f73df2495fed..4c8a55857e00 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -61,3 +61,14 @@ config RMI4_F30 Function 30 provides GPIO and LED support for RMI4 devices. This includes support for buttons on TouchPads and ClickPads. + +config RMI4_F54 + bool "RMI4 Function 54 (Analog diagnostics)" + depends on RMI4_CORE + depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) + select VIDEOBUF2_VMALLOC + help + Say Y here if you want to add support for RMI4 function 54 + + Function 54 provides access to various diagnostic features in certain + RMI4 touch sensors. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 95c00a783992..0bafc8502c4b 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o +rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index e0b5a45e2b15..ef8c747c35e7 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -311,6 +311,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif +#ifdef CONFIG_RMI4_F54 + &rmi_f54_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 6e140fa3cce1..8dfbebe9bf86 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -102,4 +102,5 @@ extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; +extern struct rmi_function_handler rmi_f54_handler; #endif diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c new file mode 100644 index 000000000000..cf805b960866 --- /dev/null +++ b/drivers/input/rmi4/rmi_f54.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2012-2015 Synaptics Incorporated + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * 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/kernel.h> +#include <linux/rmi.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> +#include "rmi_driver.h" + +#define F54_NAME "rmi4_f54" + +/* F54 data offsets */ +#define F54_REPORT_DATA_OFFSET 3 +#define F54_FIFO_OFFSET 1 +#define F54_NUM_TX_OFFSET 1 +#define F54_NUM_RX_OFFSET 0 + +/* F54 commands */ +#define F54_GET_REPORT 1 +#define F54_FORCE_CAL 2 + +/* Fixed sizes of reports */ +#define F54_QUERY_LEN 27 + +/* F54 capabilities */ +#define F54_CAP_BASELINE (1 << 2) +#define F54_CAP_IMAGE8 (1 << 3) +#define F54_CAP_IMAGE16 (1 << 6) + +/** + * enum rmi_f54_report_type - RMI4 F54 report types + * + * @F54_8BIT_IMAGE: Normalized 8-Bit Image Report. The capacitance variance + * from baseline for each pixel. + * + * @F54_16BIT_IMAGE: Normalized 16-Bit Image Report. The capacitance variance + * from baseline for each pixel. + * + * @F54_RAW_16BIT_IMAGE: + * Raw 16-Bit Image Report. The raw capacitance for each + * pixel. + * + * @F54_TRUE_BASELINE: True Baseline Report. The baseline capacitance for each + * pixel. + * + * @F54_FULL_RAW_CAP: Full Raw Capacitance Report. The raw capacitance with + * low reference set to its minimum value and high + * reference set to its maximum value. + * + * @F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + * Full Raw Capacitance with Receiver Offset Removed + * Report. Set Low reference to its minimum value and high + * references to its maximum value, then report the raw + * capacitance for each pixel. + */ +enum rmi_f54_report_type { + F54_REPORT_NONE = 0, + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_OFFSET_REMOVED = 20, + F54_MAX_REPORT_TYPE, +}; + +const char *rmi_f54_report_type_names[] = { + [F54_REPORT_NONE] = "Unknown", + [F54_8BIT_IMAGE] = "Normalized 8-Bit Image", + [F54_16BIT_IMAGE] = "Normalized 16-Bit Image", + [F54_RAW_16BIT_IMAGE] = "Raw 16-Bit Image", + [F54_TRUE_BASELINE] = "True Baseline", + [F54_FULL_RAW_CAP] = "Full Raw Capacitance", + [F54_FULL_RAW_CAP_RX_OFFSET_REMOVED] + = "Full Raw Capacitance RX Offset Removed", +}; + +struct rmi_f54_reports { + int start; + int size; +}; + +struct f54_data { + struct rmi_function *fn; + + u8 qry[F54_QUERY_LEN]; + u8 num_rx_electrodes; + u8 num_tx_electrodes; + u8 capabilities; + u16 clock_rate; + u8 family; + + enum rmi_f54_report_type report_type; + u8 *report_data; + int report_size; + struct rmi_f54_reports standard_report[2]; + + bool is_busy; + struct mutex status_mutex; + struct mutex data_mutex; + + struct workqueue_struct *workqueue; + struct delayed_work work; + unsigned long timeout; + + struct completion cmd_done; + + /* V4L2 support */ + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; + enum rmi_f54_report_type inputs[F54_MAX_REPORT_TYPE]; +}; + +/* + * Basic checks on report_type to ensure we write a valid type + * to the sensor. + */ +static bool is_f54_report_type_valid(struct f54_data *f54, + enum rmi_f54_report_type reptype) +{ + switch (reptype) { + case F54_8BIT_IMAGE: + return f54->capabilities & F54_CAP_IMAGE8; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + return f54->capabilities & F54_CAP_IMAGE16; + case F54_TRUE_BASELINE: + return f54->capabilities & F54_CAP_IMAGE16; + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + return true; + default: + return false; + } +} + +static enum rmi_f54_report_type rmi_f54_get_reptype(struct f54_data *f54, + unsigned int i) +{ + if (i >= F54_MAX_REPORT_TYPE) + return F54_REPORT_NONE; + + return f54->inputs[i]; +} + +static void rmi_f54_create_input_map(struct f54_data *f54) +{ + int i = 0; + enum rmi_f54_report_type reptype; + + for (reptype = 1; reptype < F54_MAX_REPORT_TYPE; reptype++) { + if (!is_f54_report_type_valid(f54, reptype)) + continue; + + f54->inputs[i++] = reptype; + } + + /* Remaining values are zero via kzalloc */ +} + +static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) +{ + struct f54_data *f54 = dev_get_drvdata(&fn->dev); + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + /* Write Report Type into F54_AD_Data0 */ + if (f54->report_type != report_type) { + error = rmi_write(rmi_dev, f54->fn->fd.data_base_addr, + report_type); + if (error) + return error; + f54->report_type = report_type; + } + + /* + * Small delay after disabling interrupts to avoid race condition + * in firmare. This value is a bit higher than absolutely necessary. + * Should be removed once issue is resolved in firmware. + */ + usleep_range(2000, 3000); + + mutex_lock(&f54->data_mutex); + + error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT); + if (error < 0) + return error; + + init_completion(&f54->cmd_done); + + f54->is_busy = 1; + f54->timeout = jiffies + msecs_to_jiffies(100); + + queue_delayed_work(f54->workqueue, &f54->work, 0); + + mutex_unlock(&f54->data_mutex); + + return 0; +} + +static size_t rmi_f54_get_report_size(struct f54_data *f54) +{ + u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes; + size_t size; + + switch (rmi_f54_get_reptype(f54, f54->input)) { + case F54_8BIT_IMAGE: + size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + size = sizeof(u16) * rx * tx; + break; + default: + size = 0; + } + + return size; +} + +static int rmi_f54_get_pixel_fmt(enum rmi_f54_report_type reptype, u32 *pixfmt) +{ + int ret = 0; + + switch (reptype) { + case F54_8BIT_IMAGE: + *pixfmt = V4L2_TCH_FMT_DELTA_TD08; + break; + + case F54_16BIT_IMAGE: + *pixfmt = V4L2_TCH_FMT_DELTA_TD16; + break; + + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED: + *pixfmt = V4L2_TCH_FMT_TU16; + break; + + case F54_REPORT_NONE: + case F54_MAX_REPORT_TYPE: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_file_operations rmi_f54_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static int rmi_f54_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct f54_data *f54 = q->drv_priv; + + if (*nplanes) + return sizes[0] < rmi_f54_get_report_size(f54) ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = rmi_f54_get_report_size(f54); + + return 0; +} + +static void rmi_f54_buffer_queue(struct vb2_buffer *vb) +{ + struct f54_data *f54 = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + enum vb2_buffer_state state; + enum rmi_f54_report_type reptype; + int ret; + + mutex_lock(&f54->status_mutex); + + reptype = rmi_f54_get_reptype(f54, f54->input); + if (reptype == F54_REPORT_NONE) { + state = VB2_BUF_STATE_ERROR; + goto done; + } + + if (f54->is_busy) { + state = VB2_BUF_STATE_ERROR; + goto done; + } + + ret = rmi_f54_request_report(f54->fn, reptype); + if (ret) { + dev_err(&f54->fn->dev, "Error requesting F54 report\n"); + state = VB2_BUF_STATE_ERROR; + goto done; + } + + /* get frame data */ + mutex_lock(&f54->data_mutex); + + while (f54->is_busy) { + mutex_unlock(&f54->data_mutex); + if (!wait_for_completion_timeout(&f54->cmd_done, + msecs_to_jiffies(1000))) { + dev_err(&f54->fn->dev, "Timed out\n"); + state = VB2_BUF_STATE_ERROR; + goto done; + } + mutex_lock(&f54->data_mutex); + } + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&f54->fn->dev, "Error acquiring frame ptr\n"); + state = VB2_BUF_STATE_ERROR; + goto data_done; + } + + memcpy(ptr, f54->report_data, f54->report_size); + vb2_set_plane_payload(vb, 0, rmi_f54_get_report_size(f54)); + state = VB2_BUF_STATE_DONE; + +data_done: + mutex_unlock(&f54->data_mutex); +done: + vb2_buffer_done(vb, state); + mutex_unlock(&f54->status_mutex); +} + +/* V4L2 structures */ +static const struct vb2_ops rmi_f54_queue_ops = { + .queue_setup = rmi_f54_queue_setup, + .buf_queue = rmi_f54_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue rmi_f54_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct vb2_buffer), + .ops = &rmi_f54_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int rmi_f54_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct f54_data *f54 = video_drvdata(file); + + strlcpy(cap->driver, F54_NAME, sizeof(cap->driver)); + strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "rmi4:%s", dev_name(&f54->fn->dev)); + + return 0; +} + +static int rmi_f54_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct f54_data *f54 = video_drvdata(file); + enum rmi_f54_report_type reptype; + + reptype = rmi_f54_get_reptype(f54, i->index); + if (reptype == F54_REPORT_NONE) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + + strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name)); + return 0; +} + +static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) +{ + struct v4l2_pix_format *f = &f54->format; + enum rmi_f54_report_type reptype; + int ret; + + reptype = rmi_f54_get_reptype(f54, i); + if (reptype == F54_REPORT_NONE) + return -EINVAL; + + ret = rmi_f54_get_pixel_fmt(reptype, &f->pixelformat); + if (ret) + return ret; + + f54->input = i; + + f->width = f54->num_rx_electrodes; + f->height = f54->num_tx_electrodes; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + return 0; +} + +static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return rmi_f54_set_input(video_drvdata(file), i); +} + +static int rmi_f54_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + struct f54_data *f54 = video_drvdata(file); + + *i = f54->input; + + return 0; +} + +static int rmi_f54_vidioc_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct f54_data *f54 = video_drvdata(file); + + f->fmt.pix = f54->format; + + return 0; +} + +static int rmi_f54_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD08; + break; + + case 2: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int rmi_f54_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops rmi_f54_video_ioctl_ops = { + .vidioc_querycap = rmi_f54_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = rmi_f54_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_g_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_try_fmt_vid_cap = rmi_f54_vidioc_fmt, + .vidioc_g_parm = rmi_f54_vidioc_g_parm, + + .vidioc_enum_input = rmi_f54_vidioc_enum_input, + .vidioc_g_input = rmi_f54_vidioc_g_input, + .vidioc_s_input = rmi_f54_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device rmi_f54_video_device = { + .name = "Synaptics RMI4", + .fops = &rmi_f54_video_fops, + .ioctl_ops = &rmi_f54_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + +static void rmi_f54_work(struct work_struct *work) +{ + struct f54_data *f54 = container_of(work, struct f54_data, work.work); + struct rmi_function *fn = f54->fn; + u8 fifo[2]; + struct rmi_f54_reports *report; + int report_size; + u8 command; + u8 *data; + int error; + + data = f54->report_data; + report_size = rmi_f54_get_report_size(f54); + if (report_size == 0) { + dev_err(&fn->dev, "Bad report size, report type=%d\n", + f54->report_type); + error = -EINVAL; + goto error; /* retry won't help */ + } + f54->standard_report[0].size = report_size; + report = f54->standard_report; + + mutex_lock(&f54->data_mutex); + + /* + * Need to check if command has completed. + * If not try again later. + */ + error = rmi_read(fn->rmi_dev, f54->fn->fd.command_base_addr, + &command); + if (error) { + dev_err(&fn->dev, "Failed to read back command\n"); + goto error; + } + if (command & F54_GET_REPORT) { + if (time_after(jiffies, f54->timeout)) { + dev_err(&fn->dev, "Get report command timed out\n"); + error = -ETIMEDOUT; + } + report_size = 0; + goto error; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n"); + + report_size = 0; + for (; report->size; report++) { + fifo[0] = report->start & 0xff; + fifo[1] = (report->start >> 8) & 0xff; + error = rmi_write_block(fn->rmi_dev, + fn->fd.data_base_addr + F54_FIFO_OFFSET, + fifo, sizeof(fifo)); + if (error) { + dev_err(&fn->dev, "Failed to set fifo start offset\n"); + goto abort; + } + + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + + F54_REPORT_DATA_OFFSET, data, + report->size); + if (error) { + dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", + __func__, report->size, error); + goto abort; + } + data += report->size; + report_size += report->size; + } + +abort: + f54->report_size = error ? 0 : report_size; +error: + if (error) + report_size = 0; + + if (report_size == 0 && !error) { + queue_delayed_work(f54->workqueue, &f54->work, + msecs_to_jiffies(1)); + } else { + f54->is_busy = false; + complete(&f54->cmd_done); + } + + mutex_unlock(&f54->data_mutex); +} + +static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits) +{ + return 0; +} + +static int rmi_f54_config(struct rmi_function *fn) +{ + struct rmi_driver *drv = fn->rmi_dev->driver; + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f54_detect(struct rmi_function *fn) +{ + int error; + struct f54_data *f54; + + f54 = dev_get_drvdata(&fn->dev); + + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + &f54->qry, sizeof(f54->qry)); + if (error) { + dev_err(&fn->dev, "%s: Failed to query F54 properties\n", + __func__); + return error; + } + + f54->num_rx_electrodes = f54->qry[0]; + f54->num_tx_electrodes = f54->qry[1]; + f54->capabilities = f54->qry[2]; + f54->clock_rate = f54->qry[3] | (f54->qry[4] << 8); + f54->family = f54->qry[5]; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_rx_electrodes: %d\n", + f54->num_rx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_tx_electrodes: %d\n", + f54->num_tx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 capabilities: 0x%x\n", + f54->capabilities); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 clock rate: 0x%x\n", + f54->clock_rate); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 family: 0x%x\n", + f54->family); + + f54->is_busy = false; + + return 0; +} + +static int rmi_f54_probe(struct rmi_function *fn) +{ + struct f54_data *f54; + int ret; + u8 rx, tx; + + f54 = devm_kzalloc(&fn->dev, sizeof(struct f54_data), GFP_KERNEL); + if (!f54) + return -ENOMEM; + + f54->fn = fn; + dev_set_drvdata(&fn->dev, f54); + + ret = rmi_f54_detect(fn); + if (ret) + return ret; + + mutex_init(&f54->data_mutex); + mutex_init(&f54->status_mutex); + + rx = f54->num_rx_electrodes; + tx = f54->num_tx_electrodes; + f54->report_data = devm_kzalloc(&fn->dev, + sizeof(u16) * tx * rx, + GFP_KERNEL); + if (f54->report_data == NULL) + return -ENOMEM; + + INIT_DELAYED_WORK(&f54->work, rmi_f54_work); + + f54->workqueue = create_singlethread_workqueue("rmi4-poller"); + if (!f54->workqueue) + return -ENOMEM; + + rmi_f54_create_input_map(f54); + + /* register video device */ + strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name)); + ret = v4l2_device_register(&fn->dev, &f54->v4l2); + if (ret) { + dev_err(&fn->dev, "Unable to register video dev.\n"); + goto remove_wq; + } + + /* initialize the queue */ + mutex_init(&f54->lock); + f54->queue = rmi_f54_queue; + f54->queue.drv_priv = f54; + f54->queue.lock = &f54->lock; + f54->queue.dev = &fn->dev; + + ret = vb2_queue_init(&f54->queue); + if (ret) + goto remove_v4l2; + + f54->vdev = rmi_f54_video_device; + f54->vdev.v4l2_dev = &f54->v4l2; + f54->vdev.lock = &f54->lock; + f54->vdev.vfl_dir = VFL_DIR_RX; + f54->vdev.queue = &f54->queue; + video_set_drvdata(&f54->vdev, f54); + + ret = video_register_device(&f54->vdev, VFL_TYPE_TOUCH, -1); + if (ret) { + dev_err(&fn->dev, "Unable to register video subdevice."); + goto remove_v4l2; + } + + return 0; + +remove_v4l2: + v4l2_device_unregister(&f54->v4l2); +remove_wq: + cancel_delayed_work_sync(&f54->work); + flush_workqueue(f54->workqueue); + destroy_workqueue(f54->workqueue); + return ret; +} + +static void rmi_f54_remove(struct rmi_function *fn) +{ + struct f54_data *f54 = dev_get_drvdata(&fn->dev); + + video_unregister_device(&f54->vdev); + v4l2_device_unregister(&f54->v4l2); +} + +struct rmi_function_handler rmi_f54_handler = { + .driver = { + .name = F54_NAME, + }, + .func = 0x54, + .probe = rmi_f54_probe, + .config = rmi_f54_config, + .attention = rmi_f54_attention, + .remove = rmi_f54_remove, +}; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 507981356921..efca0133e266 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -115,6 +115,15 @@ config TOUCHSCREEN_ATMEL_MXT To compile this driver as a module, choose M here: the module will be called atmel_mxt_ts. +config TOUCHSCREEN_ATMEL_MXT_T37 + bool "Support T37 Diagnostic Data" + depends on TOUCHSCREEN_ATMEL_MXT + depends on VIDEO_V4L2=y || (TOUCHSCREEN_ATMEL_MXT=m && VIDEO_V4L2=m) + select VIDEOBUF2_VMALLOC + help + Say Y here if you want support to output data from the T37 + Diagnostic Data object using a V4L device. + config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" depends on I2C diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 5af7907d0af4..e5d185fe69b9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 Samsung Electronics Co.Ltd * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2016 Zodiac Inflight Innovations * * Author: Joonyoung Shim <jy0922.shim@samsung.com> * @@ -28,6 +29,10 @@ #include <linux/of.h> #include <linux/slab.h> #include <asm/unaligned.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -99,6 +104,8 @@ struct t7_config { /* MXT_TOUCH_MULTI_T9 field */ #define MXT_T9_CTRL 0 +#define MXT_T9_XSIZE 3 +#define MXT_T9_YSIZE 4 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -119,11 +126,31 @@ struct t9_range { /* MXT_TOUCH_MULTI_T9 orient */ #define MXT_T9_ORIENT_SWITCH (1 << 0) +#define MXT_T9_ORIENT_INVERTX (1 << 1) +#define MXT_T9_ORIENT_INVERTY (1 << 2) /* MXT_SPT_COMMSCONFIG_T18 */ #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +/* MXT_DEBUG_DIAGNOSTIC_T37 */ +#define MXT_DIAGNOSTIC_PAGEUP 0x01 +#define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_REFS 0x11 +#define MXT_DIAGNOSTIC_SIZE 128 + +#define MXT_FAMILY_1386 160 +#define MXT1386_COLUMNS 3 +#define MXT1386_PAGES_PER_COLUMN 8 + +struct t37_debug { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + u8 mode; + u8 page; + u8 data[MXT_DIAGNOSTIC_SIZE]; +#endif +}; + /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_RESET_VALUE 0x01 @@ -133,10 +160,14 @@ struct t9_range { #define MXT_T100_CTRL 0 #define MXT_T100_CFG1 1 #define MXT_T100_TCHAUX 3 +#define MXT_T100_XSIZE 9 #define MXT_T100_XRANGE 13 +#define MXT_T100_YSIZE 20 #define MXT_T100_YRANGE 24 #define MXT_T100_CFG_SWITCHXY BIT(5) +#define MXT_T100_CFG_INVERTY BIT(6) +#define MXT_T100_CFG_INVERTX BIT(7) #define MXT_T100_TCHAUX_VECT BIT(0) #define MXT_T100_TCHAUX_AMPL BIT(1) @@ -205,6 +236,37 @@ struct mxt_object { u8 num_report_ids; } __packed; +struct mxt_dbg { + u16 t37_address; + u16 diag_cmd_address; + struct t37_debug *t37_buf; + unsigned int t37_pages; + unsigned int t37_nodes; + + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; +}; + +enum v4l_dbg_inputs { + MXT_V4L_INPUT_DELTAS, + MXT_V4L_INPUT_REFS, + MXT_V4L_INPUT_MAX, +}; + +static const struct v4l2_file_operations mxt_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -216,7 +278,11 @@ struct mxt_data { unsigned int irq; unsigned int max_x; unsigned int max_y; + bool invertx; + bool inverty; bool xy_switch; + u8 xsize; + u8 ysize; bool in_bootloader; u16 mem_size; u8 t100_aux_ampl; @@ -233,6 +299,7 @@ struct mxt_data { u8 num_touchids; u8 multitouch; struct t7_config t7_cfg; + struct mxt_dbg dbg; /* Cached parameters from object table */ u16 T5_address; @@ -257,6 +324,11 @@ struct mxt_data { struct completion crc_completion; }; +struct mxt_vb2_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + static size_t mxt_obj_size(const struct mxt_object *obj) { return obj->size_minus_one + 1; @@ -1503,6 +1575,11 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + video_unregister_device(&data->dbg.vdev); + v4l2_device_unregister(&data->dbg.v4l2); +#endif + kfree(data->object_table); data->object_table = NULL; kfree(data->msg_buf); @@ -1661,6 +1738,18 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return -EINVAL; error = __mxt_read_reg(client, + object->start_address + MXT_T9_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + + error = __mxt_read_reg(client, object->start_address + MXT_T9_RANGE, sizeof(range), &range); if (error) @@ -1676,6 +1765,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return error; data->xy_switch = orient & MXT_T9_ORIENT_SWITCH; + data->invertx = orient & MXT_T9_ORIENT_INVERTX; + data->inverty = orient & MXT_T9_ORIENT_INVERTY; return 0; } @@ -1710,6 +1801,18 @@ static int mxt_read_t100_config(struct mxt_data *data) data->max_y = get_unaligned_le16(&range_y); + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + /* read orientation config */ error = __mxt_read_reg(client, object->start_address + MXT_T100_CFG1, @@ -1718,6 +1821,8 @@ static int mxt_read_t100_config(struct mxt_data *data) return error; data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY; + data->invertx = cfg & MXT_T100_CFG_INVERTX; + data->inverty = cfg & MXT_T100_CFG_INVERTY; /* allocate aux bytes */ error = __mxt_read_reg(client, @@ -2043,6 +2148,420 @@ recheck: return 0; } +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 +static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, + unsigned int y) +{ + struct mxt_info *info = &data->info; + struct mxt_dbg *dbg = &data->dbg; + unsigned int ofs, page; + unsigned int col = 0; + unsigned int col_width; + + if (info->family_id == MXT_FAMILY_1386) { + col_width = info->matrix_ysize / MXT1386_COLUMNS; + col = y / col_width; + y = y % col_width; + } else { + col_width = info->matrix_ysize; + } + + ofs = (y + (x * col_width)) * sizeof(u16); + page = ofs / MXT_DIAGNOSTIC_SIZE; + ofs %= MXT_DIAGNOSTIC_SIZE; + + if (info->family_id == MXT_FAMILY_1386) + page += col * MXT1386_PAGES_PER_COLUMN; + + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); +} + +static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int x = 0; + unsigned int y = 0; + unsigned int i, rx, ry; + + for (i = 0; i < dbg->t37_nodes; i++) { + /* Handle orientation */ + rx = data->xy_switch ? y : x; + ry = data->xy_switch ? x : y; + rx = data->invertx ? (data->xsize - 1 - rx) : rx; + ry = data->inverty ? (data->ysize - 1 - ry) : ry; + + outbuf[i] = mxt_get_debug_value(data, rx, ry); + + /* Next value */ + if (++x >= (data->xy_switch ? data->ysize : data->xsize)) { + x = 0; + y++; + } + } + + return 0; +} + +static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode, + u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + int retries = 0; + int page; + int ret; + u8 cmd = mode; + struct t37_debug *p; + u8 cmd_poll; + + for (page = 0; page < dbg->t37_pages; page++) { + p = dbg->t37_buf + page; + + ret = mxt_write_reg(data->client, dbg->diag_cmd_address, + cmd); + if (ret) + return ret; + + retries = 0; + msleep(20); +wait_cmd: + /* Read back command byte */ + ret = __mxt_read_reg(data->client, dbg->diag_cmd_address, + sizeof(cmd_poll), &cmd_poll); + if (ret) + return ret; + + /* Field is cleared once the command has been processed */ + if (cmd_poll) { + if (retries++ > 100) + return -EINVAL; + + msleep(20); + goto wait_cmd; + } + + /* Read T37 page */ + ret = __mxt_read_reg(data->client, dbg->t37_address, + sizeof(struct t37_debug), p); + if (ret) + return ret; + + if (p->mode != mode || p->page != page) { + dev_err(&data->client->dev, "T37 page mismatch\n"); + return -EINVAL; + } + + dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", + __func__, page, retries); + + /* For remaining pages, write PAGEUP rather than mode */ + cmd = MXT_DIAGNOSTIC_PAGEUP; + } + + return mxt_convert_debug_pages(data, outbuf); +} + +static int mxt_queue_setup(struct vb2_queue *q, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mxt_data *data = q->drv_priv; + size_t size = data->dbg.t37_nodes * sizeof(u16); + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static void mxt_buffer_queue(struct vb2_buffer *vb) +{ + struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + int ret; + u8 mode; + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&data->client->dev, "Error acquiring frame ptr\n"); + goto fault; + } + + switch (data->dbg.input) { + case MXT_V4L_INPUT_DELTAS: + default: + mode = MXT_DIAGNOSTIC_DELTAS; + break; + + case MXT_V4L_INPUT_REFS: + mode = MXT_DIAGNOSTIC_REFS; + break; + } + + ret = mxt_read_diagnostic_debug(data, mode, ptr); + if (ret) + goto fault; + + vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16)); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + return; + +fault: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* V4L2 structures */ +static const struct vb2_ops mxt_queue_ops = { + .queue_setup = mxt_queue_setup, + .buf_queue = mxt_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue mxt_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct mxt_vb2_buffer), + .ops = &mxt_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int mxt_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct mxt_data *data = video_drvdata(file); + + strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver)); + strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "I2C:%s", dev_name(&data->client->dev)); + return 0; +} + +static int mxt_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + + switch (i->index) { + case MXT_V4L_INPUT_REFS: + strlcpy(i->name, "Mutual Capacitance References", + sizeof(i->name)); + break; + case MXT_V4L_INPUT_DELTAS: + strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + break; + } + + return 0; +} + +static int mxt_set_input(struct mxt_data *data, unsigned int i) +{ + struct v4l2_pix_format *f = &data->dbg.format; + + if (i >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + if (i == MXT_V4L_INPUT_DELTAS) + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + else + f->pixelformat = V4L2_TCH_FMT_TU16; + + f->width = data->xy_switch ? data->ysize : data->xsize; + f->height = data->xy_switch ? data->xsize : data->ysize; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + data->dbg.input = i; + + return 0; +} + +static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return mxt_set_input(video_drvdata(file), i); +} + +static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct mxt_data *data = video_drvdata(file); + + *i = data->dbg.input; + + return 0; +} + +static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct mxt_data *data = video_drvdata(file); + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix = data->dbg.format; + + return 0; +} + +static int mxt_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mxt_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = { + .vidioc_querycap = mxt_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_try_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_parm = mxt_vidioc_g_parm, + + .vidioc_enum_input = mxt_vidioc_enum_input, + .vidioc_g_input = mxt_vidioc_g_input, + .vidioc_s_input = mxt_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device mxt_video_device = { + .name = "Atmel maxTouch", + .fops = &mxt_video_fops, + .ioctl_ops = &mxt_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + +static void mxt_debug_init(struct mxt_data *data) +{ + struct mxt_info *info = &data->info; + struct mxt_dbg *dbg = &data->dbg; + struct mxt_object *object; + int error; + + object = mxt_get_object(data, MXT_GEN_COMMAND_T6); + if (!object) + goto error; + + dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; + + object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); + if (!object) + goto error; + + if (mxt_obj_size(object) != sizeof(struct t37_debug)) { + dev_warn(&data->client->dev, "Bad T37 size"); + goto error; + } + + dbg->t37_address = object->start_address; + + /* Calculate size of data and allocate buffer */ + dbg->t37_nodes = data->xsize * data->ysize; + + if (info->family_id == MXT_FAMILY_1386) + dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; + else + dbg->t37_pages = DIV_ROUND_UP(data->xsize * + data->info.matrix_ysize * + sizeof(u16), + sizeof(dbg->t37_buf->data)); + + dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, + sizeof(struct t37_debug), GFP_KERNEL); + if (!dbg->t37_buf) + goto error; + + /* init channel to zero */ + mxt_set_input(data, 0); + + /* register video device */ + snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts"); + error = v4l2_device_register(&data->client->dev, &dbg->v4l2); + if (error) + goto error; + + /* initialize the queue */ + mutex_init(&dbg->lock); + dbg->queue = mxt_queue; + dbg->queue.drv_priv = data; + dbg->queue.lock = &dbg->lock; + dbg->queue.dev = &data->client->dev; + + error = vb2_queue_init(&dbg->queue); + if (error) + goto error_unreg_v4l2; + + dbg->vdev = mxt_video_device; + dbg->vdev.v4l2_dev = &dbg->v4l2; + dbg->vdev.lock = &dbg->lock; + dbg->vdev.vfl_dir = VFL_DIR_RX; + dbg->vdev.queue = &dbg->queue; + video_set_drvdata(&dbg->vdev, data); + + error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1); + if (error) + goto error_unreg_v4l2; + + return; + +error_unreg_v4l2: + v4l2_device_unregister(&dbg->v4l2); +error: + dev_warn(&data->client->dev, "Error initializing T37\n"); +} +#else +static void mxt_debug_init(struct mxt_data *data) +{ +} +#endif + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -2070,6 +2589,8 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "No touch object detected\n"); } + mxt_debug_init(data); + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 4ea475775d58..aefb6e11f88a 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -139,6 +139,27 @@ struct sur40_image_header { #define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ #define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ +static const struct v4l2_pix_format sur40_pix_format[] = { + { + .pixelformat = V4L2_TCH_FMT_TU08, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), + }, + { + .pixelformat = V4L2_PIX_FMT_GREY, + .width = SENSOR_RES_X / 2, + .height = SENSOR_RES_Y / 2, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bytesperline = SENSOR_RES_X / 2, + .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), + } +}; + /* master device state */ struct sur40_state { @@ -149,6 +170,7 @@ struct sur40_state { struct v4l2_device v4l2; struct video_device vdev; struct mutex lock; + struct v4l2_pix_format pix_fmt; struct vb2_queue queue; struct list_head buf_list; @@ -169,7 +191,6 @@ struct sur40_buffer { /* forward declarations */ static const struct video_device sur40_video_device; -static const struct v4l2_pix_format sur40_video_format; static const struct vb2_queue sur40_queue; static void sur40_process_video(struct sur40_state *sur40); @@ -420,7 +441,7 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } - if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) { + if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) { dev_err(sur40->dev, "image size mismatch\n"); goto err_poll; } @@ -431,7 +452,7 @@ static void sur40_process_video(struct sur40_state *sur40) result = usb_sg_init(&sgr, sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, - sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0); + sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0); if (result < 0) { dev_err(sur40->dev, "error %d in usb_sg_init\n", result); goto err_poll; @@ -586,13 +607,14 @@ static int sur40_probe(struct usb_interface *interface, if (error) goto err_unreg_v4l2; + sur40->pix_fmt = sur40_pix_format[0]; sur40->vdev = sur40_video_device; sur40->vdev.v4l2_dev = &sur40->v4l2; sur40->vdev.lock = &sur40->lock; sur40->vdev.queue = &sur40->queue; video_set_drvdata(&sur40->vdev, sur40); - error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1); + error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1); if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); @@ -647,14 +669,16 @@ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { + struct sur40_state *sur40 = vb2_get_drv_priv(q); + if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; if (*nplanes) - return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; + return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0; *nplanes = 1; - sizes[0] = sur40_video_format.sizeimage; + sizes[0] = sur40->pix_fmt.sizeimage; return 0; } @@ -666,7 +690,7 @@ static int sur40_queue_setup(struct vb2_queue *q, static int sur40_buffer_prepare(struct vb2_buffer *vb) { struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = sur40_video_format.sizeimage; + unsigned long size = sur40->pix_fmt.sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", @@ -741,7 +765,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card)); usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -753,7 +777,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv, { if (i->index != 0) return -EINVAL; - i->type = V4L2_INPUT_TYPE_CAMERA; + i->type = V4L2_INPUT_TYPE_TOUCH; i->std = V4L2_STD_UNKNOWN; strlcpy(i->name, "In-Cell Sensor", sizeof(i->name)); i->capabilities = 0; @@ -771,19 +795,70 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int sur40_vidioc_fmt(struct file *file, void *priv, +static int sur40_vidioc_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + f->fmt.pix = sur40_pix_format[1]; + break; + + default: + f->fmt.pix = sur40_pix_format[0]; + break; + } + + return 0; +} + +static int sur40_vidioc_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct sur40_state *sur40 = video_drvdata(file); + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_GREY: + sur40->pix_fmt = sur40_pix_format[1]; + break; + + default: + sur40->pix_fmt = sur40_pix_format[0]; + break; + } + + f->fmt.pix = sur40->pix_fmt; + return 0; +} + +static int sur40_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - f->fmt.pix = sur40_video_format; + struct sur40_state *sur40 = video_drvdata(file); + + f->fmt.pix = sur40->pix_fmt; + return 0; +} + +static int sur40_ioctl_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + p->parm.capture.timeperframe.numerator = 1; + p->parm.capture.timeperframe.denominator = 60; + p->parm.capture.readbuffers = 3; return 0; } static int sur40_vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) + if (f->index >= ARRAY_SIZE(sur40_pix_format)) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_GREY; + + f->pixelformat = sur40_pix_format[f->index].pixelformat; f->flags = 0; return 0; } @@ -791,25 +866,31 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, static int sur40_vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *f) { - if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY)) + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (f->pixel_format != V4L2_PIX_FMT_GREY))) return -EINVAL; f->type = V4L2_FRMSIZE_TYPE_DISCRETE; - f->discrete.width = sur40_video_format.width; - f->discrete.height = sur40_video_format.height; + f->discrete.width = sur40->pix_fmt.width; + f->discrete.height = sur40->pix_fmt.height; return 0; } static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *f) { - if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY) - || (f->width != sur40_video_format.width) - || (f->height != sur40_video_format.height)) - return -EINVAL; + struct sur40_state *sur40 = video_drvdata(file); + + if ((f->index > 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) + && (f->pixel_format != V4L2_PIX_FMT_GREY)) + || (f->width != sur40->pix_fmt.width) + || (f->height != sur40->pix_fmt.height)) + return -EINVAL; f->type = V4L2_FRMIVAL_TYPE_DISCRETE; - f->discrete.denominator = 60/(f->index+1); + f->discrete.denominator = 60; f->discrete.numerator = 1; return 0; } @@ -862,13 +943,16 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { .vidioc_querycap = sur40_vidioc_querycap, .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, - .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt, - .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, - .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_try_fmt_vid_cap = sur40_vidioc_try_fmt, + .vidioc_s_fmt_vid_cap = sur40_vidioc_s_fmt, + .vidioc_g_fmt_vid_cap = sur40_vidioc_g_fmt, .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + .vidioc_g_parm = sur40_ioctl_parm, + .vidioc_s_parm = sur40_ioctl_parm, + .vidioc_enum_input = sur40_vidioc_enum_input, .vidioc_g_input = sur40_vidioc_g_input, .vidioc_s_input = sur40_vidioc_s_input, @@ -891,16 +975,6 @@ static const struct video_device sur40_video_device = { .release = video_device_release_empty, }; -static const struct v4l2_pix_format sur40_video_format = { - .pixelformat = V4L2_PIX_FMT_GREY, - .width = SENSOR_RES_X / 2, - .height = SENSOR_RES_Y / 2, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .bytesperline = SENSOR_RES_X / 2, - .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), -}; - /* USB-specific object needed to register this driver with the USB subsystem. */ static struct usb_driver sur40_driver = { .name = DRIVER_SHORT, diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index d432ca828472..8ee54d71c7eb 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -309,7 +309,7 @@ config ARM_SMMU config ARM_SMMU_V3 bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support" - depends on ARM64 && PCI + depends on ARM64 select IOMMU_API select IOMMU_IO_PGTABLE_LPAE select GENERIC_MSI_IRQ_DOMAIN diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 58fa8cc0262b..754595ee11b6 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -103,7 +103,7 @@ struct flush_queue { struct flush_queue_entry *entries; }; -DEFINE_PER_CPU(struct flush_queue, flush_queue); +static DEFINE_PER_CPU(struct flush_queue, flush_queue); static atomic_t queue_timer_on; static struct timer_list queue_timer; @@ -1361,7 +1361,8 @@ static u64 *alloc_pte(struct protection_domain *domain, __npte = PM_LEVEL_PDE(level, virt_to_phys(page)); - if (cmpxchg64(pte, __pte, __npte)) { + /* pte could have been changed somewhere. */ + if (cmpxchg64(pte, __pte, __npte) != __pte) { free_page((unsigned long)page); continue; } @@ -1741,6 +1742,9 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom) free_pagetable(&dom->domain); + if (dom->domain.id) + domain_id_free(dom->domain.id); + kfree(dom); } @@ -3649,7 +3653,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) table = irq_lookup_table[devid]; if (table) - goto out; + goto out_unlock; alias = amd_iommu_alias_table[devid]; table = irq_lookup_table[alias]; @@ -3663,7 +3667,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) /* Nothing there yet, allocate new irq remapping table */ table = kzalloc(sizeof(*table), GFP_ATOMIC); if (!table) - goto out; + goto out_unlock; /* Initialize table spin-lock */ spin_lock_init(&table->lock); @@ -3676,7 +3680,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) if (!table->table) { kfree(table); table = NULL; - goto out; + goto out_unlock; } if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir)) @@ -4153,6 +4157,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq, } if (index < 0) { pr_warn("Failed to allocate IRTE\n"); + ret = index; goto out_free_parent; } diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index cd1713631a4a..157e93421fb8 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -20,6 +20,7 @@ #include <linux/pci.h> #include <linux/acpi.h> #include <linux/list.h> +#include <linux/bitmap.h> #include <linux/slab.h> #include <linux/syscore_ops.h> #include <linux/interrupt.h> @@ -2285,7 +2286,7 @@ static int __init early_amd_iommu_init(void) * never allocate domain 0 because its used as the non-allocated and * error value placeholder */ - amd_iommu_pd_alloc_bitmap[0] = 1; + __set_bit(0, amd_iommu_pd_alloc_bitmap); spin_lock_init(&amd_iommu_pd_lock); diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h index faa3b4895cf0..7eb60c15c582 100644 --- a/drivers/iommu/amd_iommu_proto.h +++ b/drivers/iommu/amd_iommu_proto.h @@ -79,12 +79,6 @@ static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu) extern int amd_iommu_complete_ppr(struct pci_dev *pdev, int pasid, int status, int tag); -#ifndef CONFIG_AMD_IOMMU_STATS - -static inline void amd_iommu_stats_init(void) { } - -#endif /* !CONFIG_AMD_IOMMU_STATS */ - static inline bool is_rd890_iommu(struct pci_dev *pdev) { return (pdev->vendor == PCI_VENDOR_ID_ATI) && diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 641e88761319..15c01c3cd540 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -30,10 +30,13 @@ #include <linux/msi.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_iommu.h> #include <linux/of_platform.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/amba/bus.h> + #include "io-pgtable.h" /* MMIO registers */ @@ -123,6 +126,10 @@ #define CR2_RECINVSID (1 << 1) #define CR2_E2H (1 << 0) +#define ARM_SMMU_GBPA 0x44 +#define GBPA_ABORT (1 << 20) +#define GBPA_UPDATE (1 << 31) + #define ARM_SMMU_IRQ_CTRL 0x50 #define IRQ_CTRL_EVTQ_IRQEN (1 << 2) #define IRQ_CTRL_PRIQ_IRQEN (1 << 1) @@ -260,6 +267,9 @@ #define STRTAB_STE_1_SHCFG_INCOMING 1UL #define STRTAB_STE_1_SHCFG_SHIFT 44 +#define STRTAB_STE_1_PRIVCFG_UNPRIV 2UL +#define STRTAB_STE_1_PRIVCFG_SHIFT 48 + #define STRTAB_STE_2_S2VMID_SHIFT 0 #define STRTAB_STE_2_S2VMID_MASK 0xffffUL #define STRTAB_STE_2_VTCR_SHIFT 32 @@ -606,12 +616,9 @@ struct arm_smmu_device { struct arm_smmu_strtab_cfg strtab_cfg; }; -/* SMMU private data for an IOMMU group */ -struct arm_smmu_group { +/* SMMU private data for each master */ +struct arm_smmu_master_data { struct arm_smmu_device *smmu; - struct arm_smmu_domain *domain; - int num_sids; - u32 *sids; struct arm_smmu_strtab_ent ste; }; @@ -713,19 +720,15 @@ static void queue_inc_prod(struct arm_smmu_queue *q) writel(q->prod, q->prod_reg); } -static bool __queue_cons_before(struct arm_smmu_queue *q, u32 until) -{ - if (Q_WRP(q, q->cons) == Q_WRP(q, until)) - return Q_IDX(q, q->cons) < Q_IDX(q, until); - - return Q_IDX(q, q->cons) >= Q_IDX(q, until); -} - -static int queue_poll_cons(struct arm_smmu_queue *q, u32 until, bool wfe) +/* + * Wait for the SMMU to consume items. If drain is true, wait until the queue + * is empty. Otherwise, wait until there is at least one free slot. + */ +static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe) { ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US); - while (queue_sync_cons(q), __queue_cons_before(q, until)) { + while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) { if (ktime_compare(ktime_get(), timeout) > 0) return -ETIMEDOUT; @@ -896,8 +899,8 @@ static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu) static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, struct arm_smmu_cmdq_ent *ent) { - u32 until; u64 cmd[CMDQ_ENT_DWORDS]; + unsigned long flags; bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); struct arm_smmu_queue *q = &smmu->cmdq.q; @@ -907,20 +910,15 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, return; } - spin_lock(&smmu->cmdq.lock); - while (until = q->prod + 1, queue_insert_raw(q, cmd) == -ENOSPC) { - /* - * Keep the queue locked, otherwise the producer could wrap - * twice and we could see a future consumer pointer that looks - * like it's behind us. - */ - if (queue_poll_cons(q, until, wfe)) + spin_lock_irqsave(&smmu->cmdq.lock, flags); + while (queue_insert_raw(q, cmd) == -ENOSPC) { + if (queue_poll_cons(q, false, wfe)) dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); } - if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, until, wfe)) + if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, true, wfe)) dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n"); - spin_unlock(&smmu->cmdq.lock); + spin_unlock_irqrestore(&smmu->cmdq.lock, flags); } /* Context descriptor manipulation functions */ @@ -1073,7 +1071,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid, #ifdef CONFIG_PCI_ATS STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT | #endif - STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT); + STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT | + STRTAB_STE_1_PRIVCFG_UNPRIV << + STRTAB_STE_1_PRIVCFG_SHIFT); if (smmu->features & ARM_SMMU_FEAT_STALLS) dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD); @@ -1161,36 +1161,66 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) struct arm_smmu_queue *q = &smmu->evtq.q; u64 evt[EVTQ_ENT_DWORDS]; - while (!queue_remove_raw(q, evt)) { - u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK; + do { + while (!queue_remove_raw(q, evt)) { + u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK; - dev_info(smmu->dev, "event 0x%02x received:\n", id); - for (i = 0; i < ARRAY_SIZE(evt); ++i) - dev_info(smmu->dev, "\t0x%016llx\n", - (unsigned long long)evt[i]); - } + dev_info(smmu->dev, "event 0x%02x received:\n", id); + for (i = 0; i < ARRAY_SIZE(evt); ++i) + dev_info(smmu->dev, "\t0x%016llx\n", + (unsigned long long)evt[i]); + + } + + /* + * Not much we can do on overflow, so scream and pretend we're + * trying harder. + */ + if (queue_sync_prod(q) == -EOVERFLOW) + dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); + } while (!queue_empty(q)); /* Sync our overflow flag, as we believe we're up to speed */ q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons); return IRQ_HANDLED; } -static irqreturn_t arm_smmu_evtq_handler(int irq, void *dev) -{ - irqreturn_t ret = IRQ_WAKE_THREAD; - struct arm_smmu_device *smmu = dev; - struct arm_smmu_queue *q = &smmu->evtq.q; +static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) +{ + u32 sid, ssid; + u16 grpid; + bool ssv, last; + + sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK; + ssv = evt[0] & PRIQ_0_SSID_V; + ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0; + last = evt[0] & PRIQ_0_PRG_LAST; + grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK; + + dev_info(smmu->dev, "unexpected PRI request received:\n"); + dev_info(smmu->dev, + "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", + sid, ssid, grpid, last ? "L" : "", + evt[0] & PRIQ_0_PERM_PRIV ? "" : "un", + evt[0] & PRIQ_0_PERM_READ ? "R" : "", + evt[0] & PRIQ_0_PERM_WRITE ? "W" : "", + evt[0] & PRIQ_0_PERM_EXEC ? "X" : "", + evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT); + + if (last) { + struct arm_smmu_cmdq_ent cmd = { + .opcode = CMDQ_OP_PRI_RESP, + .substream_valid = ssv, + .pri = { + .sid = sid, + .ssid = ssid, + .grpid = grpid, + .resp = PRI_RESP_DENY, + }, + }; - /* - * Not much we can do on overflow, so scream and pretend we're - * trying harder. - */ - if (queue_sync_prod(q) == -EOVERFLOW) - dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); - else if (queue_empty(q)) - ret = IRQ_NONE; - - return ret; + arm_smmu_cmdq_issue_cmd(smmu, &cmd); + } } static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) @@ -1199,63 +1229,19 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) struct arm_smmu_queue *q = &smmu->priq.q; u64 evt[PRIQ_ENT_DWORDS]; - while (!queue_remove_raw(q, evt)) { - u32 sid, ssid; - u16 grpid; - bool ssv, last; + do { + while (!queue_remove_raw(q, evt)) + arm_smmu_handle_ppr(smmu, evt); - sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK; - ssv = evt[0] & PRIQ_0_SSID_V; - ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0; - last = evt[0] & PRIQ_0_PRG_LAST; - grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK; - - dev_info(smmu->dev, "unexpected PRI request received:\n"); - dev_info(smmu->dev, - "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", - sid, ssid, grpid, last ? "L" : "", - evt[0] & PRIQ_0_PERM_PRIV ? "" : "un", - evt[0] & PRIQ_0_PERM_READ ? "R" : "", - evt[0] & PRIQ_0_PERM_WRITE ? "W" : "", - evt[0] & PRIQ_0_PERM_EXEC ? "X" : "", - evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT); - - if (last) { - struct arm_smmu_cmdq_ent cmd = { - .opcode = CMDQ_OP_PRI_RESP, - .substream_valid = ssv, - .pri = { - .sid = sid, - .ssid = ssid, - .grpid = grpid, - .resp = PRI_RESP_DENY, - }, - }; - - arm_smmu_cmdq_issue_cmd(smmu, &cmd); - } - } + if (queue_sync_prod(q) == -EOVERFLOW) + dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n"); + } while (!queue_empty(q)); /* Sync our overflow flag, as we believe we're up to speed */ q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons); return IRQ_HANDLED; } -static irqreturn_t arm_smmu_priq_handler(int irq, void *dev) -{ - irqreturn_t ret = IRQ_WAKE_THREAD; - struct arm_smmu_device *smmu = dev; - struct arm_smmu_queue *q = &smmu->priq.q; - - /* PRIQ overflow indicates a programming error */ - if (queue_sync_prod(q) == -EOVERFLOW) - dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n"); - else if (queue_empty(q)) - ret = IRQ_NONE; - - return ret; -} - static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev) { /* We don't actually use CMD_SYNC interrupts for anything */ @@ -1288,15 +1274,11 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev) if (active & GERROR_MSI_GERROR_ABT_ERR) dev_warn(smmu->dev, "GERROR MSI write aborted\n"); - if (active & GERROR_MSI_PRIQ_ABT_ERR) { + if (active & GERROR_MSI_PRIQ_ABT_ERR) dev_warn(smmu->dev, "PRIQ MSI write aborted\n"); - arm_smmu_priq_handler(irq, smmu->dev); - } - if (active & GERROR_MSI_EVTQ_ABT_ERR) { + if (active & GERROR_MSI_EVTQ_ABT_ERR) dev_warn(smmu->dev, "EVTQ MSI write aborted\n"); - arm_smmu_evtq_handler(irq, smmu->dev); - } if (active & GERROR_MSI_CMDQ_ABT_ERR) { dev_warn(smmu->dev, "CMDQ MSI write aborted\n"); @@ -1569,6 +1551,8 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain) return -ENOMEM; domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; + domain->geometry.aperture_end = (1UL << ias) - 1; + domain->geometry.force_aperture = true; smmu_domain->pgtbl_ops = pgtbl_ops; ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg); @@ -1578,20 +1562,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain) return ret; } -static struct arm_smmu_group *arm_smmu_group_get(struct device *dev) -{ - struct iommu_group *group; - struct arm_smmu_group *smmu_group; - - group = iommu_group_get(dev); - if (!group) - return NULL; - - smmu_group = iommu_group_get_iommudata(group); - iommu_group_put(group); - return smmu_group; -} - static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) { __le64 *step; @@ -1614,27 +1584,17 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) return step; } -static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group) +static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec) { int i; - struct arm_smmu_domain *smmu_domain = smmu_group->domain; - struct arm_smmu_strtab_ent *ste = &smmu_group->ste; - struct arm_smmu_device *smmu = smmu_group->smmu; + struct arm_smmu_master_data *master = fwspec->iommu_priv; + struct arm_smmu_device *smmu = master->smmu; - if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { - ste->s1_cfg = &smmu_domain->s1_cfg; - ste->s2_cfg = NULL; - arm_smmu_write_ctx_desc(smmu, ste->s1_cfg); - } else { - ste->s1_cfg = NULL; - ste->s2_cfg = &smmu_domain->s2_cfg; - } - - for (i = 0; i < smmu_group->num_sids; ++i) { - u32 sid = smmu_group->sids[i]; + for (i = 0; i < fwspec->num_ids; ++i) { + u32 sid = fwspec->ids[i]; __le64 *step = arm_smmu_get_step_for_sid(smmu, sid); - arm_smmu_write_strtab_ent(smmu, sid, step, ste); + arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste); } return 0; @@ -1642,13 +1602,11 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group) static void arm_smmu_detach_dev(struct device *dev) { - struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); + struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv; - smmu_group->ste.bypass = true; - if (arm_smmu_install_ste_for_group(smmu_group) < 0) + master->ste.bypass = true; + if (arm_smmu_install_ste_for_dev(dev->iommu_fwspec) < 0) dev_warn(dev, "failed to install bypass STE\n"); - - smmu_group->domain = NULL; } static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) @@ -1656,16 +1614,20 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) int ret = 0; struct arm_smmu_device *smmu; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); + struct arm_smmu_master_data *master; + struct arm_smmu_strtab_ent *ste; - if (!smmu_group) + if (!dev->iommu_fwspec) return -ENOENT; + master = dev->iommu_fwspec->iommu_priv; + smmu = master->smmu; + ste = &master->ste; + /* Already attached to a different domain? */ - if (smmu_group->domain && smmu_group->domain != smmu_domain) + if (!ste->bypass) arm_smmu_detach_dev(dev); - smmu = smmu_group->smmu; mutex_lock(&smmu_domain->init_mutex); if (!smmu_domain->smmu) { @@ -1684,21 +1646,21 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) goto out_unlock; } - /* Group already attached to this domain? */ - if (smmu_group->domain) - goto out_unlock; - - smmu_group->domain = smmu_domain; + ste->bypass = false; + ste->valid = true; - /* - * FIXME: This should always be "false" once we have IOMMU-backed - * DMA ops for all devices behind the SMMU. - */ - smmu_group->ste.bypass = domain->type == IOMMU_DOMAIN_DMA; + if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { + ste->s1_cfg = &smmu_domain->s1_cfg; + ste->s2_cfg = NULL; + arm_smmu_write_ctx_desc(smmu, ste->s1_cfg); + } else { + ste->s1_cfg = NULL; + ste->s2_cfg = &smmu_domain->s2_cfg; + } - ret = arm_smmu_install_ste_for_group(smmu_group); + ret = arm_smmu_install_ste_for_dev(dev->iommu_fwspec); if (ret < 0) - smmu_group->domain = NULL; + ste->valid = false; out_unlock: mutex_unlock(&smmu_domain->init_mutex); @@ -1757,40 +1719,19 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) return ret; } -static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp) -{ - *(u32 *)sidp = alias; - return 0; /* Continue walking */ -} +static struct platform_driver arm_smmu_driver; -static void __arm_smmu_release_pci_iommudata(void *data) +static int arm_smmu_match_node(struct device *dev, void *data) { - kfree(data); + return dev->of_node == data; } -static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev) +static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np) { - struct device_node *of_node; - struct platform_device *smmu_pdev; - struct arm_smmu_device *smmu = NULL; - struct pci_bus *bus = pdev->bus; - - /* Walk up to the root bus */ - while (!pci_is_root_bus(bus)) - bus = bus->parent; - - /* Follow the "iommus" phandle from the host controller */ - of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0); - if (!of_node) - return NULL; - - /* See if we can find an SMMU corresponding to the phandle */ - smmu_pdev = of_find_device_by_node(of_node); - if (smmu_pdev) - smmu = platform_get_drvdata(smmu_pdev); - - of_node_put(of_node); - return smmu; + struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL, + np, arm_smmu_match_node); + put_device(dev); + return dev ? dev_get_drvdata(dev) : NULL; } static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) @@ -1803,94 +1744,91 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) return sid < limit; } +static struct iommu_ops arm_smmu_ops; + static int arm_smmu_add_device(struct device *dev) { int i, ret; - u32 sid, *sids; - struct pci_dev *pdev; - struct iommu_group *group; - struct arm_smmu_group *smmu_group; struct arm_smmu_device *smmu; + struct arm_smmu_master_data *master; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct iommu_group *group; - /* We only support PCI, for now */ - if (!dev_is_pci(dev)) + if (!fwspec || fwspec->ops != &arm_smmu_ops) return -ENODEV; - - pdev = to_pci_dev(dev); - group = iommu_group_get_for_dev(dev); - if (IS_ERR(group)) - return PTR_ERR(group); - - smmu_group = iommu_group_get_iommudata(group); - if (!smmu_group) { - smmu = arm_smmu_get_for_pci_dev(pdev); - if (!smmu) { - ret = -ENOENT; - goto out_remove_dev; - } - - smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL); - if (!smmu_group) { - ret = -ENOMEM; - goto out_remove_dev; - } - - smmu_group->ste.valid = true; - smmu_group->smmu = smmu; - iommu_group_set_iommudata(group, smmu_group, - __arm_smmu_release_pci_iommudata); + /* + * We _can_ actually withstand dodgy bus code re-calling add_device() + * without an intervening remove_device()/of_xlate() sequence, but + * we're not going to do so quietly... + */ + if (WARN_ON_ONCE(fwspec->iommu_priv)) { + master = fwspec->iommu_priv; + smmu = master->smmu; } else { - smmu = smmu_group->smmu; - } + smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode)); + if (!smmu) + return -ENODEV; + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; - /* Assume SID == RID until firmware tells us otherwise */ - pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); - for (i = 0; i < smmu_group->num_sids; ++i) { - /* If we already know about this SID, then we're done */ - if (smmu_group->sids[i] == sid) - goto out_put_group; + master->smmu = smmu; + fwspec->iommu_priv = master; } - /* Check the SID is in range of the SMMU and our stream table */ - if (!arm_smmu_sid_in_range(smmu, sid)) { - ret = -ERANGE; - goto out_remove_dev; - } + /* Check the SIDs are in range of the SMMU and our stream table */ + for (i = 0; i < fwspec->num_ids; i++) { + u32 sid = fwspec->ids[i]; - /* Ensure l2 strtab is initialised */ - if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { - ret = arm_smmu_init_l2_strtab(smmu, sid); - if (ret) - goto out_remove_dev; - } + if (!arm_smmu_sid_in_range(smmu, sid)) + return -ERANGE; - /* Resize the SID array for the group */ - smmu_group->num_sids++; - sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids), - GFP_KERNEL); - if (!sids) { - smmu_group->num_sids--; - ret = -ENOMEM; - goto out_remove_dev; + /* Ensure l2 strtab is initialised */ + if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { + ret = arm_smmu_init_l2_strtab(smmu, sid); + if (ret) + return ret; + } } - /* Add the new SID */ - sids[smmu_group->num_sids - 1] = sid; - smmu_group->sids = sids; - -out_put_group: - iommu_group_put(group); - return 0; + group = iommu_group_get_for_dev(dev); + if (!IS_ERR(group)) + iommu_group_put(group); -out_remove_dev: - iommu_group_remove_device(dev); - iommu_group_put(group); - return ret; + return PTR_ERR_OR_ZERO(group); } static void arm_smmu_remove_device(struct device *dev) { + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct arm_smmu_master_data *master; + + if (!fwspec || fwspec->ops != &arm_smmu_ops) + return; + + master = fwspec->iommu_priv; + if (master && master->ste.valid) + arm_smmu_detach_dev(dev); iommu_group_remove_device(dev); + kfree(master); + iommu_fwspec_free(dev); +} + +static struct iommu_group *arm_smmu_device_group(struct device *dev) +{ + struct iommu_group *group; + + /* + * We don't support devices sharing stream IDs other than PCI RID + * aliases, since the necessary ID-to-device lookup becomes rather + * impractical given a potential sparse 32-bit stream ID space. + */ + if (dev_is_pci(dev)) + group = pci_device_group(dev); + else + group = generic_device_group(dev); + + return group; } static int arm_smmu_domain_get_attr(struct iommu_domain *domain, @@ -1937,6 +1875,11 @@ out_unlock: return ret; } +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) +{ + return iommu_fwspec_add_ids(dev, args->args, 1); +} + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, @@ -1948,9 +1891,10 @@ static struct iommu_ops arm_smmu_ops = { .iova_to_phys = arm_smmu_iova_to_phys, .add_device = arm_smmu_add_device, .remove_device = arm_smmu_remove_device, - .device_group = pci_device_group, + .device_group = arm_smmu_device_group, .domain_get_attr = arm_smmu_domain_get_attr, .domain_set_attr = arm_smmu_domain_set_attr, + .of_xlate = arm_smmu_of_xlate, .pgsize_bitmap = -1UL, /* Restricted during device attach */ }; @@ -2151,6 +2095,24 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val, 1, ARM_SMMU_POLL_TIMEOUT_US); } +/* GBPA is "special" */ +static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr) +{ + int ret; + u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA; + + ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), + 1, ARM_SMMU_POLL_TIMEOUT_US); + if (ret) + return ret; + + reg &= ~clr; + reg |= set; + writel_relaxed(reg | GBPA_UPDATE, gbpa); + return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), + 1, ARM_SMMU_POLL_TIMEOUT_US); +} + static void arm_smmu_free_msis(void *data) { struct device *dev = data; @@ -2235,10 +2197,10 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) /* Request interrupt lines */ irq = smmu->evtq.q.irq; if (irq) { - ret = devm_request_threaded_irq(smmu->dev, irq, - arm_smmu_evtq_handler, + ret = devm_request_threaded_irq(smmu->dev, irq, NULL, arm_smmu_evtq_thread, - 0, "arm-smmu-v3-evtq", smmu); + IRQF_ONESHOT, + "arm-smmu-v3-evtq", smmu); if (ret < 0) dev_warn(smmu->dev, "failed to enable evtq irq\n"); } @@ -2263,10 +2225,10 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) if (smmu->features & ARM_SMMU_FEAT_PRI) { irq = smmu->priq.q.irq; if (irq) { - ret = devm_request_threaded_irq(smmu->dev, irq, - arm_smmu_priq_handler, + ret = devm_request_threaded_irq(smmu->dev, irq, NULL, arm_smmu_priq_thread, - 0, "arm-smmu-v3-priq", + IRQF_ONESHOT, + "arm-smmu-v3-priq", smmu); if (ret < 0) dev_warn(smmu->dev, @@ -2296,7 +2258,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu) return ret; } -static int arm_smmu_device_reset(struct arm_smmu_device *smmu) +static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) { int ret; u32 reg, enables; @@ -2397,8 +2359,17 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu) return ret; } - /* Enable the SMMU interface */ - enables |= CR0_SMMUEN; + + /* Enable the SMMU interface, or ensure bypass */ + if (!bypass || disable_bypass) { + enables |= CR0_SMMUEN; + } else { + ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT); + if (ret) { + dev_err(smmu->dev, "GBPA not responding to update\n"); + return ret; + } + } ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, ARM_SMMU_CR0ACK); if (ret) { @@ -2597,6 +2568,15 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) struct resource *res; struct arm_smmu_device *smmu; struct device *dev = &pdev->dev; + bool bypass = true; + u32 cells; + + if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells)) + dev_err(dev, "missing #iommu-cells property\n"); + else if (cells != 1) + dev_err(dev, "invalid #iommu-cells value (%d)\n", cells); + else + bypass = false; smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); if (!smmu) { @@ -2649,7 +2629,24 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, smmu); /* Reset the device */ - return arm_smmu_device_reset(smmu); + ret = arm_smmu_device_reset(smmu, bypass); + if (ret) + return ret; + + /* And we're up. Go go go! */ + of_iommu_set_ops(dev->of_node, &arm_smmu_ops); +#ifdef CONFIG_PCI + pci_request_acs(); + ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops); + if (ret) + return ret; +#endif +#ifdef CONFIG_ARM_AMBA + ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops); + if (ret) + return ret; +#endif + return bus_set_iommu(&platform_bus_type, &arm_smmu_ops); } static int arm_smmu_device_remove(struct platform_device *pdev) @@ -2677,22 +2674,14 @@ static struct platform_driver arm_smmu_driver = { static int __init arm_smmu_init(void) { - struct device_node *np; - int ret; - - np = of_find_matching_node(NULL, arm_smmu_of_match); - if (!np) - return 0; - - of_node_put(np); - - ret = platform_driver_register(&arm_smmu_driver); - if (ret) - return ret; - - pci_request_acs(); + static bool registered; + int ret = 0; - return bus_set_iommu(&pci_bus_type, &arm_smmu_ops); + if (!registered) { + ret = platform_driver_register(&arm_smmu_driver); + registered = !ret; + } + return ret; } static void __exit arm_smmu_exit(void) @@ -2703,6 +2692,20 @@ static void __exit arm_smmu_exit(void) subsys_initcall(arm_smmu_init); module_exit(arm_smmu_exit); +static int __init arm_smmu_of_init(struct device_node *np) +{ + int ret = arm_smmu_init(); + + if (ret) + return ret; + + if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root)) + return -ENODEV; + + return 0; +} +IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init); + MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations"); MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 2db74ebc3240..c841eb7a1a74 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -28,6 +28,7 @@ #define pr_fmt(fmt) "arm-smmu: " fmt +#include <linux/atomic.h> #include <linux/delay.h> #include <linux/dma-iommu.h> #include <linux/dma-mapping.h> @@ -40,6 +41,8 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_iommu.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -49,15 +52,9 @@ #include "io-pgtable.h" -/* Maximum number of stream IDs assigned to a single device */ -#define MAX_MASTER_STREAMIDS 128 - /* Maximum number of context banks per SMMU */ #define ARM_SMMU_MAX_CBS 128 -/* Maximum number of mapping groups per SMMU */ -#define ARM_SMMU_MAX_SMRS 128 - /* SMMU global address space */ #define ARM_SMMU_GR0(smmu) ((smmu)->base) #define ARM_SMMU_GR1(smmu) ((smmu)->base + (1 << (smmu)->pgshift)) @@ -165,21 +162,27 @@ #define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) #define SMR_VALID (1 << 31) #define SMR_MASK_SHIFT 16 -#define SMR_MASK_MASK 0x7fff #define SMR_ID_SHIFT 0 -#define SMR_ID_MASK 0x7fff #define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) #define S2CR_CBNDX_SHIFT 0 #define S2CR_CBNDX_MASK 0xff #define S2CR_TYPE_SHIFT 16 #define S2CR_TYPE_MASK 0x3 -#define S2CR_TYPE_TRANS (0 << S2CR_TYPE_SHIFT) -#define S2CR_TYPE_BYPASS (1 << S2CR_TYPE_SHIFT) -#define S2CR_TYPE_FAULT (2 << S2CR_TYPE_SHIFT) +enum arm_smmu_s2cr_type { + S2CR_TYPE_TRANS, + S2CR_TYPE_BYPASS, + S2CR_TYPE_FAULT, +}; #define S2CR_PRIVCFG_SHIFT 24 -#define S2CR_PRIVCFG_UNPRIV (2 << S2CR_PRIVCFG_SHIFT) +#define S2CR_PRIVCFG_MASK 0x3 +enum arm_smmu_s2cr_privcfg { + S2CR_PRIVCFG_DEFAULT, + S2CR_PRIVCFG_DIPAN, + S2CR_PRIVCFG_UNPRIV, + S2CR_PRIVCFG_PRIV, +}; /* Context bank attribute registers */ #define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2)) @@ -217,6 +220,7 @@ #define ARM_SMMU_CB_TTBR0 0x20 #define ARM_SMMU_CB_TTBR1 0x28 #define ARM_SMMU_CB_TTBCR 0x30 +#define ARM_SMMU_CB_CONTEXTIDR 0x34 #define ARM_SMMU_CB_S1_MAIR0 0x38 #define ARM_SMMU_CB_S1_MAIR1 0x3c #define ARM_SMMU_CB_PAR 0x50 @@ -239,7 +243,6 @@ #define SCTLR_AFE (1 << 2) #define SCTLR_TRE (1 << 1) #define SCTLR_M (1 << 0) -#define SCTLR_EAE_SBOP (SCTLR_AFE | SCTLR_TRE) #define ARM_MMU500_ACTLR_CPRE (1 << 1) @@ -296,23 +299,33 @@ enum arm_smmu_implementation { CAVIUM_SMMUV2, }; +struct arm_smmu_s2cr { + struct iommu_group *group; + int count; + enum arm_smmu_s2cr_type type; + enum arm_smmu_s2cr_privcfg privcfg; + u8 cbndx; +}; + +#define s2cr_init_val (struct arm_smmu_s2cr){ \ + .type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS, \ +} + struct arm_smmu_smr { - u8 idx; u16 mask; u16 id; + bool valid; }; struct arm_smmu_master_cfg { - int num_streamids; - u16 streamids[MAX_MASTER_STREAMIDS]; - struct arm_smmu_smr *smrs; -}; - -struct arm_smmu_master { - struct device_node *of_node; - struct rb_node node; - struct arm_smmu_master_cfg cfg; + struct arm_smmu_device *smmu; + s16 smendx[]; }; +#define INVALID_SMENDX -1 +#define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv) +#define fwspec_smmu(fw) (__fwspec_cfg(fw)->smmu) +#define for_each_cfg_sme(fw, i, idx) \ + for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i) struct arm_smmu_device { struct device *dev; @@ -346,7 +359,11 @@ struct arm_smmu_device { atomic_t irptndx; u32 num_mapping_groups; - DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS); + u16 streamid_mask; + u16 smr_mask_mask; + struct arm_smmu_smr *smrs; + struct arm_smmu_s2cr *s2crs; + struct mutex stream_map_mutex; unsigned long va_size; unsigned long ipa_size; @@ -357,9 +374,6 @@ struct arm_smmu_device { u32 num_context_irqs; unsigned int *irqs; - struct list_head list; - struct rb_root masters; - u32 cavium_id_base; /* Specific to Cavium */ }; @@ -397,15 +411,6 @@ struct arm_smmu_domain { struct iommu_domain domain; }; -struct arm_smmu_phandle_args { - struct device_node *np; - int args_count; - uint32_t args[MAX_MASTER_STREAMIDS]; -}; - -static DEFINE_SPINLOCK(arm_smmu_devices_lock); -static LIST_HEAD(arm_smmu_devices); - struct arm_smmu_option_prop { u32 opt; const char *prop; @@ -413,6 +418,8 @@ struct arm_smmu_option_prop { static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); +static bool using_legacy_binding, using_generic_binding; + static struct arm_smmu_option_prop arm_smmu_options[] = { { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" }, { 0, NULL}, @@ -444,131 +451,86 @@ static struct device_node *dev_get_dev_node(struct device *dev) while (!pci_is_root_bus(bus)) bus = bus->parent; - return bus->bridge->parent->of_node; + return of_node_get(bus->bridge->parent->of_node); } - return dev->of_node; + return of_node_get(dev->of_node); } -static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, - struct device_node *dev_node) +static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) { - struct rb_node *node = smmu->masters.rb_node; - - while (node) { - struct arm_smmu_master *master; - - master = container_of(node, struct arm_smmu_master, node); - - if (dev_node < master->of_node) - node = node->rb_left; - else if (dev_node > master->of_node) - node = node->rb_right; - else - return master; - } - - return NULL; + *((__be32 *)data) = cpu_to_be32(alias); + return 0; /* Continue walking */ } -static struct arm_smmu_master_cfg * -find_smmu_master_cfg(struct device *dev) +static int __find_legacy_master_phandle(struct device *dev, void *data) { - struct arm_smmu_master_cfg *cfg = NULL; - struct iommu_group *group = iommu_group_get(dev); - - if (group) { - cfg = iommu_group_get_iommudata(group); - iommu_group_put(group); - } - - return cfg; + struct of_phandle_iterator *it = *(void **)data; + struct device_node *np = it->node; + int err; + + of_for_each_phandle(it, err, dev->of_node, "mmu-masters", + "#stream-id-cells", 0) + if (it->node == np) { + *(void **)data = dev; + return 1; + } + it->node = np; + return err == -ENOENT ? 0 : err; } -static int insert_smmu_master(struct arm_smmu_device *smmu, - struct arm_smmu_master *master) +static struct platform_driver arm_smmu_driver; +static struct iommu_ops arm_smmu_ops; + +static int arm_smmu_register_legacy_master(struct device *dev, + struct arm_smmu_device **smmu) { - struct rb_node **new, *parent; - - new = &smmu->masters.rb_node; - parent = NULL; - while (*new) { - struct arm_smmu_master *this - = container_of(*new, struct arm_smmu_master, node); - - parent = *new; - if (master->of_node < this->of_node) - new = &((*new)->rb_left); - else if (master->of_node > this->of_node) - new = &((*new)->rb_right); - else - return -EEXIST; + struct device *smmu_dev; + struct device_node *np; + struct of_phandle_iterator it; + void *data = ⁢ + u32 *sids; + __be32 pci_sid; + int err; + + np = dev_get_dev_node(dev); + if (!np || !of_find_property(np, "#stream-id-cells", NULL)) { + of_node_put(np); + return -ENODEV; } - rb_link_node(&master->node, parent, new); - rb_insert_color(&master->node, &smmu->masters); - return 0; -} - -static int register_smmu_master(struct arm_smmu_device *smmu, - struct device *dev, - struct arm_smmu_phandle_args *masterspec) -{ - int i; - struct arm_smmu_master *master; + it.node = np; + err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data, + __find_legacy_master_phandle); + smmu_dev = data; + of_node_put(np); + if (err == 0) + return -ENODEV; + if (err < 0) + return err; - master = find_smmu_master(smmu, masterspec->np); - if (master) { - dev_err(dev, - "rejecting multiple registrations for master device %s\n", - masterspec->np->name); - return -EBUSY; + if (dev_is_pci(dev)) { + /* "mmu-masters" assumes Stream ID == Requester ID */ + pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, + &pci_sid); + it.cur = &pci_sid; + it.cur_count = 1; } - if (masterspec->args_count > MAX_MASTER_STREAMIDS) { - dev_err(dev, - "reached maximum number (%d) of stream IDs for master device %s\n", - MAX_MASTER_STREAMIDS, masterspec->np->name); - return -ENOSPC; - } + err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode, + &arm_smmu_ops); + if (err) + return err; - master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL); - if (!master) + sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL); + if (!sids) return -ENOMEM; - master->of_node = masterspec->np; - master->cfg.num_streamids = masterspec->args_count; - - for (i = 0; i < master->cfg.num_streamids; ++i) { - u16 streamid = masterspec->args[i]; - - if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && - (streamid >= smmu->num_mapping_groups)) { - dev_err(dev, - "stream ID for master device %s greater than maximum allowed (%d)\n", - masterspec->np->name, smmu->num_mapping_groups); - return -ERANGE; - } - master->cfg.streamids[i] = streamid; - } - return insert_smmu_master(smmu, master); -} - -static struct arm_smmu_device *find_smmu_for_device(struct device *dev) -{ - struct arm_smmu_device *smmu; - struct arm_smmu_master *master = NULL; - struct device_node *dev_node = dev_get_dev_node(dev); - - spin_lock(&arm_smmu_devices_lock); - list_for_each_entry(smmu, &arm_smmu_devices, list) { - master = find_smmu_master(smmu, dev_node); - if (master) - break; - } - spin_unlock(&arm_smmu_devices_lock); - - return master ? smmu : NULL; + *smmu = dev_get_drvdata(smmu_dev); + of_phandle_iterator_args(&it, sids, it.cur_count); + err = iommu_fwspec_add_ids(dev, sids, it.cur_count); + kfree(sids); + return err; } static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end) @@ -738,7 +700,7 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *pgtbl_cfg) { - u32 reg; + u32 reg, reg2; u64 reg64; bool stage1; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; @@ -781,14 +743,22 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, /* TTBRs */ if (stage1) { - reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; - - reg64 |= ((u64)ARM_SMMU_CB_ASID(smmu, cfg)) << TTBRn_ASID_SHIFT; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); - - reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; - reg64 |= ((u64)ARM_SMMU_CB_ASID(smmu, cfg)) << TTBRn_ASID_SHIFT; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1); + u16 asid = ARM_SMMU_CB_ASID(smmu, cfg); + + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + reg = pgtbl_cfg->arm_v7s_cfg.ttbr[0]; + writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0); + reg = pgtbl_cfg->arm_v7s_cfg.ttbr[1]; + writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1); + writel_relaxed(asid, cb_base + ARM_SMMU_CB_CONTEXTIDR); + } else { + reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; + reg64 |= (u64)asid << TTBRn_ASID_SHIFT; + writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); + reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; + reg64 |= (u64)asid << TTBRn_ASID_SHIFT; + writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1); + } } else { reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); @@ -796,28 +766,36 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, /* TTBCR */ if (stage1) { - reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); - if (smmu->version > ARM_SMMU_V1) { - reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; - reg |= TTBCR2_SEP_UPSTREAM; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + reg = pgtbl_cfg->arm_v7s_cfg.tcr; + reg2 = 0; + } else { + reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr; + reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; + reg2 |= TTBCR2_SEP_UPSTREAM; } + if (smmu->version > ARM_SMMU_V1) + writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2); } else { reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); } + writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); /* MAIRs (stage-1 only) */ if (stage1) { - reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + reg = pgtbl_cfg->arm_v7s_cfg.prrr; + reg2 = pgtbl_cfg->arm_v7s_cfg.nmrr; + } else { + reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; + reg2 = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; + } writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0); - reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR1); + writel_relaxed(reg2, cb_base + ARM_SMMU_CB_S1_MAIR1); } /* SCTLR */ - reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP; + reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE | SCTLR_M; if (stage1) reg |= SCTLR_S1_ASIDPNE; #ifdef __BIG_ENDIAN @@ -841,12 +819,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, if (smmu_domain->smmu) goto out_unlock; - /* We're bypassing these SIDs, so don't allocate an actual context */ - if (domain->type == IOMMU_DOMAIN_DMA) { - smmu_domain->smmu = smmu; - goto out_unlock; - } - /* * Mapping the requested stage onto what we support is surprisingly * complicated, mainly because the spec allows S1+S2 SMMUs without @@ -880,6 +852,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, */ if (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_L) cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_L; + if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) && + !IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_ARM_LPAE) && + (smmu->features & ARM_SMMU_FEAT_FMT_AARCH32_S) && + (smmu_domain->stage == ARM_SMMU_DOMAIN_S1)) + cfg->fmt = ARM_SMMU_CTX_FMT_AARCH32_S; if ((IS_ENABLED(CONFIG_64BIT) || cfg->fmt == ARM_SMMU_CTX_FMT_NONE) && (smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_64K | ARM_SMMU_FEAT_FMT_AARCH64_16K | @@ -899,10 +876,14 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, oas = smmu->ipa_size; if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) { fmt = ARM_64_LPAE_S1; - } else { + } else if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_L) { fmt = ARM_32_LPAE_S1; ias = min(ias, 32UL); oas = min(oas, 40UL); + } else { + fmt = ARM_V7S; + ias = min(ias, 32UL); + oas = min(oas, 32UL); } break; case ARM_SMMU_DOMAIN_NESTED: @@ -958,6 +939,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, /* Update the domain's page sizes to reflect the page table format */ domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; + domain->geometry.aperture_end = (1UL << ias) - 1; + domain->geometry.force_aperture = true; /* Initialise the context bank with our page table cfg */ arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); @@ -996,7 +979,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) void __iomem *cb_base; int irq; - if (!smmu || domain->type == IOMMU_DOMAIN_DMA) + if (!smmu) return; /* @@ -1030,8 +1013,8 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) if (!smmu_domain) return NULL; - if (type == IOMMU_DOMAIN_DMA && - iommu_get_dma_cookie(&smmu_domain->domain)) { + if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding || + iommu_get_dma_cookie(&smmu_domain->domain))) { kfree(smmu_domain); return NULL; } @@ -1055,162 +1038,197 @@ static void arm_smmu_domain_free(struct iommu_domain *domain) kfree(smmu_domain); } -static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu, - struct arm_smmu_master_cfg *cfg) +static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx) { - int i; - struct arm_smmu_smr *smrs; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); + struct arm_smmu_smr *smr = smmu->smrs + idx; + u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT; - if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH)) - return 0; + if (smr->valid) + reg |= SMR_VALID; + writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx)); +} - if (cfg->smrs) - return -EEXIST; +static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) +{ + struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; + u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT | + (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT | + (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT; - smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL); - if (!smrs) { - dev_err(smmu->dev, "failed to allocate %d SMRs\n", - cfg->num_streamids); - return -ENOMEM; - } + writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx)); +} - /* Allocate the SMRs on the SMMU */ - for (i = 0; i < cfg->num_streamids; ++i) { - int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0, - smmu->num_mapping_groups); - if (idx < 0) { - dev_err(smmu->dev, "failed to allocate free SMR\n"); - goto err_free_smrs; - } +static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) +{ + arm_smmu_write_s2cr(smmu, idx); + if (smmu->smrs) + arm_smmu_write_smr(smmu, idx); +} - smrs[i] = (struct arm_smmu_smr) { - .idx = idx, - .mask = 0, /* We don't currently share SMRs */ - .id = cfg->streamids[i], - }; - } +static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask) +{ + struct arm_smmu_smr *smrs = smmu->smrs; + int i, free_idx = -ENOSPC; - /* It worked! Now, poke the actual hardware */ - for (i = 0; i < cfg->num_streamids; ++i) { - u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT | - smrs[i].mask << SMR_MASK_SHIFT; - writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx)); - } + /* Stream indexing is blissfully easy */ + if (!smrs) + return id; - cfg->smrs = smrs; - return 0; + /* Validating SMRs is... less so */ + for (i = 0; i < smmu->num_mapping_groups; ++i) { + if (!smrs[i].valid) { + /* + * Note the first free entry we come across, which + * we'll claim in the end if nothing else matches. + */ + if (free_idx < 0) + free_idx = i; + continue; + } + /* + * If the new entry is _entirely_ matched by an existing entry, + * then reuse that, with the guarantee that there also cannot + * be any subsequent conflicting entries. In normal use we'd + * expect simply identical entries for this case, but there's + * no harm in accommodating the generalisation. + */ + if ((mask & smrs[i].mask) == mask && + !((id ^ smrs[i].id) & ~smrs[i].mask)) + return i; + /* + * If the new entry has any other overlap with an existing one, + * though, then there always exists at least one stream ID + * which would cause a conflict, and we can't allow that risk. + */ + if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask))) + return -EINVAL; + } -err_free_smrs: - while (--i >= 0) - __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx); - kfree(smrs); - return -ENOSPC; + return free_idx; } -static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu, - struct arm_smmu_master_cfg *cfg) +static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx) { - int i; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); - struct arm_smmu_smr *smrs = cfg->smrs; - - if (!smrs) - return; - - /* Invalidate the SMRs before freeing back to the allocator */ - for (i = 0; i < cfg->num_streamids; ++i) { - u8 idx = smrs[i].idx; + if (--smmu->s2crs[idx].count) + return false; - writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx)); - __arm_smmu_free_bitmap(smmu->smr_map, idx); - } + smmu->s2crs[idx] = s2cr_init_val; + if (smmu->smrs) + smmu->smrs[idx].valid = false; - cfg->smrs = NULL; - kfree(smrs); + return true; } -static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, - struct arm_smmu_master_cfg *cfg) +static int arm_smmu_master_alloc_smes(struct device *dev) { - int i, ret; - struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv; + struct arm_smmu_device *smmu = cfg->smmu; + struct arm_smmu_smr *smrs = smmu->smrs; + struct iommu_group *group; + int i, idx, ret; - /* - * FIXME: This won't be needed once we have IOMMU-backed DMA ops - * for all devices behind the SMMU. Note that we need to take - * care configuring SMRs for devices both a platform_device and - * and a PCI device (i.e. a PCI host controller) - */ - if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA) - return 0; + mutex_lock(&smmu->stream_map_mutex); + /* Figure out a viable stream map entry allocation */ + for_each_cfg_sme(fwspec, i, idx) { + u16 sid = fwspec->ids[i]; + u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT; - /* Devices in an IOMMU group may already be configured */ - ret = arm_smmu_master_configure_smrs(smmu, cfg); - if (ret) - return ret == -EEXIST ? 0 : ret; + if (idx != INVALID_SMENDX) { + ret = -EEXIST; + goto out_err; + } - for (i = 0; i < cfg->num_streamids; ++i) { - u32 idx, s2cr; + ret = arm_smmu_find_sme(smmu, sid, mask); + if (ret < 0) + goto out_err; + + idx = ret; + if (smrs && smmu->s2crs[idx].count == 0) { + smrs[idx].id = sid; + smrs[idx].mask = mask; + smrs[idx].valid = true; + } + smmu->s2crs[idx].count++; + cfg->smendx[i] = (s16)idx; + } - idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i]; - s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV | - (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT); - writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); + group = iommu_group_get_for_dev(dev); + if (!group) + group = ERR_PTR(-ENOMEM); + if (IS_ERR(group)) { + ret = PTR_ERR(group); + goto out_err; } + iommu_group_put(group); + /* It worked! Now, poke the actual hardware */ + for_each_cfg_sme(fwspec, i, idx) { + arm_smmu_write_sme(smmu, idx); + smmu->s2crs[idx].group = group; + } + + mutex_unlock(&smmu->stream_map_mutex); return 0; + +out_err: + while (i--) { + arm_smmu_free_sme(smmu, cfg->smendx[i]); + cfg->smendx[i] = INVALID_SMENDX; + } + mutex_unlock(&smmu->stream_map_mutex); + return ret; } -static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, - struct arm_smmu_master_cfg *cfg) +static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec) { - int i; - struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); - - /* An IOMMU group is torn down by the first device to be removed */ - if ((smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && !cfg->smrs) - return; + struct arm_smmu_device *smmu = fwspec_smmu(fwspec); + struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv; + int i, idx; - /* - * We *must* clear the S2CR first, because freeing the SMR means - * that it can be re-allocated immediately. - */ - for (i = 0; i < cfg->num_streamids; ++i) { - u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i]; - u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS; - - writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx)); + mutex_lock(&smmu->stream_map_mutex); + for_each_cfg_sme(fwspec, i, idx) { + if (arm_smmu_free_sme(smmu, idx)) + arm_smmu_write_sme(smmu, idx); + cfg->smendx[i] = INVALID_SMENDX; } - - arm_smmu_master_free_smrs(smmu, cfg); + mutex_unlock(&smmu->stream_map_mutex); } -static void arm_smmu_detach_dev(struct device *dev, - struct arm_smmu_master_cfg *cfg) +static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, + struct iommu_fwspec *fwspec) { - struct iommu_domain *domain = dev->archdata.iommu; - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct arm_smmu_device *smmu = smmu_domain->smmu; + struct arm_smmu_s2cr *s2cr = smmu->s2crs; + enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS; + u8 cbndx = smmu_domain->cfg.cbndx; + int i, idx; + + for_each_cfg_sme(fwspec, i, idx) { + if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx) + continue; - dev->archdata.iommu = NULL; - arm_smmu_domain_remove_master(smmu_domain, cfg); + s2cr[idx].type = type; + s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV; + s2cr[idx].cbndx = cbndx; + arm_smmu_write_s2cr(smmu, idx); + } + return 0; } static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { int ret; - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct iommu_fwspec *fwspec = dev->iommu_fwspec; struct arm_smmu_device *smmu; - struct arm_smmu_master_cfg *cfg; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - smmu = find_smmu_for_device(dev); - if (!smmu) { + if (!fwspec || fwspec->ops != &arm_smmu_ops) { dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); return -ENXIO; } + smmu = fwspec_smmu(fwspec); /* Ensure that the domain is finalised */ ret = arm_smmu_init_domain_context(domain, smmu); if (ret < 0) @@ -1228,18 +1246,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) } /* Looks ok, so add the device to the domain */ - cfg = find_smmu_master_cfg(dev); - if (!cfg) - return -ENODEV; - - /* Detach the dev from its current domain */ - if (dev->archdata.iommu) - arm_smmu_detach_dev(dev, cfg); - - ret = arm_smmu_domain_add_master(smmu_domain, cfg); - if (!ret) - dev->archdata.iommu = domain; - return ret; + return arm_smmu_domain_add_master(smmu_domain, fwspec); } static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, @@ -1358,110 +1365,113 @@ static bool arm_smmu_capable(enum iommu_cap cap) } } -static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) +static int arm_smmu_match_node(struct device *dev, void *data) { - *((u16 *)data) = alias; - return 0; /* Continue walking */ + return dev->of_node == data; } -static void __arm_smmu_release_pci_iommudata(void *data) +static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np) { - kfree(data); + struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL, + np, arm_smmu_match_node); + put_device(dev); + return dev ? dev_get_drvdata(dev) : NULL; } -static int arm_smmu_init_pci_device(struct pci_dev *pdev, - struct iommu_group *group) +static int arm_smmu_add_device(struct device *dev) { + struct arm_smmu_device *smmu; struct arm_smmu_master_cfg *cfg; - u16 sid; - int i; - - cfg = iommu_group_get_iommudata(group); - if (!cfg) { - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + int i, ret; - iommu_group_set_iommudata(group, cfg, - __arm_smmu_release_pci_iommudata); + if (using_legacy_binding) { + ret = arm_smmu_register_legacy_master(dev, &smmu); + fwspec = dev->iommu_fwspec; + if (ret) + goto out_free; + } else if (fwspec) { + smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode)); + } else { + return -ENODEV; } - if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) - return -ENOSPC; + ret = -EINVAL; + for (i = 0; i < fwspec->num_ids; i++) { + u16 sid = fwspec->ids[i]; + u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT; - /* - * Assume Stream ID == Requester ID for now. - * We need a way to describe the ID mappings in FDT. - */ - pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); - for (i = 0; i < cfg->num_streamids; ++i) - if (cfg->streamids[i] == sid) - break; - - /* Avoid duplicate SIDs, as this can lead to SMR conflicts */ - if (i == cfg->num_streamids) - cfg->streamids[cfg->num_streamids++] = sid; - - return 0; -} - -static int arm_smmu_init_platform_device(struct device *dev, - struct iommu_group *group) -{ - struct arm_smmu_device *smmu = find_smmu_for_device(dev); - struct arm_smmu_master *master; + if (sid & ~smmu->streamid_mask) { + dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n", + sid, smmu->streamid_mask); + goto out_free; + } + if (mask & ~smmu->smr_mask_mask) { + dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n", + sid, smmu->smr_mask_mask); + goto out_free; + } + } - if (!smmu) - return -ENODEV; + ret = -ENOMEM; + cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]), + GFP_KERNEL); + if (!cfg) + goto out_free; - master = find_smmu_master(smmu, dev->of_node); - if (!master) - return -ENODEV; + cfg->smmu = smmu; + fwspec->iommu_priv = cfg; + while (i--) + cfg->smendx[i] = INVALID_SMENDX; - iommu_group_set_iommudata(group, &master->cfg, NULL); + ret = arm_smmu_master_alloc_smes(dev); + if (ret) + goto out_free; return 0; -} -static int arm_smmu_add_device(struct device *dev) -{ - struct iommu_group *group; - - group = iommu_group_get_for_dev(dev); - if (IS_ERR(group)) - return PTR_ERR(group); - - iommu_group_put(group); - return 0; +out_free: + if (fwspec) + kfree(fwspec->iommu_priv); + iommu_fwspec_free(dev); + return ret; } static void arm_smmu_remove_device(struct device *dev) { + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + + if (!fwspec || fwspec->ops != &arm_smmu_ops) + return; + + arm_smmu_master_free_smes(fwspec); iommu_group_remove_device(dev); + kfree(fwspec->iommu_priv); + iommu_fwspec_free(dev); } static struct iommu_group *arm_smmu_device_group(struct device *dev) { - struct iommu_group *group; - int ret; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct arm_smmu_device *smmu = fwspec_smmu(fwspec); + struct iommu_group *group = NULL; + int i, idx; - if (dev_is_pci(dev)) - group = pci_device_group(dev); - else - group = generic_device_group(dev); + for_each_cfg_sme(fwspec, i, idx) { + if (group && smmu->s2crs[idx].group && + group != smmu->s2crs[idx].group) + return ERR_PTR(-EINVAL); + + group = smmu->s2crs[idx].group; + } - if (IS_ERR(group)) + if (group) return group; if (dev_is_pci(dev)) - ret = arm_smmu_init_pci_device(to_pci_dev(dev), group); + group = pci_device_group(dev); else - ret = arm_smmu_init_platform_device(dev, group); - - if (ret) { - iommu_group_put(group); - group = ERR_PTR(ret); - } + group = generic_device_group(dev); return group; } @@ -1510,6 +1520,19 @@ out_unlock: return ret; } +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) +{ + u32 fwid = 0; + + if (args->args_count > 0) + fwid |= (u16)args->args[0]; + + if (args->args_count > 1) + fwid |= (u16)args->args[1] << SMR_MASK_SHIFT; + + return iommu_fwspec_add_ids(dev, &fwid, 1); +} + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, @@ -1524,6 +1547,7 @@ static struct iommu_ops arm_smmu_ops = { .device_group = arm_smmu_device_group, .domain_get_attr = arm_smmu_domain_get_attr, .domain_set_attr = arm_smmu_domain_set_attr, + .of_xlate = arm_smmu_of_xlate, .pgsize_bitmap = -1UL, /* Restricted during device attach */ }; @@ -1531,19 +1555,19 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) { void __iomem *gr0_base = ARM_SMMU_GR0(smmu); void __iomem *cb_base; - int i = 0; + int i; u32 reg, major; /* clear global FSR */ reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR); writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR); - /* Mark all SMRn as invalid and all S2CRn as bypass unless overridden */ - reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS; - for (i = 0; i < smmu->num_mapping_groups; ++i) { - writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i)); - writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i)); - } + /* + * Reset stream mapping groups: Initial values mark all SMRn as + * invalid and all S2CRn as bypass unless overridden. + */ + for (i = 0; i < smmu->num_mapping_groups; ++i) + arm_smmu_write_sme(smmu, i); /* * Before clearing ARM_MMU500_ACTLR_CPRE, need to @@ -1632,6 +1656,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) void __iomem *gr0_base = ARM_SMMU_GR0(smmu); u32 id; bool cttw_dt, cttw_reg; + int i; dev_notice(smmu->dev, "probing hardware configuration...\n"); dev_notice(smmu->dev, "SMMUv%d with:\n", @@ -1690,39 +1715,55 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) dev_notice(smmu->dev, "\t(IDR0.CTTW overridden by dma-coherent property)\n"); + /* Max. number of entries we have for stream matching/indexing */ + size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK); + smmu->streamid_mask = size - 1; if (id & ID0_SMS) { - u32 smr, sid, mask; + u32 smr; smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH; - smmu->num_mapping_groups = (id >> ID0_NUMSMRG_SHIFT) & - ID0_NUMSMRG_MASK; - if (smmu->num_mapping_groups == 0) { + size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK; + if (size == 0) { dev_err(smmu->dev, "stream-matching supported, but no SMRs present!\n"); return -ENODEV; } - smr = SMR_MASK_MASK << SMR_MASK_SHIFT; - smr |= (SMR_ID_MASK << SMR_ID_SHIFT); + /* + * SMR.ID bits may not be preserved if the corresponding MASK + * bits are set, so check each one separately. We can reject + * masters later if they try to claim IDs outside these masks. + */ + smr = smmu->streamid_mask << SMR_ID_SHIFT; writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0)); smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0)); + smmu->streamid_mask = smr >> SMR_ID_SHIFT; - mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK; - sid = (smr >> SMR_ID_SHIFT) & SMR_ID_MASK; - if ((mask & sid) != sid) { - dev_err(smmu->dev, - "SMR mask bits (0x%x) insufficient for ID field (0x%x)\n", - mask, sid); - return -ENODEV; - } + smr = smmu->streamid_mask << SMR_MASK_SHIFT; + writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0)); + smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0)); + smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT; + + /* Zero-initialised to mark as invalid */ + smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs), + GFP_KERNEL); + if (!smmu->smrs) + return -ENOMEM; dev_notice(smmu->dev, - "\tstream matching with %u register groups, mask 0x%x", - smmu->num_mapping_groups, mask); - } else { - smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) & - ID0_NUMSIDB_MASK; + "\tstream matching with %lu register groups, mask 0x%x", + size, smmu->smr_mask_mask); } + /* s2cr->type == 0 means translation, so initialise explicitly */ + smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs), + GFP_KERNEL); + if (!smmu->s2crs) + return -ENOMEM; + for (i = 0; i < size; i++) + smmu->s2crs[i] = s2cr_init_val; + + smmu->num_mapping_groups = size; + mutex_init(&smmu->stream_map_mutex); if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) { smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; @@ -1855,15 +1896,24 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match); static int arm_smmu_device_dt_probe(struct platform_device *pdev) { - const struct of_device_id *of_id; const struct arm_smmu_match_data *data; struct resource *res; struct arm_smmu_device *smmu; struct device *dev = &pdev->dev; - struct rb_node *node; - struct of_phandle_iterator it; - struct arm_smmu_phandle_args *masterspec; int num_irqs, i, err; + bool legacy_binding; + + legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL); + if (legacy_binding && !using_generic_binding) { + if (!using_legacy_binding) + pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n"); + using_legacy_binding = true; + } else if (!legacy_binding && !using_legacy_binding) { + using_generic_binding = true; + } else { + dev_err(dev, "not probing due to mismatched DT properties\n"); + return -ENODEV; + } smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); if (!smmu) { @@ -1872,8 +1922,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } smmu->dev = dev; - of_id = of_match_node(arm_smmu_of_match, dev->of_node); - data = of_id->data; + data = of_device_get_match_data(dev); smmu->version = data->version; smmu->model = data->model; @@ -1923,37 +1972,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) if (err) return err; - i = 0; - smmu->masters = RB_ROOT; - - err = -ENOMEM; - /* No need to zero the memory for masterspec */ - masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL); - if (!masterspec) - goto out_put_masters; - - of_for_each_phandle(&it, err, dev->of_node, - "mmu-masters", "#stream-id-cells", 0) { - int count = of_phandle_iterator_args(&it, masterspec->args, - MAX_MASTER_STREAMIDS); - masterspec->np = of_node_get(it.node); - masterspec->args_count = count; - - err = register_smmu_master(smmu, dev, masterspec); - if (err) { - dev_err(dev, "failed to add master %s\n", - masterspec->np->name); - kfree(masterspec); - goto out_put_masters; - } - - i++; - } - - dev_notice(dev, "registered %d master devices\n", i); - - kfree(masterspec); - parse_driver_options(smmu); if (smmu->version == ARM_SMMU_V2 && @@ -1961,8 +1979,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) dev_err(dev, "found only %d context interrupt(s) but %d required\n", smmu->num_context_irqs, smmu->num_context_banks); - err = -ENODEV; - goto out_put_masters; + return -ENODEV; } for (i = 0; i < smmu->num_global_irqs; ++i) { @@ -1974,59 +1991,39 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) if (err) { dev_err(dev, "failed to request global IRQ %d (%u)\n", i, smmu->irqs[i]); - goto out_put_masters; + return err; } } - INIT_LIST_HEAD(&smmu->list); - spin_lock(&arm_smmu_devices_lock); - list_add(&smmu->list, &arm_smmu_devices); - spin_unlock(&arm_smmu_devices_lock); - + of_iommu_set_ops(dev->of_node, &arm_smmu_ops); + platform_set_drvdata(pdev, smmu); arm_smmu_device_reset(smmu); - return 0; -out_put_masters: - for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { - struct arm_smmu_master *master - = container_of(node, struct arm_smmu_master, node); - of_node_put(master->of_node); + /* Oh, for a proper bus abstraction */ + if (!iommu_present(&platform_bus_type)) + bus_set_iommu(&platform_bus_type, &arm_smmu_ops); +#ifdef CONFIG_ARM_AMBA + if (!iommu_present(&amba_bustype)) + bus_set_iommu(&amba_bustype, &arm_smmu_ops); +#endif +#ifdef CONFIG_PCI + if (!iommu_present(&pci_bus_type)) { + pci_request_acs(); + bus_set_iommu(&pci_bus_type, &arm_smmu_ops); } - - return err; +#endif + return 0; } static int arm_smmu_device_remove(struct platform_device *pdev) { - int i; - struct device *dev = &pdev->dev; - struct arm_smmu_device *curr, *smmu = NULL; - struct rb_node *node; - - spin_lock(&arm_smmu_devices_lock); - list_for_each_entry(curr, &arm_smmu_devices, list) { - if (curr->dev == dev) { - smmu = curr; - list_del(&smmu->list); - break; - } - } - spin_unlock(&arm_smmu_devices_lock); + struct arm_smmu_device *smmu = platform_get_drvdata(pdev); if (!smmu) return -ENODEV; - for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { - struct arm_smmu_master *master - = container_of(node, struct arm_smmu_master, node); - of_node_put(master->of_node); - } - if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS)) - dev_err(dev, "removing device with active domains!\n"); - - for (i = 0; i < smmu->num_global_irqs; ++i) - devm_free_irq(smmu->dev, smmu->irqs[i], smmu); + dev_err(&pdev->dev, "removing device with active domains!\n"); /* Turn the thing off */ writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); @@ -2044,41 +2041,14 @@ static struct platform_driver arm_smmu_driver = { static int __init arm_smmu_init(void) { - struct device_node *np; - int ret; - - /* - * Play nice with systems that don't have an ARM SMMU by checking that - * an ARM SMMU exists in the system before proceeding with the driver - * and IOMMU bus operation registration. - */ - np = of_find_matching_node(NULL, arm_smmu_of_match); - if (!np) - return 0; - - of_node_put(np); - - ret = platform_driver_register(&arm_smmu_driver); - if (ret) - return ret; - - /* Oh, for a proper bus abstraction */ - if (!iommu_present(&platform_bus_type)) - bus_set_iommu(&platform_bus_type, &arm_smmu_ops); - -#ifdef CONFIG_ARM_AMBA - if (!iommu_present(&amba_bustype)) - bus_set_iommu(&amba_bustype, &arm_smmu_ops); -#endif + static bool registered; + int ret = 0; -#ifdef CONFIG_PCI - if (!iommu_present(&pci_bus_type)) { - pci_request_acs(); - bus_set_iommu(&pci_bus_type, &arm_smmu_ops); + if (!registered) { + ret = platform_driver_register(&arm_smmu_driver); + registered = !ret; } -#endif - - return 0; + return ret; } static void __exit arm_smmu_exit(void) @@ -2089,6 +2059,25 @@ static void __exit arm_smmu_exit(void) subsys_initcall(arm_smmu_init); module_exit(arm_smmu_exit); +static int __init arm_smmu_of_init(struct device_node *np) +{ + int ret = arm_smmu_init(); + + if (ret) + return ret; + + if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root)) + return -ENODEV; + + return 0; +} +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", arm_smmu_of_init); +IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", arm_smmu_of_init); + MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 00c8a08d56e7..c5ab8667e6f2 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -25,10 +25,29 @@ #include <linux/huge_mm.h> #include <linux/iommu.h> #include <linux/iova.h> +#include <linux/irq.h> #include <linux/mm.h> +#include <linux/pci.h> #include <linux/scatterlist.h> #include <linux/vmalloc.h> +struct iommu_dma_msi_page { + struct list_head list; + dma_addr_t iova; + phys_addr_t phys; +}; + +struct iommu_dma_cookie { + struct iova_domain iovad; + struct list_head msi_page_list; + spinlock_t msi_lock; +}; + +static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain) +{ + return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; +} + int iommu_dma_init(void) { return iova_cache_get(); @@ -43,15 +62,19 @@ int iommu_dma_init(void) */ int iommu_get_dma_cookie(struct iommu_domain *domain) { - struct iova_domain *iovad; + struct iommu_dma_cookie *cookie; if (domain->iova_cookie) return -EEXIST; - iovad = kzalloc(sizeof(*iovad), GFP_KERNEL); - domain->iova_cookie = iovad; + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return -ENOMEM; - return iovad ? 0 : -ENOMEM; + spin_lock_init(&cookie->msi_lock); + INIT_LIST_HEAD(&cookie->msi_page_list); + domain->iova_cookie = cookie; + return 0; } EXPORT_SYMBOL(iommu_get_dma_cookie); @@ -63,32 +86,58 @@ EXPORT_SYMBOL(iommu_get_dma_cookie); */ void iommu_put_dma_cookie(struct iommu_domain *domain) { - struct iova_domain *iovad = domain->iova_cookie; + struct iommu_dma_cookie *cookie = domain->iova_cookie; + struct iommu_dma_msi_page *msi, *tmp; - if (!iovad) + if (!cookie) return; - if (iovad->granule) - put_iova_domain(iovad); - kfree(iovad); + if (cookie->iovad.granule) + put_iova_domain(&cookie->iovad); + + list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) { + list_del(&msi->list); + kfree(msi); + } + kfree(cookie); domain->iova_cookie = NULL; } EXPORT_SYMBOL(iommu_put_dma_cookie); +static void iova_reserve_pci_windows(struct pci_dev *dev, + struct iova_domain *iovad) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus); + struct resource_entry *window; + unsigned long lo, hi; + + resource_list_for_each_entry(window, &bridge->windows) { + if (resource_type(window->res) != IORESOURCE_MEM && + resource_type(window->res) != IORESOURCE_IO) + continue; + + lo = iova_pfn(iovad, window->res->start - window->offset); + hi = iova_pfn(iovad, window->res->end - window->offset); + reserve_iova(iovad, lo, hi); + } +} + /** * iommu_dma_init_domain - Initialise a DMA mapping domain * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() * @base: IOVA at which the mappable address space starts * @size: Size of IOVA space + * @dev: Device the domain is being initialised for * * @base and @size should be exact multiples of IOMMU page granularity to * avoid rounding surprises. If necessary, we reserve the page at address 0 * to ensure it is an invalid IOVA. It is safe to reinitialise a domain, but * any change which could make prior IOVAs invalid will fail. */ -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size) +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, + u64 size, struct device *dev) { - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); unsigned long order, base_pfn, end_pfn; if (!iovad) @@ -124,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size iovad->dma_32bit_pfn = end_pfn; } else { init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn); + if (dev && dev_is_pci(dev)) + iova_reserve_pci_windows(to_pci_dev(dev), iovad); } return 0; } @@ -155,7 +206,7 @@ int dma_direction_to_prot(enum dma_data_direction dir, bool coherent) static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size, dma_addr_t dma_limit) { - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); unsigned long shift = iova_shift(iovad); unsigned long length = iova_align(iovad, size) >> shift; @@ -171,7 +222,7 @@ static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size, /* The IOVA allocator knows what we mapped, so just unmap whatever that was */ static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr) { - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); unsigned long shift = iova_shift(iovad); unsigned long pfn = dma_addr >> shift; struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +345,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp, void (*flush_page)(struct device *, const void *, phys_addr_t)) { struct iommu_domain *domain = iommu_get_domain_for_dev(dev); - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); struct iova *iova; struct page **pages; struct sg_table sgt; @@ -386,7 +437,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, { dma_addr_t dma_addr; struct iommu_domain *domain = iommu_get_domain_for_dev(dev); - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); phys_addr_t phys = page_to_phys(page) + offset; size_t iova_off = iova_offset(iovad, phys); size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +546,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, int prot) { struct iommu_domain *domain = iommu_get_domain_for_dev(dev); - struct iova_domain *iovad = domain->iova_cookie; + struct iova_domain *iovad = cookie_iovad(domain); struct iova *iova; struct scatterlist *s, *prev = NULL; dma_addr_t dma_addr; @@ -587,3 +638,81 @@ int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == DMA_ERROR_CODE; } + +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, + phys_addr_t msi_addr, struct iommu_domain *domain) +{ + struct iommu_dma_cookie *cookie = domain->iova_cookie; + struct iommu_dma_msi_page *msi_page; + struct iova_domain *iovad = &cookie->iovad; + struct iova *iova; + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; + + msi_addr &= ~(phys_addr_t)iova_mask(iovad); + list_for_each_entry(msi_page, &cookie->msi_page_list, list) + if (msi_page->phys == msi_addr) + return msi_page; + + msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC); + if (!msi_page) + return NULL; + + iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev)); + if (!iova) + goto out_free_page; + + msi_page->phys = msi_addr; + msi_page->iova = iova_dma_addr(iovad, iova); + if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot)) + goto out_free_iova; + + INIT_LIST_HEAD(&msi_page->list); + list_add(&msi_page->list, &cookie->msi_page_list); + return msi_page; + +out_free_iova: + __free_iova(iovad, iova); +out_free_page: + kfree(msi_page); + return NULL; +} + +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) +{ + struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq)); + struct iommu_domain *domain = iommu_get_domain_for_dev(dev); + struct iommu_dma_cookie *cookie; + struct iommu_dma_msi_page *msi_page; + phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo; + unsigned long flags; + + if (!domain || !domain->iova_cookie) + return; + + cookie = domain->iova_cookie; + + /* + * We disable IRQs to rule out a possible inversion against + * irq_desc_lock if, say, someone tries to retarget the affinity + * of an MSI from within an IPI handler. + */ + spin_lock_irqsave(&cookie->msi_lock, flags); + msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain); + spin_unlock_irqrestore(&cookie->msi_lock, flags); + + if (WARN_ON(!msi_page)) { + /* + * We're called from a void callback, so the best we can do is + * 'fail' by filling the message with obviously bogus values. + * Since we got this far due to an IOMMU being present, it's + * not like the existing address would have worked anyway... + */ + msg->address_hi = ~0U; + msg->address_lo = ~0U; + msg->data = ~0U; + } else { + msg->address_hi = upper_32_bits(msi_page->iova); + msg->address_lo &= iova_mask(&cookie->iovad); + msg->address_lo += lower_32_bits(msi_page->iova); + } +} diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 33dcc29ec200..30808e91b775 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1345,8 +1345,8 @@ static int __init exynos_iommu_of_setup(struct device_node *np) exynos_iommu_init(); pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); + if (!pdev) + return -ENODEV; /* * use the first registered sysmmu device for performing diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ebb5bf3ddbd9..a4407eabf0e6 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2452,20 +2452,15 @@ static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque) return 0; } -/* domain is initialized */ -static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) +static struct dmar_domain *find_or_alloc_domain(struct device *dev, int gaw) { struct device_domain_info *info = NULL; - struct dmar_domain *domain, *tmp; + struct dmar_domain *domain = NULL; struct intel_iommu *iommu; u16 req_id, dma_alias; unsigned long flags; u8 bus, devfn; - domain = find_domain(dev); - if (domain) - return domain; - iommu = device_to_iommu(dev, &bus, &devfn); if (!iommu) return NULL; @@ -2487,9 +2482,9 @@ static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) } spin_unlock_irqrestore(&device_domain_lock, flags); - /* DMA alias already has a domain, uses it */ + /* DMA alias already has a domain, use it */ if (info) - goto found_domain; + goto out; } /* Allocate and initialize new domain for the device */ @@ -2501,28 +2496,67 @@ static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) return NULL; } - /* register PCI DMA alias device */ - if (dev_is_pci(dev) && req_id != dma_alias) { - tmp = dmar_insert_one_dev_info(iommu, PCI_BUS_NUM(dma_alias), - dma_alias & 0xff, NULL, domain); +out: - if (!tmp || tmp != domain) { - domain_exit(domain); - domain = tmp; - } + return domain; +} - if (!domain) - return NULL; +static struct dmar_domain *set_domain_for_dev(struct device *dev, + struct dmar_domain *domain) +{ + struct intel_iommu *iommu; + struct dmar_domain *tmp; + u16 req_id, dma_alias; + u8 bus, devfn; + + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) + return NULL; + + req_id = ((u16)bus << 8) | devfn; + + if (dev_is_pci(dev)) { + struct pci_dev *pdev = to_pci_dev(dev); + + pci_for_each_dma_alias(pdev, get_last_alias, &dma_alias); + + /* register PCI DMA alias device */ + if (req_id != dma_alias) { + tmp = dmar_insert_one_dev_info(iommu, PCI_BUS_NUM(dma_alias), + dma_alias & 0xff, NULL, domain); + + if (!tmp || tmp != domain) + return tmp; + } } -found_domain: tmp = dmar_insert_one_dev_info(iommu, bus, devfn, dev, domain); + if (!tmp || tmp != domain) + return tmp; + + return domain; +} - if (!tmp || tmp != domain) { +static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) +{ + struct dmar_domain *domain, *tmp; + + domain = find_domain(dev); + if (domain) + goto out; + + domain = find_or_alloc_domain(dev, gaw); + if (!domain) + goto out; + + tmp = set_domain_for_dev(dev, domain); + if (!tmp || domain != tmp) { domain_exit(domain); domain = tmp; } +out: + return domain; } @@ -3394,17 +3428,18 @@ static unsigned long intel_alloc_iova(struct device *dev, static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev) { + struct dmar_domain *domain, *tmp; struct dmar_rmrr_unit *rmrr; - struct dmar_domain *domain; struct device *i_dev; int i, ret; - domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH); - if (!domain) { - pr_err("Allocating domain for %s failed\n", - dev_name(dev)); - return NULL; - } + domain = find_domain(dev); + if (domain) + goto out; + + domain = find_or_alloc_domain(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH); + if (!domain) + goto out; /* We have a new domain - setup possible RMRRs for the device */ rcu_read_lock(); @@ -3423,6 +3458,18 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev) } rcu_read_unlock(); + tmp = set_domain_for_dev(dev, domain); + if (!tmp || domain != tmp) { + domain_exit(domain); + domain = tmp; + } + +out: + + if (!domain) + pr_err("Allocating domain for %s failed\n", dev_name(dev)); + + return domain; } diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index def8ca1c982d..f50e51c1a9c8 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -633,6 +633,10 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg, { struct arm_v7s_io_pgtable *data; +#ifdef PHYS_OFFSET + if (upper_32_bits(PHYS_OFFSET)) + return NULL; +#endif if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS) return NULL; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index b06d93594436..9a2f1960873b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -31,6 +31,7 @@ #include <linux/err.h> #include <linux/pci.h> #include <linux/bitops.h> +#include <linux/property.h> #include <trace/events/iommu.h> static struct kset *iommu_group_kset; @@ -1613,3 +1614,60 @@ out: return ret; } + +int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, + const struct iommu_ops *ops) +{ + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + + if (fwspec) + return ops == fwspec->ops ? 0 : -EINVAL; + + fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return -ENOMEM; + + of_node_get(to_of_node(iommu_fwnode)); + fwspec->iommu_fwnode = iommu_fwnode; + fwspec->ops = ops; + dev->iommu_fwspec = fwspec; + return 0; +} +EXPORT_SYMBOL_GPL(iommu_fwspec_init); + +void iommu_fwspec_free(struct device *dev) +{ + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + + if (fwspec) { + fwnode_handle_put(fwspec->iommu_fwnode); + kfree(fwspec); + dev->iommu_fwspec = NULL; + } +} +EXPORT_SYMBOL_GPL(iommu_fwspec_free); + +int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) +{ + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + size_t size; + int i; + + if (!fwspec) + return -EINVAL; + + size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]); + if (size > sizeof(*fwspec)) { + fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL); + if (!fwspec) + return -ENOMEM; + } + + for (i = 0; i < num_ids; i++) + fwspec->ids[fwspec->num_ids + i] = ids[i]; + + fwspec->num_ids += num_ids; + dev->iommu_fwspec = fwspec; + return 0; +} +EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids); diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 2fdbac67a77f..ace331da6459 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -636,7 +636,7 @@ static int ipmmu_add_device(struct device *dev) spin_unlock(&ipmmu_devices_lock); if (ret < 0) - return -ENODEV; + goto error; for (i = 0; i < num_utlbs; ++i) { if (utlbs[i] >= mmu->num_utlbs) { diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 57f23eaaa2f9..5b82862f571f 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -22,6 +22,7 @@ #include <linux/limits.h> #include <linux/of.h> #include <linux/of_iommu.h> +#include <linux/of_pci.h> #include <linux/slab.h> static const struct of_device_id __iommu_of_table_sentinel @@ -134,6 +135,47 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np) return ops; } +static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) +{ + struct of_phandle_args *iommu_spec = data; + + iommu_spec->args[0] = alias; + return iommu_spec->np == pdev->bus->dev.of_node; +} + +static const struct iommu_ops +*of_pci_iommu_configure(struct pci_dev *pdev, struct device_node *bridge_np) +{ + const struct iommu_ops *ops; + struct of_phandle_args iommu_spec; + + /* + * Start by tracing the RID alias down the PCI topology as + * far as the host bridge whose OF node we have... + * (we're not even attempting to handle multi-alias devices yet) + */ + iommu_spec.args_count = 1; + iommu_spec.np = bridge_np; + pci_for_each_dma_alias(pdev, __get_pci_rid, &iommu_spec); + /* + * ...then find out what that becomes once it escapes the PCI + * bus into the system beyond, and which IOMMU it ends up at. + */ + iommu_spec.np = NULL; + if (of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map", + "iommu-map-mask", &iommu_spec.np, iommu_spec.args)) + return NULL; + + ops = of_iommu_get_ops(iommu_spec.np); + if (!ops || !ops->of_xlate || + iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) || + ops->of_xlate(&pdev->dev, &iommu_spec)) + ops = NULL; + + of_node_put(iommu_spec.np); + return ops; +} + const struct iommu_ops *of_iommu_configure(struct device *dev, struct device_node *master_np) { @@ -142,12 +184,8 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, const struct iommu_ops *ops = NULL; int idx = 0; - /* - * We can't do much for PCI devices without knowing how - * device IDs are wired up from the PCI bus to the IOMMU. - */ if (dev_is_pci(dev)) - return NULL; + return of_pci_iommu_configure(to_pci_dev(dev), master_np); /* * We don't currently walk up the tree looking for a parent IOMMU. @@ -160,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, np = iommu_spec.np; ops = of_iommu_get_ops(np); - if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec)) + if (!ops || !ops->of_xlate || + iommu_fwspec_init(dev, &np->fwnode, ops) || + ops->of_xlate(dev, &iommu_spec)) goto err_put_node; of_node_put(np); diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 35eb7ac5d21f..863e073c6f7f 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "GICv2m: " fmt #include <linux/acpi.h> +#include <linux/dma-iommu.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) msg->data -= v2m->spi_offset; + + iommu_dma_map_msi_msg(data->irq, msg); } static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 35c851c14e49..003495d91f9c 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -19,6 +19,7 @@ #include <linux/bitmap.h> #include <linux/cpu.h> #include <linux/delay.h> +#include <linux/dma-iommu.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> #include <linux/acpi_iort.h> @@ -659,6 +660,8 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) msg->address_lo = addr & ((1UL << 32) - 1); msg->address_hi = addr >> 32; msg->data = its_get_event_id(d); + + iommu_dma_map_msi_msg(d->irq, msg); } static struct irq_chip its_irq_chip = { diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 962f2a9a6614..7b8540291217 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -180,14 +180,14 @@ source "drivers/media/firewire/Kconfig" # Common driver options source "drivers/media/common/Kconfig" -comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" +comment "Media ancillary drivers (tuners, sensors, i2c, spi, frontends)" # -# Ancillary drivers (tuners, i2c, frontends) +# Ancillary drivers (tuners, i2c, spi, frontends) # config MEDIA_SUBDRV_AUTOSELECT - bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" + bool "Autoselect ancillary drivers (tuners, sensors, i2c, spi, frontends)" depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT || MEDIA_SDR_SUPPORT depends on HAS_IOMEM select I2C @@ -216,6 +216,7 @@ config MEDIA_ATTACH default MODULES source "drivers/media/i2c/Kconfig" +source "drivers/media/spi/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 081a7866fd44..0deaa93efdee 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -32,6 +32,6 @@ obj-y += rc/ # Finally, merge the drivers that require the core # -obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ +obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ spi/ obj-$(CONFIG_VIDEO_DEV) += radio/ diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 3ec3cebe62b9..1684810cab83 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -504,14 +504,14 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0)) static const int bt601[3][3] = { - { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) }, - { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) }, + { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) }, + { COEFF(-0.1687, 224), COEFF(-0.3313, 224), COEFF(0.5, 224) }, + { COEFF(0.5, 224), COEFF(-0.4187, 224), COEFF(-0.0813, 224) }, }; static const int bt601_full[3][3] = { - { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) }, - { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) }, - { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) }, + { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) }, + { COEFF(-0.1687, 255), COEFF(-0.3313, 255), COEFF(0.5, 255) }, + { COEFF(0.5, 255), COEFF(-0.4187, 255), COEFF(-0.0813, 255) }, }; static const int rec709[3][3] = { { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) }, @@ -558,7 +558,6 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: - case V4L2_YCBCR_ENC_SYCC: rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr); break; case V4L2_YCBCR_ENC_XV601: @@ -674,7 +673,6 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: - case V4L2_YCBCR_ENC_SYCC: ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b); break; case V4L2_YCBCR_ENC_XV601: diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h index 4b4c1da20f4b..aeda2b64931c 100644 --- a/drivers/media/dvb-core/demux.h +++ b/drivers/media/dvb-core/demux.h @@ -202,7 +202,7 @@ struct dmx_section_feed { * * This function callback prototype, provided by the client of the demux API, * is called from the demux code. The function is only called when filtering - * on ae TS feed has been enabled using the start_filtering() function at + * on a TS feed has been enabled using the start_filtering\(\) function at * the &dmx_demux. * Any TS packets that match the filter settings are copied to a circular * buffer. The filtered TS packets are delivered to the client using this @@ -243,8 +243,10 @@ struct dmx_section_feed { * will also be sent to the hardware MPEG decoder. * * Return: - * 0, on success; - * -EOVERFLOW, on buffer overflow. + * + * - 0, on success; + * + * - -EOVERFLOW, on buffer overflow. */ typedef int (*dmx_ts_cb)(const u8 *buffer1, size_t buffer1_length, @@ -293,9 +295,9 @@ typedef int (*dmx_section_cb)(const u8 *buffer1, size_t buffer2_len, struct dmx_section_filter *source); -/*--------------------------------------------------------------------------*/ -/* DVB Front-End */ -/*--------------------------------------------------------------------------*/ +/* + * DVB Front-End + */ /** * enum dmx_frontend_source - Used to identify the type of frontend @@ -349,15 +351,15 @@ enum dmx_demux_caps { /* * Demux resource type identifier. -*/ - -/* - * DMX_FE_ENTRY(): Casts elements in the list of registered - * front-ends from the generic type struct list_head - * to the type * struct dmx_frontend - *. -*/ + */ +/** + * DMX_FE_ENTRY - Casts elements in the list of registered + * front-ends from the generic type struct list_head + * to the type * struct dmx_frontend + * + * @list: list of struct dmx_frontend + */ #define DMX_FE_ENTRY(list) \ list_entry(list, struct dmx_frontend, connectivity_list) @@ -551,7 +553,6 @@ enum dmx_demux_caps { * 0 on success; * -EINVAL on bad parameter. */ - struct dmx_demux { enum dmx_demux_caps capabilities; struct dmx_frontend *frontend; @@ -581,15 +582,12 @@ struct dmx_demux { int (*get_pes_pids)(struct dmx_demux *demux, u16 *pids); - /* private: Not used upstream and never documented */ -#if 0 - int (*get_caps)(struct dmx_demux *demux, struct dmx_caps *caps); - int (*set_source)(struct dmx_demux *demux, const dmx_source_t *src); -#endif + /* private: */ + /* - * private: Only used at av7110, to read some data from firmware. - * As this was never documented, we have no clue about what's - * there, and its usage on other drivers aren't encouraged. + * Only used at av7110, to read some data from firmware. + * As this was never documented, we have no clue about what's + * there, and its usage on other drivers aren't encouraged. */ int (*get_stc)(struct dmx_demux *demux, unsigned int num, u64 *stc, unsigned int *base); diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index be99c8dbc5f8..01511e5a5566 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1969,17 +1969,9 @@ static int dvb_frontend_ioctl_properties(struct file *file, if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); - if (!tvp) { - err = -ENOMEM; - goto out; - } - - if (copy_from_user(tvp, (void __user *)tvps->props, - tvps->num * sizeof(struct dtv_property))) { - err = -EFAULT; - goto out; - } + tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + if (IS_ERR(tvp)) + return PTR_ERR(tvp); for (i = 0; i < tvps->num; i++) { err = dtv_property_process_set(fe, tvp + i, file); @@ -2002,17 +1994,9 @@ static int dvb_frontend_ioctl_properties(struct file *file, if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); - if (!tvp) { - err = -ENOMEM; - goto out; - } - - if (copy_from_user(tvp, (void __user *)tvps->props, - tvps->num * sizeof(struct dtv_property))) { - err = -EFAULT; - goto out; - } + tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp)); + if (IS_ERR(tvp)) + return PTR_ERR(tvp); /* * Let's use our own copy of property cache, in order to diff --git a/drivers/media/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h index 2f0326674ca6..4d11d3529c14 100644 --- a/drivers/media/dvb-core/dvb_math.h +++ b/drivers/media/dvb-core/dvb_math.h @@ -25,7 +25,7 @@ #include <linux/types.h> /** - * cintlog2 - computes log2 of a value; the result is shifted left by 24 bits + * intlog2 - computes log2 of a value; the result is shifted left by 24 bits * * @value: The value (must be != 0) * diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h index 8af642399f1e..bbe94873d44d 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.h +++ b/drivers/media/dvb-core/dvb_ringbuffer.h @@ -18,10 +18,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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. */ #ifndef _DVB_RINGBUFFER_H_ @@ -30,6 +26,18 @@ #include <linux/spinlock.h> #include <linux/wait.h> +/** + * struct dvb_ringbuffer - Describes a ring buffer used at DVB framework + * + * @data: Area were the ringbuffer data is written + * @size: size of the ringbuffer + * @pread: next position to read + * @pwrite: next position to write + * @error: used by ringbuffer clients to indicate that an error happened. + * @queue: Wait queue used by ringbuffer clients to indicate when buffer + * was filled + * @lock: Spinlock used to protect the ringbuffer + */ struct dvb_ringbuffer { u8 *data; ssize_t size; @@ -43,99 +51,161 @@ struct dvb_ringbuffer { #define DVB_RINGBUFFER_PKTHDRSIZE 3 +/** + * dvb_ringbuffer_init - initialize ring buffer, lock and queue + * + * @rbuf: pointer to struct dvb_ringbuffer + * @data: pointer to the buffer where the data will be stored + * @len: bytes from ring buffer into @buf + */ +extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, + size_t len); -/* - * Notes: - * ------ - * (1) For performance reasons read and write routines don't check buffer sizes - * and/or number of bytes free/available. This has to be done before these - * routines are called. For example: - * - * *** write @buflen: bytes *** - * free = dvb_ringbuffer_free(rbuf); - * if (free >= buflen) - * count = dvb_ringbuffer_write(rbuf, buffer, buflen); - * else - * ... - * - * *** read min. 1000, max. @bufsize: bytes *** - * avail = dvb_ringbuffer_avail(rbuf); - * if (avail >= 1000) - * count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); - * else - * ... - * - * (2) If there is exactly one reader and one writer, there is no need - * to lock read or write operations. - * Two or more readers must be locked against each other. - * Flushing the buffer counts as a read operation. - * Resetting the buffer counts as a read and write operation. - * Two or more writers must be locked against each other. - */ - -/* initialize ring buffer, lock and queue */ -extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len); - -/* test whether buffer is empty */ +/** + * dvb_ringbuffer_empty - test whether buffer is empty + * + * @rbuf: pointer to struct dvb_ringbuffer + */ extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf); -/* return the number of free bytes in the buffer */ +/** + * dvb_ringbuffer_free - returns the number of free bytes in the buffer + * + * @rbuf: pointer to struct dvb_ringbuffer + * + * Return: number of free bytes in the buffer + */ extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf); -/* return the number of bytes waiting in the buffer */ +/** + * dvb_ringbuffer_avail - returns the number of bytes waiting in the buffer + * + * @rbuf: pointer to struct dvb_ringbuffer + * + * Return: number of bytes waiting in the buffer + */ extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); - -/* - * Reset the read and write pointers to zero and flush the buffer +/** + * dvb_ringbuffer_reset - resets the ringbuffer to initial state + * + * @rbuf: pointer to struct dvb_ringbuffer + * + * Resets the read and write pointers to zero and flush the buffer. + * * This counts as a read and write operation */ extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); +/* + * read routines & macros + */ -/* read routines & macros */ -/* ---------------------- */ -/* flush buffer */ +/** + * dvb_ringbuffer_flush - flush buffer + * + * @rbuf: pointer to struct dvb_ringbuffer + */ extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf); -/* flush buffer protected by spinlock and wake-up waiting task(s) */ +/** + * dvb_ringbuffer_flush_spinlock_wakeup- flush buffer protected by spinlock + * and wake-up waiting task(s) + * + * @rbuf: pointer to struct dvb_ringbuffer + */ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); -/* peek at byte @offs: in the buffer */ -#define DVB_RINGBUFFER_PEEK(rbuf,offs) \ - (rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size] +/** + * DVB_RINGBUFFER_PEEK - peek at byte @offs in the buffer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @offs: offset inside the ringbuffer + */ +#define DVB_RINGBUFFER_PEEK(rbuf, offs) \ + ((rbuf)->data[((rbuf)->pread + (offs)) % (rbuf)->size]) -/* advance read ptr by @num: bytes */ -#define DVB_RINGBUFFER_SKIP(rbuf,num) \ - (rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size +/** + * DVB_RINGBUFFER_SKIP - advance read ptr by @num bytes + * + * @rbuf: pointer to struct dvb_ringbuffer + * @num: number of bytes to advance + */ +#define DVB_RINGBUFFER_SKIP(rbuf, num) {\ + (rbuf)->pread = ((rbuf)->pread + (num)) % (rbuf)->size;\ +} -/* - * read @len: bytes from ring buffer into @buf: - * @usermem: specifies whether @buf: resides in user space - * returns number of bytes transferred or -EFAULT +/** + * dvb_ringbuffer_read_user - Reads a buffer into an user pointer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @buf: pointer to the buffer where the data will be stored + * @len: bytes from ring buffer into @buf + * + * This variant assumes that the buffer is a memory at the userspace. So, + * it will internally call copy_to_user(). + * + * Return: number of bytes transferred or -EFAULT */ extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len); + +/** + * dvb_ringbuffer_read - Reads a buffer into a pointer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @buf: pointer to the buffer where the data will be stored + * @len: bytes from ring buffer into @buf + * + * This variant assumes that the buffer is a memory at the Kernel space + * + * Return: number of bytes transferred or -EFAULT + */ extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len); - -/* write routines & macros */ -/* ----------------------- */ -/* write single byte to ring buffer */ -#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte) \ - { (rbuf)->data[(rbuf)->pwrite]=(byte); \ - (rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; } /* - * write @len: bytes to ring buffer - * @usermem: specifies whether @buf: resides in user space - * returns number of bytes transferred or -EFAULT -*/ + * write routines & macros + */ + +/** + * DVB_RINGBUFFER_WRITE_BYTE - write single byte to ring buffer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @byte: byte to write + */ +#define DVB_RINGBUFFER_WRITE_BYTE(rbuf, byte) \ + { (rbuf)->data[(rbuf)->pwrite] = (byte); \ + (rbuf)->pwrite = ((rbuf)->pwrite + 1) % (rbuf)->size; } + +/** + * dvb_ringbuffer_write - Writes a buffer into the ringbuffer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @buf: pointer to the buffer where the data will be read + * @len: bytes from ring buffer into @buf + * + * This variant assumes that the buffer is a memory at the Kernel space + * + * return: number of bytes transferred or -EFAULT + */ extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len); -extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, - const u8 __user *buf, size_t len); +/** + * dvb_ringbuffer_write_user - Writes a buffer received via an user pointer + * + * @rbuf: pointer to struct dvb_ringbuffer + * @buf: pointer to the buffer where the data will be read + * @len: bytes from ring buffer into @buf + * + * This variant assumes that the buffer is a memory at the userspace. So, + * it will internally call copy_from_user(). + * + * Return: number of bytes transferred or -EFAULT + */ +extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, + const u8 __user *buf, size_t len); /** * dvb_ringbuffer_pkt_write - Write a packet into the ringbuffer. @@ -143,9 +213,10 @@ extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, * @rbuf: Ringbuffer to write to. * @buf: Buffer to write. * @len: Length of buffer (currently limited to 65535 bytes max). - * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. + * + * Return: Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. */ -extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, +extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len); /** @@ -157,7 +228,7 @@ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, * @buf: Destination buffer for data. * @len: Size of destination buffer. * - * returns Number of bytes read, or -EFAULT. + * Return: Number of bytes read, or -EFAULT. * * .. note:: * @@ -167,7 +238,7 @@ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, */ extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, - int offset, u8 __user *buf, + int offset, u8 __user *buf, size_t len); /** @@ -181,7 +252,7 @@ extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, * @buf: Destination buffer for data. * @len: Size of destination buffer. * - * returns Number of bytes read, or -EFAULT. + * Return: Number of bytes read, or -EFAULT. */ extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, int offset, u8 *buf, size_t len); @@ -199,10 +270,11 @@ extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx); * * @rbuf: Ringbuffer concerned. * @idx: Previous packet index, or -1 to return the first packet index. - * @pktlen: On success, will be updated to contain the length of the packet in bytes. + * @pktlen: On success, will be updated to contain the length of the packet + * in bytes. * returns Packet index (if >=0), or -1 if no packets available. */ -extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen); - +extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, + size_t idx, size_t *pktlen); #endif /* _DVB_RINGBUFFER_H_ */ diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index c645aa81f423..012225587c25 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -67,6 +67,7 @@ config DVB_TDA18271C2DD config DVB_SI2165 tristate "Silicon Labs si2165 based" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help A DVB-C/T demodulator. @@ -463,6 +464,7 @@ config DVB_STV0367 config DVB_CXD2820R tristate "Sony CXD2820R" depends on DVB_CORE && I2C + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index 8cc8c4597b6a..ad304eed656d 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -464,7 +464,7 @@ static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops ascot2e_tuner_ops = { +static const struct dvb_tuner_ops ascot2e_tuner_ops = { .info = { .name = "Sony ASCOT2E", .frequency_min = 1000000, diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h index 297a71a671f5..f3ff8f6eb3bb 100644 --- a/drivers/media/dvb-frontends/cxd2820r.h +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -36,6 +36,32 @@ #define CXD2820R_TS_PARALLEL 0x30 #define CXD2820R_TS_PARALLEL_MSB 0x70 +/* + * I2C address: 0x6c, 0x6d + */ + +/** + * struct cxd2820r_platform_data - Platform data for the cxd2820r driver + * @ts_mode: TS mode. + * @ts_clk_inv: TS clock inverted. + * @if_agc_polarity: IF AGC polarity. + * @spec_inv: Input spectrum inverted. + * @gpio_chip_base: GPIO. + * @get_dvb_frontend: Get DVB frontend. + */ + +struct cxd2820r_platform_data { + u8 ts_mode; + bool ts_clk_inv; + bool if_agc_polarity; + bool spec_inv; + int **gpio_chip_base; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); +/* private: For legacy media attach wrapper. Do not set value. */ + bool attach_in_use; +}; + struct cxd2820r_config { /* Demodulator I2C address. * Driver determines DVB-C slave I2C address automatically from master diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c index a674a6312c38..d75b0776d5b5 100644 --- a/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -24,12 +24,12 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i; + int ret; + unsigned int utmp; u8 buf[2]; - u32 if_freq; - u16 if_ctl; - u64 num; + u32 if_frequency; struct reg_val_mask tab[] = { { 0x00080, 0x01, 0xff }, { 0x00081, 0x05, 0xff }, @@ -43,25 +43,24 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) { 0x10059, 0x50, 0xff }, { 0x10087, 0x0c, 0x3c }, { 0x1008b, 0x07, 0xff }, - { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 }, - { 0x10070, priv->cfg.ts_mode, 0xff }, - { 0x10071, !priv->cfg.ts_clock_inv << 4, 0x10 }, + { 0x1001f, priv->if_agc_polarity << 7, 0x80 }, + { 0x10070, priv->ts_mode, 0xff }, + { 0x10071, !priv->ts_clk_inv << 4, 0x10 }, }; - dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__, - c->frequency, c->symbol_rate); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%u inversion=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->symbol_rate, c->inversion); /* program tuner */ if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); if (priv->delivery_system != SYS_DVBC_ANNEX_A) { - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, - tab[i].val, tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; } priv->delivery_system = SYS_DVBC_ANNEX_A; @@ -69,35 +68,33 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe) /* program IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) { - ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); if (ret) goto error; - } else - if_freq = 0; - - dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); - - num = if_freq / 1000; /* Hz => kHz */ - num *= 0x4000; - if_ctl = 0x4000 - DIV_ROUND_CLOSEST_ULL(num, 41000); - buf[0] = (if_ctl >> 8) & 0x3f; - buf[1] = (if_ctl >> 0) & 0xff; + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } - ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2); + utmp = 0x4000 - DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x4000, CXD2820R_CLK); + buf[0] = (utmp >> 8) & 0xff; + buf[1] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[1], 0x0042, buf, 2); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -105,20 +102,24 @@ int cxd2820r_get_frontend_c(struct dvb_frontend *fe, struct dtv_frontend_properties *c) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; int ret; + unsigned int utmp; u8 buf[2]; - ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2); + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[1], 0x001a, buf, 2); if (ret) goto error; c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]); - ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]); + ret = regmap_read(priv->regmap[1], 0x0019, &utmp); if (ret) goto error; - switch ((buf[0] >> 0) & 0x07) { + switch ((utmp >> 0) & 0x07) { case 0: c->modulation = QAM_16; break; @@ -136,7 +137,7 @@ int cxd2820r_get_frontend_c(struct dvb_frontend *fe, break; } - switch ((buf[0] >> 7) & 0x01) { + switch ((utmp >> 7) & 0x01) { case 0: c->inversion = INVERSION_OFF; break; @@ -147,167 +148,169 @@ int cxd2820r_get_frontend_c(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber) +int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - u8 buf[3], start_ber = 0; - *ber = 0; + unsigned int utmp, utmp1, utmp2; + u8 buf[3]; - if (priv->ber_running) { - ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf)); - if (ret) - goto error; + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[1], 0x0088, &buf[0], 1); + if (ret) + goto error; + ret = regmap_bulk_read(priv->regmap[1], 0x0073, &buf[1], 1); + if (ret) + goto error; - if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { - *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; - start_ber = 1; - } + utmp1 = (buf[0] >> 0) & 0x01; + utmp2 = (buf[1] >> 3) & 0x01; + + if (utmp1 == 1 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 1 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; } else { - priv->ber_running = true; - start_ber = 1; + *status = 0; } - if (start_ber) { - /* (re)start BER */ - ret = cxd2820r_wr_reg(priv, 0x10079, 0x01); - if (ret) - goto error; - } + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 2, buf, utmp1, utmp2); - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; -int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, - u16 *strength) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; - u16 tmp; + ret = regmap_bulk_read(priv->regmap[1], 0x0049, buf, 2); + if (ret) + goto error; - ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf)); - if (ret) - goto error; + utmp = buf[0] << 8 | buf[1] << 0; + utmp = 511 - sign_extend32(utmp, 9); + /* Scale value to 0x0000-0xffff */ + strength = utmp << 6 | utmp >> 4; - tmp = (buf[0] & 0x03) << 8 | buf[1]; - tmp = (~tmp & 0x03ff); + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - if (tmp == 512) - /* ~no signal */ - tmp = 0; - else if (tmp > 350) - tmp = 350; + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr, const_a, const_b; - /* scale value to 0x0000-0xffff */ - *strength = tmp * 0xffff / (350-0); + ret = regmap_read(priv->regmap[1], 0x0019, &utmp); + if (ret) + goto error; - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + if (((utmp >> 0) & 0x03) % 2) { + const_a = 8750; + const_b = 650; + } else { + const_a = 9500; + const_b = 760; + } -int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 tmp; - unsigned int A, B; - /* report SNR in dB * 10 */ + ret = regmap_read(priv->regmap[1], 0x004d, &utmp); + if (ret) + goto error; - ret = cxd2820r_rd_reg(priv, 0x10019, &tmp); - if (ret) - goto error; + #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ + if (utmp) + cnr = div_u64((u64)(intlog2(const_b) - intlog2(utmp)) + * const_a, CXD2820R_LOG2_E_24); + else + cnr = 0; - if (((tmp >> 0) & 0x03) % 2) { - A = 875; - B = 650; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; } else { - A = 950; - B = 760; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp); - if (ret) - goto error; - - #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ - if (tmp) - *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5) - / 10; - else - *snr = 0; - - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; + bool start_ber; -int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks) -{ - *ucblocks = 0; - /* no way to read ? */ - return 0; -} + if (priv->ber_running) { + ret = regmap_bulk_read(priv->regmap[1], 0x0076, buf, 3); + if (ret) + goto error; -int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; - *status = 0; + if ((buf[2] >> 7) & 0x01) { + post_bit_error = buf[2] << 16 | buf[1] << 8 | + buf[0] << 0; + post_bit_error &= 0x0fffff; + start_ber = true; + } else { + post_bit_error = 0; + start_ber = false; + } + } else { + post_bit_error = 0; + start_ber = true; + } - ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf)); - if (ret) - goto error; + if (start_ber) { + ret = regmap_write(priv->regmap[1], 0x0079, 0x01); + if (ret) + goto error; + priv->ber_running = true; + } - if (((buf[0] >> 0) & 0x01) == 1) { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC; + priv->post_bit_error += post_bit_error; - if (((buf[1] >> 3) & 0x01) == 1) { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - } + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - dev_dbg(&priv->i2c->dev, "%s: lock=%02x %02x\n", __func__, buf[0], - buf[1]); - return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } int cxd2820r_init_c(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; int ret; - ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); + dev_dbg(&client->dev, "\n"); + + ret = regmap_write(priv->regmap[0], 0x0085, 0x07); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } int cxd2820r_sleep_c(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret, i; + struct i2c_client *client = priv->client[0]; + int ret; struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, @@ -316,20 +319,17 @@ int cxd2820r_sleep_c(struct dvb_frontend *fe) { 0x00080, 0x00, 0xff }, }; - dev_dbg(&priv->i2c->dev, "%s\n", __func__); + dev_dbg(&client->dev, "\n"); priv->delivery_system = SYS_UNDEFINED; - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, - tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 314d3b8c1080..95267c6edb3a 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -21,178 +21,50 @@ #include "cxd2820r_priv.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - -/* write multiple registers */ -static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, - u8 *val, int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = i2c, - .flags = 0, - .len = len + 1, - .buf = buf, - } - }; - - if (1 + len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple registers */ -static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, - u8 *val, int len) -{ - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[2] = { - { - .addr = i2c, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = i2c, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write multiple registers */ -int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, - int len) +/* Write register table */ +int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv, + const struct reg_val_mask *tab, int tab_len) { + struct i2c_client *client = priv->client[0]; int ret; - u8 i2c_addr; - u8 reg = (reginfo >> 0) & 0xff; - u8 bank = (reginfo >> 8) & 0xff; - u8 i2c = (reginfo >> 16) & 0x01; - - /* select I2C */ - if (i2c) - i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ - else - i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ + unsigned int i, reg, mask, val; + struct regmap *regmap; - /* switch bank if needed */ - if (bank != priv->bank[i2c]) { - ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); - if (ret) - return ret; - priv->bank[i2c] = bank; - } - return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len); -} - -/* read multiple registers */ -int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, - int len) -{ - int ret; - u8 i2c_addr; - u8 reg = (reginfo >> 0) & 0xff; - u8 bank = (reginfo >> 8) & 0xff; - u8 i2c = (reginfo >> 16) & 0x01; - - /* select I2C */ - if (i2c) - i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ - else - i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ + dev_dbg(&client->dev, "tab_len=%d\n", tab_len); - /* switch bank if needed */ - if (bank != priv->bank[i2c]) { - ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); - if (ret) - return ret; - priv->bank[i2c] = bank; - } - return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len); -} - -/* write single register */ -int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val) -{ - return cxd2820r_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ -int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val) -{ - return cxd2820r_rd_regs(priv, reg, val, 1); -} + for (i = 0; i < tab_len; i++) { + if ((tab[i].reg >> 16) & 0x1) + regmap = priv->regmap[1]; + else + regmap = priv->regmap[0]; -/* write single register with mask */ -int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, - u8 mask) -{ - int ret; - u8 tmp; + reg = (tab[i].reg >> 0) & 0xffff; + val = tab[i].val; + mask = tab[i].mask; - /* no need for read if whole reg is written */ - if (mask != 0xff) { - ret = cxd2820r_rd_reg(priv, reg, &tmp); + if (mask == 0xff) + ret = regmap_write(regmap, reg, val); + else + ret = regmap_write_bits(regmap, reg, mask, val); if (ret) - return ret; - - val &= mask; - tmp &= ~mask; - val |= tmp; + goto error; } - return cxd2820r_wr_reg(priv, reg, val); + return 0; +error: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 tmp0, tmp1; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); /* update GPIOs only when needed */ if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio))) @@ -219,20 +91,18 @@ int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) else tmp1 |= (0 << (0 + i)); - dev_dbg(&priv->i2c->dev, "%s: gpio i=%d %02x %02x\n", __func__, - i, tmp0, tmp1); + dev_dbg(&client->dev, "gpio i=%d %02x %02x\n", i, tmp0, tmp1); } - dev_dbg(&priv->i2c->dev, "%s: wr gpio=%02x %02x\n", __func__, tmp0, - tmp1); + dev_dbg(&client->dev, "wr gpio=%02x %02x\n", tmp0, tmp1); /* write bits [7:2] */ - ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc); + ret = regmap_update_bits(priv->regmap[0], 0x0089, 0xfc, tmp0); if (ret) goto error; /* write bits [5:0] */ - ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f); + ret = regmap_update_bits(priv->regmap[0], 0x008e, 0x3f, tmp1); if (ret) goto error; @@ -240,18 +110,18 @@ int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int cxd2820r_set_frontend(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); switch (c->delivery_system) { case SYS_DVBT: @@ -279,8 +149,7 @@ static int cxd2820r_set_frontend(struct dvb_frontend *fe) goto err; break; default: - dev_dbg(&priv->i2c->dev, "%s: error state=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; break; } @@ -291,12 +160,13 @@ err: static int cxd2820r_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { + switch (c->delivery_system) { case SYS_DVBT: ret = cxd2820r_read_status_t(fe, status); break; @@ -317,15 +187,16 @@ static int cxd2820r_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *p) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); if (priv->delivery_system == SYS_UNDEFINED) return 0; - switch (fe->dtv_property_cache.delivery_system) { + switch (c->delivery_system) { case SYS_DVBT: ret = cxd2820r_get_frontend_t(fe, p); break; @@ -345,101 +216,60 @@ static int cxd2820r_get_frontend(struct dvb_frontend *fe, static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { - case SYS_DVBT: - ret = cxd2820r_read_ber_t(fe, ber); - break; - case SYS_DVBT2: - ret = cxd2820r_read_ber_t2(fe, ber); - break; - case SYS_DVBC_ANNEX_A: - ret = cxd2820r_read_ber_c(fe, ber); - break; - default: - ret = -EINVAL; - break; - } - return ret; + *ber = (priv->post_bit_error - priv->post_bit_error_prev_dvbv3); + priv->post_bit_error_prev_dvbv3 = priv->post_bit_error; + + return 0; } static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { - case SYS_DVBT: - ret = cxd2820r_read_signal_strength_t(fe, strength); - break; - case SYS_DVBT2: - ret = cxd2820r_read_signal_strength_t2(fe, strength); - break; - case SYS_DVBC_ANNEX_A: - ret = cxd2820r_read_signal_strength_c(fe, strength); - break; - default: - ret = -EINVAL; - break; - } - return ret; + if (c->strength.stat[0].scale == FE_SCALE_RELATIVE) + *strength = c->strength.stat[0].uvalue; + else + *strength = 0; + + return 0; } static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { - case SYS_DVBT: - ret = cxd2820r_read_snr_t(fe, snr); - break; - case SYS_DVBT2: - ret = cxd2820r_read_snr_t2(fe, snr); - break; - case SYS_DVBC_ANNEX_A: - ret = cxd2820r_read_snr_c(fe, snr); - break; - default: - ret = -EINVAL; - break; - } - return ret; + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; } static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { - case SYS_DVBT: - ret = cxd2820r_read_ucblocks_t(fe, ucblocks); - break; - case SYS_DVBT2: - ret = cxd2820r_read_ucblocks_t2(fe, ucblocks); - break; - case SYS_DVBC_ANNEX_A: - ret = cxd2820r_read_ucblocks_c(fe, ucblocks); - break; - default: - ret = -EINVAL; - break; - } - return ret; + *ucblocks = 0; + + return 0; } static int cxd2820r_init(struct dvb_frontend *fe) @@ -450,12 +280,13 @@ static int cxd2820r_init(struct dvb_frontend *fe) static int cxd2820r_sleep(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { + switch (c->delivery_system) { case SYS_DVBT: ret = cxd2820r_sleep_t(fe); break; @@ -476,12 +307,13 @@ static int cxd2820r_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); - switch (fe->dtv_property_cache.delivery_system) { + switch (c->delivery_system) { case SYS_DVBT: ret = cxd2820r_get_tune_settings_t(fe, s); break; @@ -501,12 +333,12 @@ static int cxd2820r_get_tune_settings(struct dvb_frontend *fe, static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; enum fe_status status = 0; - dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, - fe->dtv_property_cache.delivery_system); + dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system); /* switch between DVB-T and DVB-T2 when tune fails */ if (priv->last_tune_failed) { @@ -530,7 +362,6 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) if (ret) goto error; - /* frontend lock wait loop count */ switch (priv->delivery_system) { case SYS_DVBT: @@ -548,7 +379,7 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) /* wait frontend lock */ for (; i > 0; i--) { - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); msleep(50); ret = cxd2820r_read_status(fe, &status); if (ret) @@ -568,7 +399,7 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) } error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return DVBFE_ALGO_SEARCH_ERROR; } @@ -580,27 +411,23 @@ static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) static void cxd2820r_release(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; - dev_dbg(&priv->i2c->dev, "%s\n", __func__); + dev_dbg(&client->dev, "\n"); -#ifdef CONFIG_GPIOLIB - /* remove GPIOs */ - if (priv->gpio_chip.label) - gpiochip_remove(&priv->gpio_chip); + i2c_unregister_device(client); -#endif - kfree(priv); return; } static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; - dev_dbg(&priv->i2c->dev, "%s: %d\n", __func__, enable); + dev_dbg_ratelimited(&client->dev, "enable=%d\n", enable); - /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */ - return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1); + return regmap_update_bits(priv->regmap[0], 0x00db, 0x01, enable ? 1 : 0); } #ifdef CONFIG_GPIOLIB @@ -608,9 +435,10 @@ static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr, int val) { struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; u8 gpio[GPIO_COUNT]; - dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); + dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val); memcpy(gpio, priv->gpio, sizeof(gpio)); gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); @@ -621,9 +449,10 @@ static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr, static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val) { struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; u8 gpio[GPIO_COUNT]; - dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); + dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val); memcpy(gpio, priv->gpio, sizeof(gpio)); gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); @@ -636,8 +465,9 @@ static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val) static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr) { struct cxd2820r_priv *priv = gpiochip_get_data(chip); + struct i2c_client *client = priv->client[0]; - dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr); + dev_dbg(&client->dev, "nr=%u\n", nr); return (priv->gpio[nr] >> 2) & 0x01; } @@ -689,52 +519,163 @@ static const struct dvb_frontend_ops cxd2820r_ops = { .read_signal_strength = cxd2820r_read_signal_strength, }; -struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, - struct i2c_adapter *i2c, int *gpio_chip_base -) +/* + * XXX: That is wrapper to cxd2820r_probe() via driver core in order to provide + * proper I2C client for legacy media attach binding. + * New users must use I2C client binding directly! + */ +struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *config, + struct i2c_adapter *adapter, + int *gpio_chip_base) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + struct cxd2820r_platform_data pdata; + + pdata.ts_mode = config->ts_mode; + pdata.ts_clk_inv = config->ts_clock_inv; + pdata.if_agc_polarity = config->if_agc_polarity; + pdata.spec_inv = config->spec_inv; + pdata.gpio_chip_base = &gpio_chip_base; + pdata.attach_in_use = true; + + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "cxd2820r", I2C_NAME_SIZE); + board_info.addr = config->i2c_address; + board_info.platform_data = &pdata; + client = i2c_new_device(adapter, &board_info); + if (!client || !client->dev.driver) + return NULL; + + return pdata.get_dvb_frontend(client); +} +EXPORT_SYMBOL(cxd2820r_attach); + +static struct dvb_frontend *cxd2820r_get_dvb_frontend(struct i2c_client *client) +{ + struct cxd2820r_priv *priv = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &priv->fe; +} + +static int cxd2820r_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct cxd2820r_platform_data *pdata = client->dev.platform_data; struct cxd2820r_priv *priv; - int ret; - u8 tmp; + int ret, *gpio_chip_base; + unsigned int utmp; + static const struct regmap_range_cfg regmap_range_cfg0[] = { + { + .range_min = 0x0000, + .range_max = 0x3fff, + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, + }; + static const struct regmap_range_cfg regmap_range_cfg1[] = { + { + .range_min = 0x0000, + .range_max = 0x01ff, + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, + }; + static const struct regmap_config regmap_config0 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3fff, + .ranges = regmap_range_cfg0, + .num_ranges = ARRAY_SIZE(regmap_range_cfg0), + .cache_type = REGCACHE_NONE, + }; + static const struct regmap_config regmap_config1 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x01ff, + .ranges = regmap_range_cfg1, + .num_ranges = ARRAY_SIZE(regmap_range_cfg1), + .cache_type = REGCACHE_NONE, + }; + + dev_dbg(&client->dev, "\n"); - priv = kzalloc(sizeof(struct cxd2820r_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; - dev_err(&i2c->dev, "%s: kzalloc() failed\n", - KBUILD_MODNAME); - goto error; + goto err; } - priv->i2c = i2c; - memcpy(&priv->cfg, cfg, sizeof(struct cxd2820r_config)); - memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; + priv->client[0] = client; + priv->i2c = client->adapter; + priv->ts_mode = pdata->ts_mode; + priv->ts_clk_inv = pdata->ts_clk_inv; + priv->if_agc_polarity = pdata->if_agc_polarity; + priv->spec_inv = pdata->spec_inv; + gpio_chip_base = *pdata->gpio_chip_base; + priv->regmap[0] = regmap_init_i2c(priv->client[0], ®map_config0); + if (IS_ERR(priv->regmap[0])) { + ret = PTR_ERR(priv->regmap[0]); + goto err_kfree; + } - priv->bank[0] = priv->bank[1] = 0xff; - ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp); - dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, tmp); - if (ret || tmp != 0xe1) - goto error; + /* Check demod answers with correct chip id */ + ret = regmap_read(priv->regmap[0], 0x00fd, &utmp); + if (ret) + goto err_regmap_0_regmap_exit; + + dev_dbg(&client->dev, "chip_id=%02x\n", utmp); + + if (utmp != 0xe1) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + + /* + * Chip has two I2C addresses for different register banks. We register + * one dummy I2C client in in order to get own I2C client for each + * register bank. + */ + priv->client[1] = i2c_new_dummy(client->adapter, client->addr | (1 << 1)); + if (!priv->client[1]) { + ret = -ENODEV; + dev_err(&client->dev, "I2C registration failed\n"); + if (ret) + goto err_regmap_0_regmap_exit; + } + + priv->regmap[1] = regmap_init_i2c(priv->client[1], ®map_config1); + if (IS_ERR(priv->regmap[1])) { + ret = PTR_ERR(priv->regmap[1]); + goto err_client_1_i2c_unregister_device; + } if (gpio_chip_base) { #ifdef CONFIG_GPIOLIB - /* add GPIOs */ + /* Add GPIOs */ priv->gpio_chip.label = KBUILD_MODNAME; - priv->gpio_chip.parent = &priv->i2c->dev; + priv->gpio_chip.parent = &client->dev; priv->gpio_chip.owner = THIS_MODULE; - priv->gpio_chip.direction_output = - cxd2820r_gpio_direction_output; + priv->gpio_chip.direction_output = cxd2820r_gpio_direction_output; priv->gpio_chip.set = cxd2820r_gpio_set; priv->gpio_chip.get = cxd2820r_gpio_get; - priv->gpio_chip.base = -1; /* dynamic allocation */ + priv->gpio_chip.base = -1; /* Dynamic allocation */ priv->gpio_chip.ngpio = GPIO_COUNT; priv->gpio_chip.can_sleep = 1; ret = gpiochip_add_data(&priv->gpio_chip, priv); if (ret) - goto error; + goto err_regmap_1_regmap_exit; - dev_dbg(&priv->i2c->dev, "%s: gpio_chip.base=%d\n", __func__, - priv->gpio_chip.base); + dev_dbg(&client->dev, "gpio_chip.base=%d\n", + priv->gpio_chip.base); *gpio_chip_base = priv->gpio_chip.base; #else @@ -748,17 +689,73 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, gpio[2] = 0; ret = cxd2820r_gpio(&priv->fe, gpio); if (ret) - goto error; + goto err_regmap_1_regmap_exit; #endif } - return &priv->fe; -error: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); + /* Create dvb frontend */ + memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(priv->fe.ops)); + if (!pdata->attach_in_use) + priv->fe.ops.release = NULL; + priv->fe.demodulator_priv = priv; + i2c_set_clientdata(client, priv); + + /* Setup callbacks */ + pdata->get_dvb_frontend = cxd2820r_get_dvb_frontend; + + dev_info(&client->dev, "Sony CXD2820R successfully identified\n"); + + return 0; +err_regmap_1_regmap_exit: + regmap_exit(priv->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(priv->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(priv->regmap[0]); +err_kfree: kfree(priv); - return NULL; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(cxd2820r_attach); + +static int cxd2820r_remove(struct i2c_client *client) +{ + struct cxd2820r_priv *priv = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + +#ifdef CONFIG_GPIOLIB + if (priv->gpio_chip.label) + gpiochip_remove(&priv->gpio_chip); +#endif + regmap_exit(priv->regmap[1]); + i2c_unregister_device(priv->client[1]); + + regmap_exit(priv->regmap[0]); + + kfree(priv); + + return 0; +} + +static const struct i2c_device_id cxd2820r_id_table[] = { + {"cxd2820r", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); + +static struct i2c_driver cxd2820r_driver = { + .driver = { + .name = "cxd2820r", + .suppress_bind_attrs = true, + }, + .probe = cxd2820r_probe, + .remove = cxd2820r_remove, + .id_table = cxd2820r_id_table, +}; + +module_i2c_driver(cxd2820r_driver); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Sony CXD2820R demodulator driver"); diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h index e31c48e53097..0d096206ac66 100644 --- a/drivers/media/dvb-frontends/cxd2820r_priv.h +++ b/drivers/media/dvb-frontends/cxd2820r_priv.h @@ -27,6 +27,8 @@ #include "dvb_math.h" #include "cxd2820r.h" #include <linux/gpio.h> +#include <linux/math64.h> +#include <linux/regmap.h> struct reg_val_mask { u32 reg; @@ -34,14 +36,23 @@ struct reg_val_mask { u8 mask; }; +#define CXD2820R_CLK 41000000 + struct cxd2820r_priv { + struct i2c_client *client[2]; + struct regmap *regmap[2]; struct i2c_adapter *i2c; struct dvb_frontend fe; - struct cxd2820r_config cfg; + u8 ts_mode; + bool ts_clk_inv; + bool if_agc_polarity; + bool spec_inv; + + u64 post_bit_error_prev_dvbv3; + u64 post_bit_error; bool ber_running; - u8 bank[2]; #define GPIO_COUNT 3 u8 gpio[GPIO_COUNT]; #ifdef CONFIG_GPIOLIB @@ -58,6 +69,9 @@ extern int cxd2820r_debug; int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio); +int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv, + const struct reg_val_mask *tab, int tab_len); + int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, u8 mask); @@ -83,14 +97,6 @@ int cxd2820r_set_frontend_c(struct dvb_frontend *fe); int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status); -int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber); - -int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength); - -int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr); - -int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks); - int cxd2820r_init_c(struct dvb_frontend *fe); int cxd2820r_sleep_c(struct dvb_frontend *fe); @@ -107,14 +113,6 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe); int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status); -int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber); - -int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength); - -int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr); - -int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks); - int cxd2820r_init_t(struct dvb_frontend *fe); int cxd2820r_sleep_t(struct dvb_frontend *fe); @@ -131,14 +129,6 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe); int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status); -int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber); - -int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength); - -int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr); - -int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks); - int cxd2820r_init_t2(struct dvb_frontend *fe); int cxd2820r_sleep_t2(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index 75ce7d8ded00..c2e7caf9b010 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -24,10 +24,11 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, i, bw_i; - u32 if_freq, if_ctl; - u64 num; + int ret, bw_i; + unsigned int utmp; + u32 if_frequency; u8 buf[3], bw_param; u8 bw_params1[][5] = { { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ @@ -45,9 +46,9 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) { 0x00085, 0x07, 0xff }, { 0x00088, 0x01, 0xff }, - { 0x00070, priv->cfg.ts_mode, 0xff }, - { 0x00071, !priv->cfg.ts_clock_inv << 4, 0x10 }, - { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, + { 0x00070, priv->ts_mode, 0xff }, + { 0x00071, !priv->ts_clk_inv << 4, 0x10 }, + { 0x000cb, priv->if_agc_polarity << 6, 0x40 }, { 0x000a5, 0x00, 0x01 }, { 0x00082, 0x20, 0x60 }, { 0x000c2, 0xc3, 0xff }, @@ -55,8 +56,10 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) { 0x00427, 0x41, 0xff }, }; - dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__, - c->frequency, c->bandwidth_hz); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->inversion); switch (c->bandwidth_hz) { case 6000000: @@ -80,12 +83,9 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) fe->ops.tuner_ops.set_params(fe); if (priv->delivery_system != SYS_DVBT) { - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, - tab[i].val, tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; } priv->delivery_system = SYS_DVBT; @@ -93,48 +93,46 @@ int cxd2820r_set_frontend_t(struct dvb_frontend *fe) /* program IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) { - ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); if (ret) goto error; - } else - if_freq = 0; - - dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); - - num = if_freq / 1000; /* Hz => kHz */ - num *= 0x1000000; - if_ctl = DIV_ROUND_CLOSEST_ULL(num, 41000); - buf[0] = ((if_ctl >> 16) & 0xff); - buf[1] = ((if_ctl >> 8) & 0xff); - buf[2] = ((if_ctl >> 0) & 0xff); + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } - ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3); + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[0], 0x00b6, buf, 3); if (ret) goto error; - ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[bw_i], 5); + ret = regmap_bulk_write(priv->regmap[0], 0x009f, bw_params1[bw_i], 5); if (ret) goto error; - ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0); + ret = regmap_update_bits(priv->regmap[0], 0x00d7, 0xc0, bw_param << 6); if (ret) goto error; - ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[bw_i], 2); + ret = regmap_bulk_write(priv->regmap[0], 0x00d9, bw_params2[bw_i], 2); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -142,10 +140,14 @@ int cxd2820r_get_frontend_t(struct dvb_frontend *fe, struct dtv_frontend_properties *c) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; int ret; + unsigned int utmp; u8 buf[2]; - ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf)); + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[0], 0x002f, buf, sizeof(buf)); if (ret) goto error; @@ -236,11 +238,11 @@ int cxd2820r_get_frontend_t(struct dvb_frontend *fe, break; } - ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]); + ret = regmap_read(priv->regmap[0], 0x07c6, &utmp); if (ret) goto error; - switch ((buf[0] >> 0) & 0x01) { + switch ((utmp >> 0) & 0x01) { case 0: c->inversion = INVERSION_OFF; break; @@ -251,169 +253,158 @@ int cxd2820r_get_frontend_t(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} - -int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[3], start_ber = 0; - *ber = 0; - - if (priv->ber_running) { - ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf)); - if (ret) - goto error; - - if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { - *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; - start_ber = 1; - } - } else { - priv->ber_running = true; - start_ber = 1; - } - - if (start_ber) { - /* (re)start BER */ - ret = cxd2820r_wr_reg(priv, 0x00079, 0x01); - if (ret) - goto error; - } - - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, - u16 *strength) +int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - u8 buf[2]; - u16 tmp; + unsigned int utmp, utmp1, utmp2; + u8 buf[3]; - ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf)); + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[0], 0x0010, &buf[0], 1); + if (ret) + goto error; + ret = regmap_bulk_read(priv->regmap[0], 0x0073, &buf[1], 1); if (ret) goto error; - tmp = (buf[0] & 0x0f) << 8 | buf[1]; - tmp = ~tmp & 0x0fff; + utmp1 = (buf[0] >> 0) & 0x07; + utmp2 = (buf[1] >> 3) & 0x01; - /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ - *strength = tmp * 0xffff / 0x0fff; + if (utmp1 == 6 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 6 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; + } else { + *status = 0; + } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 2, buf, utmp1, utmp2); -int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; - u16 tmp; - /* report SNR in dB * 10 */ + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; - ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf)); - if (ret) - goto error; + ret = regmap_bulk_read(priv->regmap[0], 0x0026, buf, 2); + if (ret) + goto error; - tmp = (buf[0] & 0x1f) << 8 | buf[1]; - #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ - if (tmp) - *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) - / 100); - else - *snr = 0; + utmp = buf[0] << 8 | buf[1] << 0; + utmp = ~utmp & 0x0fff; + /* Scale value to 0x0000-0xffff */ + strength = utmp << 4 | utmp >> 8; - dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr, - tmp); + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr; -int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks) -{ - *ucblocks = 0; - /* no way to read ? */ - return 0; -} + ret = regmap_bulk_read(priv->regmap[0], 0x002c, buf, 2); + if (ret) + goto error; -int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[4]; - *status = 0; + utmp = buf[0] << 8 | buf[1] << 0; + if (utmp) + cnr = div_u64((u64)(intlog10(utmp) + - intlog10(32000 - utmp) + 55532585) + * 10000, (1 << 24)); + else + cnr = 0; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]); - if (ret) - goto error; + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; + bool start_ber; - if ((buf[0] & 0x07) == 6) { - ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]); - if (ret) - goto error; + if (priv->ber_running) { + ret = regmap_bulk_read(priv->regmap[0], 0x0076, buf, 3); + if (ret) + goto error; - if (((buf[1] >> 3) & 0x01) == 1) { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if ((buf[2] >> 7) & 0x01) { + post_bit_error = buf[2] << 16 | buf[1] << 8 | + buf[0] << 0; + post_bit_error &= 0x0fffff; + start_ber = true; + } else { + post_bit_error = 0; + start_ber = false; + } } else { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC; + post_bit_error = 0; + start_ber = true; } - } else { - ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]); - if (ret) - goto error; - if ((buf[2] & 0x0f) >= 4) { - ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]); + if (start_ber) { + ret = regmap_write(priv->regmap[0], 0x0079, 0x01); if (ret) goto error; - - if (((buf[3] >> 4) & 0x01) == 1) - *status |= FE_HAS_SIGNAL; + priv->ber_running = true; } - } - dev_dbg(&priv->i2c->dev, "%s: lock=%*ph\n", __func__, 4, buf); + priv->post_bit_error += post_bit_error; + + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } int cxd2820r_init_t(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; int ret; - ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); + dev_dbg(&client->dev, "\n"); + + ret = regmap_write(priv->regmap[0], 0x0085, 0x07); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } int cxd2820r_sleep_t(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret, i; + struct i2c_client *client = priv->client[0]; + int ret; struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, @@ -422,20 +413,17 @@ int cxd2820r_sleep_t(struct dvb_frontend *fe) { 0x00080, 0x00, 0xff }, }; - dev_dbg(&priv->i2c->dev, "%s\n", __func__); + dev_dbg(&client->dev, "\n"); priv->delivery_system = SYS_UNDEFINED; - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, - tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index 704475676234..e641fde75379 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -23,11 +23,12 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) { - struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret, i, bw_i; - u32 if_freq, if_ctl; - u64 num; + struct i2c_client *client = priv->client[0]; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, bw_i; + unsigned int utmp; + u32 if_frequency; u8 buf[3], bw_param; u8 bw_params1[][5] = { { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */ @@ -45,10 +46,10 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) { 0x0207f, 0x2a, 0xff }, { 0x02082, 0x0a, 0xff }, { 0x02083, 0x0a, 0xff }, - { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 }, - { 0x02070, priv->cfg.ts_mode, 0xff }, - { 0x02071, !priv->cfg.ts_clock_inv << 6, 0x40 }, - { 0x020b5, priv->cfg.spec_inv << 4, 0x10 }, + { 0x020cb, priv->if_agc_polarity << 6, 0x40 }, + { 0x02070, priv->ts_mode, 0xff }, + { 0x02071, !priv->ts_clk_inv << 6, 0x40 }, + { 0x020b5, priv->spec_inv << 4, 0x10 }, { 0x02567, 0x07, 0x0f }, { 0x02569, 0x03, 0x03 }, { 0x02595, 0x1a, 0xff }, @@ -69,8 +70,10 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) { 0x027ef, 0x10, 0x18 }, }; - dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__, - c->frequency, c->bandwidth_hz); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d stream_id=%u\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->inversion, c->stream_id); switch (c->bandwidth_hz) { case 5000000: @@ -98,73 +101,67 @@ int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) fe->ops.tuner_ops.set_params(fe); if (priv->delivery_system != SYS_DVBT2) { - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, - tab[i].val, tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; } priv->delivery_system = SYS_DVBT2; /* program IF frequency */ if (fe->ops.tuner_ops.get_if_frequency) { - ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); if (ret) goto error; - } else - if_freq = 0; - - dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); + dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto error; + } - num = if_freq / 1000; /* Hz => kHz */ - num *= 0x1000000; - if_ctl = DIV_ROUND_CLOSEST_ULL(num, 41000); - buf[0] = ((if_ctl >> 16) & 0xff); - buf[1] = ((if_ctl >> 8) & 0xff); - buf[2] = ((if_ctl >> 0) & 0xff); + utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK); + buf[0] = (utmp >> 16) & 0xff; + buf[1] = (utmp >> 8) & 0xff; + buf[2] = (utmp >> 0) & 0xff; + ret = regmap_bulk_write(priv->regmap[0], 0x20b6, buf, 3); + if (ret) + goto error; /* PLP filtering */ if (c->stream_id > 255) { - dev_dbg(&priv->i2c->dev, "%s: Disable PLP filtering\n", __func__); - ret = cxd2820r_wr_reg(priv, 0x023ad , 0); + dev_dbg(&client->dev, "disable PLP filtering\n"); + ret = regmap_write(priv->regmap[0], 0x23ad, 0x00); if (ret) goto error; } else { - dev_dbg(&priv->i2c->dev, "%s: Enable PLP filtering = %d\n", __func__, - c->stream_id); - ret = cxd2820r_wr_reg(priv, 0x023af , c->stream_id & 0xFF); + dev_dbg(&client->dev, "enable PLP filtering\n"); + ret = regmap_write(priv->regmap[0], 0x23af, c->stream_id & 0xff); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x023ad , 1); + ret = regmap_write(priv->regmap[0], 0x23ad, 0x01); if (ret) goto error; } - ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3); + ret = regmap_bulk_write(priv->regmap[0], 0x209f, bw_params1[bw_i], 5); if (ret) goto error; - ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[bw_i], 5); + ret = regmap_update_bits(priv->regmap[0], 0x20d7, 0xc0, bw_param << 6); if (ret) goto error; - ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0); + ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); if (ret) goto error; - ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); - if (ret) - goto error; - - ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); + ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); if (ret) goto error; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -173,10 +170,14 @@ int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, struct dtv_frontend_properties *c) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client[0]; int ret; + unsigned int utmp; u8 buf[2]; - ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2); + dev_dbg(&client->dev, "\n"); + + ret = regmap_bulk_read(priv->regmap[0], 0x205c, buf, 2); if (ret) goto error; @@ -225,7 +226,7 @@ int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, break; } - ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2); + ret = regmap_bulk_read(priv->regmap[0], 0x225b, buf, 2); if (ret) goto error; @@ -265,11 +266,11 @@ int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, break; } - ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]); + ret = regmap_read(priv->regmap[0], 0x20b5, &utmp); if (ret) goto error; - switch ((buf[0] >> 4) & 0x01) { + switch ((utmp >> 4) & 0x01) { case 0: c->inversion = INVERSION_OFF; break; @@ -280,130 +281,124 @@ int cxd2820r_get_frontend_t2(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status) { struct cxd2820r_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = priv->client[0]; int ret; - u8 buf[1]; - *status = 0; + unsigned int utmp, utmp1, utmp2; + u8 buf[4]; - ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]); + /* Lock detection */ + ret = regmap_bulk_read(priv->regmap[0], 0x2010, &buf[0], 1); if (ret) goto error; - if ((buf[0] & 0x07) == 6) { - if (((buf[0] >> 5) & 0x01) == 1) { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - } else { - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC; - } - } + utmp1 = (buf[0] >> 0) & 0x07; + utmp2 = (buf[0] >> 5) & 0x01; - dev_dbg(&priv->i2c->dev, "%s: lock=%02x\n", __func__, buf[0]); + if (utmp1 == 6 && utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else if (utmp1 == 6 || utmp2 == 1) { + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC; + } else { + *status = 0; + } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", + *status, 1, buf, utmp1, utmp2); -int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[4]; - unsigned int errbits; - *ber = 0; - /* FIXME: correct calculation */ + /* Signal strength */ + if (*status & FE_HAS_SIGNAL) { + unsigned int strength; - ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf)); - if (ret) - goto error; + ret = regmap_bulk_read(priv->regmap[0], 0x2026, buf, 2); + if (ret) + goto error; - if ((buf[0] >> 4) & 0x01) { - errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 | - buf[2] << 8 | buf[3]; + utmp = buf[0] << 8 | buf[1] << 0; + utmp = ~utmp & 0x0fff; + /* Scale value to 0x0000-0xffff */ + strength = utmp << 4 | utmp >> 8; - if (errbits) - *ber = errbits * 64 / 16588800; + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = strength; + } else { + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* CNR */ + if (*status & FE_HAS_VITERBI) { + unsigned int cnr; -int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, - u16 *strength) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; - u16 tmp; - - ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf)); - if (ret) - goto error; - - tmp = (buf[0] & 0x0f) << 8 | buf[1]; - tmp = ~tmp & 0x0fff; + ret = regmap_bulk_read(priv->regmap[0], 0x2028, buf, 2); + if (ret) + goto error; - /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ - *strength = tmp * 0xffff / 0x0fff; + utmp = buf[0] << 8 | buf[1] << 0; + utmp = utmp & 0x0fff; + #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ + if (utmp) + cnr = div_u64((u64)(intlog10(utmp) + - CXD2820R_LOG10_8_24) * 10000, + (1 << 24)); + else + cnr = 0; + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + } else { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - return ret; -error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; -} + /* BER */ + if (*status & FE_HAS_SYNC) { + unsigned int post_bit_error; -int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr) -{ - struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret; - u8 buf[2]; - u16 tmp; - /* report SNR in dB * 10 */ + ret = regmap_bulk_read(priv->regmap[0], 0x2039, buf, 4); + if (ret) + goto error; - ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf)); - if (ret) - goto error; + if ((buf[0] >> 4) & 0x01) { + post_bit_error = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + post_bit_error &= 0x0fffffff; + } else { + post_bit_error = 0; + } - tmp = (buf[0] & 0x0f) << 8 | buf[1]; - #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ - if (tmp) - *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) - / 100); - else - *snr = 0; + priv->post_bit_error += post_bit_error; - dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr, - tmp); + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = priv->post_bit_error; + } else { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks) -{ - *ucblocks = 0; - /* no way to read ? */ - return 0; -} - int cxd2820r_sleep_t2(struct dvb_frontend *fe) { struct cxd2820r_priv *priv = fe->demodulator_priv; - int ret, i; + struct i2c_client *client = priv->client[0]; + int ret; struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, @@ -413,20 +408,17 @@ int cxd2820r_sleep_t2(struct dvb_frontend *fe) { 0x00080, 0x00, 0xff }, }; - dev_dbg(&priv->i2c->dev, "%s\n", __func__); + dev_dbg(&client->dev, "\n"); - for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, - tab[i].mask); - if (ret) - goto error; - } + ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); + if (ret) + goto error; priv->delivery_system = SYS_UNDEFINED; return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index ffe88bc6b813..5afb9c508f65 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -206,6 +206,9 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = { (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \ (u32)(((iffreq)/41.0)*16777216.0 + 0.5)) +static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv); +static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv); + static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, u8 addr, u8 reg, u8 write, const u8 *data, u32 len) @@ -1401,6 +1404,41 @@ static int cxd2841er_read_ber_c(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_read_ber_i(struct cxd2841er_priv *priv, + u32 *bit_error, u32 *bit_count) +{ + u8 data[3]; + u8 pktnum[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + + cxd2841er_freeze_regs(priv); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); + cxd2841er_read_regs(priv, I2C_SLVT, 0x5B, pktnum, sizeof(pktnum)); + cxd2841er_read_regs(priv, I2C_SLVT, 0x16, data, sizeof(data)); + + if (!pktnum[0] && !pktnum[1]) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + cxd2841er_unfreeze_regs(priv); + return -EINVAL; + } + + *bit_error = ((u32)(data[0] & 0x7F) << 16) | + ((u32)data[1] << 8) | data[2]; + *bit_count = ((((u32)pktnum[0] << 8) | pktnum[1]) * 204 * 8); + dev_dbg(&priv->i2c->dev, "%s(): bit_error=%u bit_count=%u\n", + __func__, *bit_error, *bit_count); + + cxd2841er_unfreeze_regs(priv); + return 0; +} + static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, u32 *bit_error, u32 *bit_count) { @@ -1570,6 +1608,25 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, return 0; } +static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv) +{ + /* + * Freeze registers: ensure multiple separate register reads + * are from the same snapshot + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); + return 0; +} + +static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv) +{ + /* + * un-freeze registers + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x00); + return 0; +} + static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys, u32 *snr) { @@ -1578,6 +1635,7 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, int min_index, max_index, index; static const struct cxd2841er_cnr_data *cn_data; + cxd2841er_freeze_regs(priv); /* Set SLV-T Bank : 0xA1 */ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1); /* @@ -1629,9 +1687,11 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, } else { dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); + cxd2841er_unfreeze_regs(priv); return -EINVAL; } done: + cxd2841er_unfreeze_regs(priv); *snr = res; return 0; } @@ -1655,12 +1715,7 @@ static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr) return -EINVAL; } - /* - * Freeze registers: ensure multiple separate register reads - * are from the same snapshot - */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); - + cxd2841er_freeze_regs(priv); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1); qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07); @@ -1670,6 +1725,7 @@ static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr) if (reg == 0) { dev_dbg(&priv->i2c->dev, "%s(): reg value out of range\n", __func__); + cxd2841er_unfreeze_regs(priv); return 0; } @@ -1690,9 +1746,11 @@ static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr) *snr = -88 * (int32_t)sony_log(reg) + 86999; break; default: + cxd2841er_unfreeze_regs(priv); return -EINVAL; } + cxd2841er_unfreeze_regs(priv); return 0; } @@ -1707,17 +1765,21 @@ static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) "%s(): invalid state %d\n", __func__, priv->state); return -EINVAL; } + + cxd2841er_freeze_regs(priv); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); reg = ((u32)data[0] << 8) | (u32)data[1]; if (reg == 0) { dev_dbg(&priv->i2c->dev, "%s(): reg value out of range\n", __func__); + cxd2841er_unfreeze_regs(priv); return 0; } if (reg > 4996) reg = 4996; *snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500; + cxd2841er_unfreeze_regs(priv); return 0; } @@ -1732,18 +1794,22 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) "%s(): invalid state %d\n", __func__, priv->state); return -EINVAL; } + + cxd2841er_freeze_regs(priv); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); reg = ((u32)data[0] << 8) | (u32)data[1]; if (reg == 0) { dev_dbg(&priv->i2c->dev, "%s(): reg value out of range\n", __func__); + cxd2841er_unfreeze_regs(priv); return 0; } if (reg > 10876) reg = 10876; *snr = 10000 * ((intlog10(reg) - intlog10(12600 - reg)) >> 24) + 32000; + cxd2841er_unfreeze_regs(priv); return 0; } @@ -1760,21 +1826,18 @@ static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr) return -EINVAL; } - /* Freeze all registers */ - cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01); - - + cxd2841er_freeze_regs(priv); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60); cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); reg = ((u32)data[0] << 8) | (u32)data[1]; if (reg == 0) { dev_dbg(&priv->i2c->dev, "%s(): reg value out of range\n", __func__); + cxd2841er_unfreeze_regs(priv); return 0; } - if (reg > 4996) - reg = 4996; - *snr = 100 * intlog10(reg) - 9031; + *snr = 10000 * (intlog10(reg) >> 24) - 9031; + cxd2841er_unfreeze_regs(priv); return 0; } @@ -1852,6 +1915,9 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe) case SYS_DVBC_ANNEX_C: ret = cxd2841er_read_ber_c(priv, &bit_error, &bit_count); break; + case SYS_ISDBT: + ret = cxd2841er_read_ber_i(priv, &bit_error, &bit_count); + break; case SYS_DVBS: ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count); break; @@ -1965,6 +2031,9 @@ static void cxd2841er_read_snr(struct dvb_frontend *fe) return; } + dev_dbg(&priv->i2c->dev, "%s(): snr=%d\n", + __func__, (int32_t)tmp); + if (!ret) { p->cnr.stat[0].scale = FE_SCALE_DECIBEL; p->cnr.stat[0].svalue = tmp; @@ -1977,7 +2046,7 @@ static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; - u32 ucblocks; + u32 ucblocks = 0; dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { @@ -1999,7 +2068,7 @@ static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; return; } - dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + dev_dbg(&priv->i2c->dev, "%s() ucblocks=%u\n", __func__, ucblocks); p->block_error.stat[0].scale = FE_SCALE_COUNTER; p->block_error.stat[0].uvalue = ucblocks; @@ -2694,6 +2763,14 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, u8 b10_b6[3]; u32 iffreq; + if (bandwidth != 6000000 && + bandwidth != 7000000 && + bandwidth != 8000000) { + dev_info(&priv->i2c->dev, "%s(): unsupported bandwidth %d. Forcing 8Mhz!\n", + __func__, bandwidth); + bandwidth = 8000000; + } + dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); switch (bandwidth) { @@ -3076,6 +3153,7 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, /* Enable demod clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00); cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); /* Enable ADC clock */ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index b975da099929..c595adc61c6f 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -6448,7 +6448,7 @@ static int get_strength(struct drxk_state *state, u64 *strength) return status; /* SCU c.o.c. */ - read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc); + status = read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc); if (status < 0) return status; diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index 53089e142715..735a96662022 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -739,7 +739,7 @@ static int dvb_pll_init(struct dvb_frontend *fe) return -EINVAL; } -static struct dvb_tuner_ops dvb_pll_tuner_ops = { +static const struct dvb_tuner_ops dvb_pll_tuner_ops = { .release = dvb_pll_release, .sleep = dvb_pll_sleep, .init = dvb_pll_init, diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 97a8982740a6..dc43c5f6d0ea 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -842,7 +842,7 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops helene_tuner_ops = { +static const struct dvb_tuner_ops helene_tuner_ops = { .info = { .name = "Sony HELENE Ter tuner", .frequency_min = 1000000, @@ -856,7 +856,7 @@ static struct dvb_tuner_ops helene_tuner_ops = { .get_frequency = helene_get_frequency, }; -static struct dvb_tuner_ops helene_tuner_ops_s = { +static const struct dvb_tuner_ops helene_tuner_ops_s = { .info = { .name = "Sony HELENE Sat tuner", .frequency_min = 500000, @@ -987,8 +987,10 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (helene_x_pon(priv) != 0) + if (helene_x_pon(priv) != 0) { + kfree(priv); return NULL; + } if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -1021,8 +1023,10 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (helene_x_pon(priv) != 0) + if (helene_x_pon(priv) != 0) { + kfree(priv); return NULL; + } if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index a98bca5270d9..0c089b5986a1 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -326,7 +326,7 @@ static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops horus3a_tuner_ops = { +static const struct dvb_tuner_ops horus3a_tuner_ops = { .info = { .name = "Sony Horus3a", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index 0e3387e00952..2826bbb36b73 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -258,7 +258,7 @@ static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops ix2505v_tuner_ops = { +static const struct dvb_tuner_ops ix2505v_tuner_ops = { .info = { .name = "Sharp IX2505V (B0017)", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 179c26e5eb4e..0ca4e810e9d8 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -731,7 +731,7 @@ static int lgdt3306a_set_if(struct lgdt3306a_state *state, switch (if_freq_khz) { default: - pr_warn("IF=%d KHz is not supportted, 3250 assumed\n", + pr_warn("IF=%d KHz is not supported, 3250 assumed\n", if_freq_khz); /* fallthrough */ case 3250: /* 3.25Mhz */ @@ -1737,24 +1737,16 @@ static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe, static int lgdt3306a_search(struct dvb_frontend *fe) { enum fe_status status = 0; - int i, ret; + int ret; /* set frontend */ ret = lgdt3306a_set_parameters(fe); if (ret) goto error; - /* wait frontend lock */ - for (i = 20; i > 0; i--) { - dbg_info(": loop=%d\n", i); - msleep(50); - ret = lgdt3306a_read_status(fe, &status); - if (ret) - goto error; - - if (status & FE_HAS_LOCK) - break; - } + ret = lgdt3306a_read_status(fe, &status); + if (ret) + goto error; /* check if we have a valid signal */ if (status & FE_HAS_LOCK) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 41325328a22e..fe79358b035e 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -71,25 +71,27 @@ static struct regdata mb86a20s_init1[] = { }; static struct regdata mb86a20s_init2[] = { - { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, + { 0x50, 0xd1 }, { 0x51, 0x22 }, + { 0x39, 0x01 }, + { 0x71, 0x00 }, { 0x3b, 0x21 }, - { 0x3c, 0x38 }, + { 0x3c, 0x3a }, { 0x01, 0x0d }, - { 0x04, 0x08 }, { 0x05, 0x03 }, + { 0x04, 0x08 }, { 0x05, 0x05 }, { 0x04, 0x0e }, { 0x05, 0x00 }, - { 0x04, 0x0f }, { 0x05, 0x37 }, - { 0x04, 0x0b }, { 0x05, 0x78 }, + { 0x04, 0x0f }, { 0x05, 0x14 }, + { 0x04, 0x0b }, { 0x05, 0x8c }, { 0x04, 0x00 }, { 0x05, 0x00 }, - { 0x04, 0x01 }, { 0x05, 0x1e }, - { 0x04, 0x02 }, { 0x05, 0x07 }, - { 0x04, 0x03 }, { 0x05, 0xd0 }, + { 0x04, 0x01 }, { 0x05, 0x07 }, + { 0x04, 0x02 }, { 0x05, 0x0f }, + { 0x04, 0x03 }, { 0x05, 0xa0 }, { 0x04, 0x09 }, { 0x05, 0x00 }, { 0x04, 0x0a }, { 0x05, 0xff }, - { 0x04, 0x27 }, { 0x05, 0x00 }, + { 0x04, 0x27 }, { 0x05, 0x64 }, { 0x04, 0x28 }, { 0x05, 0x00 }, - { 0x04, 0x1e }, { 0x05, 0x00 }, - { 0x04, 0x29 }, { 0x05, 0x64 }, - { 0x04, 0x32 }, { 0x05, 0x02 }, + { 0x04, 0x1e }, { 0x05, 0xff }, + { 0x04, 0x29 }, { 0x05, 0x0a }, + { 0x04, 0x32 }, { 0x05, 0x0a }, { 0x04, 0x14 }, { 0x05, 0x02 }, { 0x04, 0x04 }, { 0x05, 0x00 }, { 0x04, 0x05 }, { 0x05, 0x22 }, @@ -97,8 +99,6 @@ static struct regdata mb86a20s_init2[] = { { 0x04, 0x07 }, { 0x05, 0xd8 }, { 0x04, 0x12 }, { 0x05, 0x00 }, { 0x04, 0x13 }, { 0x05, 0xff }, - { 0x04, 0x15 }, { 0x05, 0x4e }, - { 0x04, 0x16 }, { 0x05, 0x20 }, /* * On this demod, when the bit count reaches the count below, @@ -152,42 +152,36 @@ static struct regdata mb86a20s_init2[] = { { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, /* CN manual mode */ - + { 0x50, 0xd5 }, { 0x51, 0x01 }, { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, - { 0x50, 0xd7 }, { 0x51, 0xbf }, - { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xff }, - { 0x28, 0x46 }, { 0x29, 0x00 }, { 0x2a, 0x1a }, { 0x2b, 0x0c }, - - { 0x04, 0x40 }, { 0x05, 0x00 }, - { 0x28, 0x00 }, { 0x2b, 0x08 }, - { 0x28, 0x05 }, { 0x2b, 0x00 }, + { 0x50, 0xd7 }, { 0x51, 0x3f }, { 0x1c, 0x01 }, - { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x1f }, - { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x18 }, - { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x12 }, - { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x30 }, - { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x37 }, - { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, - { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x09 }, - { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x06 }, - { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7b }, - { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x76 }, - { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7d }, - { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x08 }, - { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0b }, - { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, - { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf2 }, - { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf3 }, - { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x05 }, - { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, - { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, - { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xef }, - { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xd8 }, - { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xf1 }, - { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x3d }, - { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x94 }, - { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xba }, + { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, + { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, + { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, + { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, + { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, + { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, + { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, + { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, + { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, + { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, + { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, + { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, + { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, + { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, + { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, + { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, + { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, + { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, + { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, + { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, + { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, + { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, + { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, + { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, + { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, { 0x50, 0x1e }, { 0x51, 0x5d }, { 0x50, 0x22 }, { 0x51, 0x00 }, { 0x50, 0x23 }, { 0x51, 0xc8 }, @@ -196,9 +190,7 @@ static struct regdata mb86a20s_init2[] = { { 0x50, 0x26 }, { 0x51, 0x00 }, { 0x50, 0x27 }, { 0x51, 0xc3 }, { 0x50, 0x39 }, { 0x51, 0x02 }, - { 0xec, 0x0f }, - { 0xeb, 0x1f }, - { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, + { 0x50, 0xd5 }, { 0x51, 0x01 }, { 0xd0, 0x00 }, }; @@ -318,7 +310,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status) if (val >= 7) *status |= FE_HAS_SYNC; - if (val >= 8) /* Maybe 9? */ + /* + * Actually, on state S8, it starts receiving TS, but the TS + * output is only on normal state after the transition to S9. + */ + if (val >= 9) *status |= FE_HAS_LOCK; dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", @@ -2058,6 +2054,11 @@ static void mb86a20s_release(struct dvb_frontend *fe) kfree(state); } +static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + static struct dvb_frontend_ops mb86a20s_ops; struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, @@ -2130,6 +2131,7 @@ static struct dvb_frontend_ops mb86a20s_ops = { .read_status = mb86a20s_read_status_and_stats, .read_signal_strength = mb86a20s_read_signal_strength_from_cache, .tune = mb86a20s_tune, + .get_frontend_algo = mb86a20s_get_frontend_algo, }; MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 8bf716a8ea58..78669ea68c61 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -25,6 +25,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/firmware.h> +#include <linux/regmap.h> #include "dvb_frontend.h" #include "dvb_math.h" @@ -40,7 +41,9 @@ */ struct si2165_state { - struct i2c_adapter *i2c; + struct i2c_client *client; + + struct regmap *regmap; struct dvb_frontend fe; @@ -108,61 +111,27 @@ static int si2165_write(struct si2165_state *state, const u16 reg, const u8 *src, const int count) { int ret; - struct i2c_msg msg; - u8 buf[2 + 4]; /* write a maximum of 4 bytes of data */ - - if (count + 2 > sizeof(buf)) { - dev_warn(&state->i2c->dev, - "%s: i2c wr reg=%04x: count=%d is too big!\n", - KBUILD_MODNAME, reg, count); - return -EINVAL; - } - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - memcpy(buf + 2, src, count); - - msg.addr = state->config.i2c_addr; - msg.flags = 0; - msg.buf = buf; - msg.len = count + 2; if (debug & DEBUG_I2C_WRITE) deb_i2c_write("reg: 0x%04x, data: %*ph\n", reg, count, src); - ret = i2c_transfer(state->i2c, &msg, 1); + ret = regmap_bulk_write(state->regmap, reg, src, count); - if (ret != 1) { - dev_err(&state->i2c->dev, "%s: ret == %d\n", __func__, ret); - if (ret < 0) - return ret; - else - return -EREMOTEIO; - } + if (ret) + dev_err(&state->client->dev, "%s: ret == %d\n", __func__, ret); - return 0; + return ret; } static int si2165_read(struct si2165_state *state, const u16 reg, u8 *val, const int count) { - int ret; - u8 reg_buf[] = { reg >> 8, reg & 0xff }; - struct i2c_msg msg[] = { - { .addr = state->config.i2c_addr, - .flags = 0, .buf = reg_buf, .len = 2 }, - { .addr = state->config.i2c_addr, - .flags = I2C_M_RD, .buf = val, .len = count }, - }; - - ret = i2c_transfer(state->i2c, msg, 2); + int ret = regmap_bulk_read(state->regmap, reg, val, count); - if (ret != 2) { - dev_err(&state->i2c->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", + if (ret) { + dev_err(&state->client->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n", __func__, state->config.i2c_addr, reg, ret); - if (ret < 0) - return ret; - else - return -EREMOTEIO; + return ret; } if (debug & DEBUG_I2C_READ) @@ -174,9 +143,9 @@ static int si2165_read(struct si2165_state *state, static int si2165_readreg8(struct si2165_state *state, const u16 reg, u8 *val) { - int ret; - - ret = si2165_read(state, reg, val, 1); + unsigned int val_tmp; + int ret = regmap_read(state->regmap, reg, &val_tmp); + *val = (u8)val_tmp; deb_readreg("R(0x%04x)=0x%02x\n", reg, *val); return ret; } @@ -194,7 +163,7 @@ static int si2165_readreg16(struct si2165_state *state, static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val) { - return si2165_write(state, reg, &val, 1); + return regmap_write(state->regmap, reg, val); } static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val) @@ -345,7 +314,7 @@ static int si2165_wait_init_done(struct si2165_state *state) return 0; usleep_range(1000, 50000); } - dev_err(&state->i2c->dev, "%s: init_done was not set\n", + dev_err(&state->client->dev, "%s: init_done was not set\n", KBUILD_MODNAME); return ret; } @@ -374,14 +343,14 @@ static int si2165_upload_firmware_block(struct si2165_state *state, wordcount = data[offset]; if (wordcount < 1 || data[offset+1] || data[offset+2] || data[offset+3]) { - dev_warn(&state->i2c->dev, + dev_warn(&state->client->dev, "%s: bad fw data[0..3] = %*ph\n", KBUILD_MODNAME, 4, data); return -EINVAL; } if (offset + 8 + wordcount * 4 > len) { - dev_warn(&state->i2c->dev, + dev_warn(&state->client->dev, "%s: len is too small for block len=%d, wordcount=%d\n", KBUILD_MODNAME, len, wordcount); return -EINVAL; @@ -444,15 +413,15 @@ static int si2165_upload_firmware(struct si2165_state *state) fw_file = SI2165_FIRMWARE_REV_D; break; default: - dev_info(&state->i2c->dev, "%s: no firmware file for revision=%d\n", + dev_info(&state->client->dev, "%s: no firmware file for revision=%d\n", KBUILD_MODNAME, state->chip_revcode); return 0; } /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); + ret = request_firmware(&fw, fw_file, &state->client->dev); if (ret) { - dev_warn(&state->i2c->dev, "%s: firmware file '%s' not found\n", + dev_warn(&state->client->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); goto error; } @@ -460,11 +429,11 @@ static int si2165_upload_firmware(struct si2165_state *state) data = fw->data; len = fw->size; - dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s' size=%d\n", + dev_info(&state->client->dev, "%s: downloading firmware from file '%s' size=%d\n", KBUILD_MODNAME, fw_file, len); if (len % 4 != 0) { - dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n", + dev_warn(&state->client->dev, "%s: firmware size is not multiple of 4\n", KBUILD_MODNAME); ret = -EINVAL; goto error; @@ -472,14 +441,14 @@ static int si2165_upload_firmware(struct si2165_state *state) /* check header (8 bytes) */ if (len < 8) { - dev_warn(&state->i2c->dev, "%s: firmware header is missing\n", + dev_warn(&state->client->dev, "%s: firmware header is missing\n", KBUILD_MODNAME); ret = -EINVAL; goto error; } if (data[0] != 1 || data[1] != 0) { - dev_warn(&state->i2c->dev, "%s: firmware file version is wrong\n", + dev_warn(&state->client->dev, "%s: firmware file version is wrong\n", KBUILD_MODNAME); ret = -EINVAL; goto error; @@ -517,7 +486,7 @@ static int si2165_upload_firmware(struct si2165_state *state) /* start right after the header */ offset = 8; - dev_info(&state->i2c->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", + dev_info(&state->client->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n", KBUILD_MODNAME, patch_version, block_count, crc_expected); ret = si2165_upload_firmware_block(state, data, len, &offset, 1); @@ -536,7 +505,7 @@ static int si2165_upload_firmware(struct si2165_state *state) ret = si2165_upload_firmware_block(state, data, len, &offset, block_count); if (ret < 0) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware could not be uploaded\n", KBUILD_MODNAME); goto error; @@ -548,7 +517,7 @@ static int si2165_upload_firmware(struct si2165_state *state) goto error; if (val16 != crc_expected) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware crc mismatch %04x != %04x\n", KBUILD_MODNAME, val16, crc_expected); ret = -EINVAL; @@ -560,7 +529,7 @@ static int si2165_upload_firmware(struct si2165_state *state) goto error; if (len != offset) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: firmware len mismatch %04x != %04x\n", KBUILD_MODNAME, len, offset); ret = -EINVAL; @@ -577,7 +546,7 @@ static int si2165_upload_firmware(struct si2165_state *state) if (ret < 0) goto error; - dev_info(&state->i2c->dev, "%s: fw load finished\n", KBUILD_MODNAME); + dev_info(&state->client->dev, "%s: fw load finished\n", KBUILD_MODNAME); ret = 0; state->firmware_loaded = true; @@ -611,7 +580,7 @@ static int si2165_init(struct dvb_frontend *fe) if (ret < 0) goto error; if (val != state->config.chip_mode) { - dev_err(&state->i2c->dev, "%s: could not set chip_mode\n", + dev_err(&state->client->dev, "%s: could not set chip_mode\n", KBUILD_MODNAME); return -EINVAL; } @@ -751,6 +720,9 @@ static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate) u64 oversamp; u32 reg_value; + if (!dvb_rate) + return -EINVAL; + oversamp = si2165_get_fe_clk(state); oversamp <<= 23; do_div(oversamp, dvb_rate); @@ -769,12 +741,15 @@ static int si2165_set_if_freq_shift(struct si2165_state *state) u32 IF = 0; if (!fe->ops.tuner_ops.get_if_frequency) { - dev_err(&state->i2c->dev, + dev_err(&state->client->dev, "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n", KBUILD_MODNAME); return -EINVAL; } + if (!fe_clk) + return -EINVAL; + fe->ops.tuner_ops.get_if_frequency(fe, &IF); if_freq_shift = IF; if_freq_shift <<= 29; @@ -1003,14 +978,6 @@ static int si2165_set_frontend(struct dvb_frontend *fe) return 0; } -static void si2165_release(struct dvb_frontend *fe) -{ - struct si2165_state *state = fe->demodulator_priv; - - dprintk("%s: called\n", __func__); - kfree(state); -} - static struct dvb_frontend_ops si2165_ops = { .info = { .name = "Silicon Labs ", @@ -1046,67 +1013,83 @@ static struct dvb_frontend_ops si2165_ops = { .set_frontend = si2165_set_frontend, .read_status = si2165_read_status, - - .release = si2165_release, }; -struct dvb_frontend *si2165_attach(const struct si2165_config *config, - struct i2c_adapter *i2c) +static int si2165_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct si2165_state *state = NULL; + struct si2165_platform_data *pdata = client->dev.platform_data; int n; - int io_ret; + int ret = 0; u8 val; char rev_char; const char *chip_name; - - if (config == NULL || i2c == NULL) - goto error; + static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x08ff, + }; /* allocate memory for the internal state */ state = kzalloc(sizeof(struct si2165_state), GFP_KERNEL); - if (state == NULL) + if (state == NULL) { + ret = -ENOMEM; goto error; + } + + /* create regmap */ + state->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(state->regmap)) { + ret = PTR_ERR(state->regmap); + goto error; + } /* setup the state */ - state->i2c = i2c; - state->config = *config; + state->client = client; + state->config.i2c_addr = client->addr; + state->config.chip_mode = pdata->chip_mode; + state->config.ref_freq_Hz = pdata->ref_freq_Hz; + state->config.inversion = pdata->inversion; if (state->config.ref_freq_Hz < 4000000 || state->config.ref_freq_Hz > 27000000) { - dev_err(&state->i2c->dev, "%s: ref_freq of %d Hz not supported by this driver\n", + dev_err(&state->client->dev, "%s: ref_freq of %d Hz not supported by this driver\n", KBUILD_MODNAME, state->config.ref_freq_Hz); + ret = -EINVAL; goto error; } /* create dvb_frontend */ memcpy(&state->fe.ops, &si2165_ops, sizeof(struct dvb_frontend_ops)); + state->fe.ops.release = NULL; state->fe.demodulator_priv = state; + i2c_set_clientdata(client, state); /* powerup */ - io_ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); - if (io_ret < 0) - goto error; + ret = si2165_writereg8(state, 0x0000, state->config.chip_mode); + if (ret < 0) + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0000, &val); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0000, &val); + if (ret < 0) + goto nodev_error; if (val != state->config.chip_mode) - goto error; + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0023, &state->chip_revcode); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0023, &state->chip_revcode); + if (ret < 0) + goto nodev_error; - io_ret = si2165_readreg8(state, 0x0118, &state->chip_type); - if (io_ret < 0) - goto error; + ret = si2165_readreg8(state, 0x0118, &state->chip_type); + if (ret < 0) + goto nodev_error; /* powerdown */ - io_ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); - if (io_ret < 0) - goto error; + ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF); + if (ret < 0) + goto nodev_error; if (state->chip_revcode < 26) rev_char = 'A' + state->chip_revcode; @@ -1124,12 +1107,12 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, state->has_dvbc = true; break; default: - dev_err(&state->i2c->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n", + dev_err(&state->client->dev, "%s: Unsupported Silicon Labs chip (type %d, rev %d)\n", KBUILD_MODNAME, state->chip_type, state->chip_revcode); - goto error; + goto nodev_error; } - dev_info(&state->i2c->dev, + dev_info(&state->client->dev, "%s: Detected Silicon Labs %s-%c (type %d, rev %d)\n", KBUILD_MODNAME, chip_name, rev_char, state->chip_type, state->chip_revcode); @@ -1149,13 +1132,46 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config, sizeof(state->fe.ops.info.name)); } - return &state->fe; + /* return fe pointer */ + *pdata->fe = &state->fe; + + return 0; +nodev_error: + ret = -ENODEV; error: kfree(state); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(si2165_attach); + +static int si2165_remove(struct i2c_client *client) +{ + struct si2165_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(state); + return 0; +} + +static const struct i2c_device_id si2165_id_table[] = { + {"si2165", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2165_id_table); + +static struct i2c_driver si2165_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "si2165", + }, + .probe = si2165_probe, + .remove = si2165_remove, + .id_table = si2165_id_table, +}; + +module_i2c_driver(si2165_driver); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h index 8a15d6a9c552..76c2ca7d7edb 100644 --- a/drivers/media/dvb-frontends/si2165.h +++ b/drivers/media/dvb-frontends/si2165.h @@ -28,10 +28,15 @@ enum { SI2165_MODE_PLL_XTAL = 0x21 }; -struct si2165_config { - /* i2c addr - * possible values: 0x64,0x65,0x66,0x67 */ - u8 i2c_addr; +/* I2C addresses + * possible values: 0x64,0x65,0x66,0x67 + */ +struct si2165_platform_data { + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; /* external clock or XTAL */ u8 chip_mode; @@ -45,18 +50,4 @@ struct si2165_config { bool inversion; }; -#if IS_REACHABLE(CONFIG_DVB_SI2165) -struct dvb_frontend *si2165_attach( - const struct si2165_config *config, - struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend *si2165_attach( - const struct si2165_config *config, - struct i2c_adapter *i2c) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif /* CONFIG_DVB_SI2165 */ - #endif /* _DVB_SI2165_H */ diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h index 2b70cf12cd79..e5932118834b 100644 --- a/drivers/media/dvb-frontends/si2165_priv.h +++ b/drivers/media/dvb-frontends/si2165_priv.h @@ -20,4 +20,21 @@ #define SI2165_FIRMWARE_REV_D "dvb-demod-si2165.fw" +struct si2165_config { + /* i2c addr + * possible values: 0x64,0x65,0x66,0x67 */ + u8 i2c_addr; + + /* external clock or XTAL */ + u8 chip_mode; + + /* frequency of external clock or xtal in Hz + * possible values: 4000000, 16000000, 20000000, 240000000, 27000000 + */ + u32 ref_freq_Hz; + + /* invert the spectrum */ + bool inversion; +}; + #endif /* _DVB_SI2165_PRIV */ diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c index a0c3c526b132..73347d51f340 100644 --- a/drivers/media/dvb-frontends/stb6000.c +++ b/drivers/media/dvb-frontends/stb6000.c @@ -186,7 +186,7 @@ static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops stb6000_tuner_ops = { +static const struct dvb_tuner_ops stb6000_tuner_ops = { .info = { .name = "ST STB6000", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c index b9c2511bf019..5add1182c3ca 100644 --- a/drivers/media/dvb-frontends/stb6100.c +++ b/drivers/media/dvb-frontends/stb6100.c @@ -522,7 +522,7 @@ static int stb6100_set_params(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops stb6100_ops = { +static const struct dvb_tuner_ops stb6100_ops = { .info = { .name = "STB6100 Silicon Tuner", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c index 91c6dcf65d2a..66a5a7f2295c 100644 --- a/drivers/media/dvb-frontends/stv6110.c +++ b/drivers/media/dvb-frontends/stv6110.c @@ -382,7 +382,7 @@ static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } -static struct dvb_tuner_ops stv6110_tuner_ops = { +static const struct dvb_tuner_ops stv6110_tuner_ops = { .info = { .name = "ST STV6110", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index a62c01e454f5..c611ad210b5c 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -345,7 +345,7 @@ static int stv6110x_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops stv6110x_ops = { +static const struct dvb_tuner_ops stv6110x_ops = { .info = { .name = "STV6110(A) Silicon Tuner", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index de0a1c110972..bc247f9b553a 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -1217,7 +1217,7 @@ static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) } -static struct dvb_tuner_ops tuner_ops = { +static const struct dvb_tuner_ops tuner_ops = { .info = { .name = "NXP TDA18271C2D", .frequency_min = 47125000, diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c index 82f8cc534f33..7ca965987f40 100644 --- a/drivers/media/dvb-frontends/tda665x.c +++ b/drivers/media/dvb-frontends/tda665x.c @@ -206,7 +206,7 @@ static int tda665x_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops tda665x_ops = { +static const struct dvb_tuner_ops tda665x_ops = { .get_status = tda665x_get_status, .set_params = tda665x_set_params, .get_frequency = tda665x_get_frequency, diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c index 3285b1bc4642..e0df93191b9e 100644 --- a/drivers/media/dvb-frontends/tda8261.c +++ b/drivers/media/dvb-frontends/tda8261.c @@ -161,7 +161,7 @@ static int tda8261_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops tda8261_ops = { +static const struct dvb_tuner_ops tda8261_ops = { .info = { .name = "TDA8261", diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c index 04bbcc24de0a..2ec671df1441 100644 --- a/drivers/media/dvb-frontends/tda826x.c +++ b/drivers/media/dvb-frontends/tda826x.c @@ -129,7 +129,7 @@ static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops tda826x_tuner_ops = { +static const struct dvb_tuner_ops tda826x_tuner_ops = { .info = { .name = "Philips TDA826X", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 14b410ffe612..a9f6bbea6df3 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -496,7 +496,7 @@ static int ts2020_read_signal_strength(struct dvb_frontend *fe, return 0; } -static struct dvb_tuner_ops ts2020_tuner_ops = { +static const struct dvb_tuner_ops ts2020_tuner_ops = { .info = { .name = "TS2020", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c index 029384d1fddd..6da12b9e55eb 100644 --- a/drivers/media/dvb-frontends/tua6100.c +++ b/drivers/media/dvb-frontends/tua6100.c @@ -157,7 +157,7 @@ static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops tua6100_tuner_ops = { +static const struct dvb_tuner_ops tua6100_tuner_ops = { .info = { .name = "Infineon TUA6100", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c index 0903d461b8fa..7ed81315965f 100644 --- a/drivers/media/dvb-frontends/zl10036.c +++ b/drivers/media/dvb-frontends/zl10036.c @@ -446,7 +446,7 @@ static int zl10036_init(struct dvb_frontend *fe) return ret; } -static struct dvb_tuner_ops zl10036_tuner_ops = { +static const struct dvb_tuner_ops zl10036_tuner_ops = { .info = { .name = "Zarlink ZL10036", .frequency_min = 950000, diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c index ee09ec26c553..f8c271be196c 100644 --- a/drivers/media/dvb-frontends/zl10039.c +++ b/drivers/media/dvb-frontends/zl10039.c @@ -255,7 +255,7 @@ static int zl10039_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops zl10039_ops = { +static const struct dvb_tuner_ops zl10039_ops = { .release = zl10039_release, .init = zl10039_init, .sleep = zl10039_sleep, diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ce9006e10a30..2669b4bad910 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -21,7 +21,7 @@ config VIDEO_IR_I2C # Encoder / Decoder module configuration # -menu "Encoders, decoders, sensors and other helper chips" +menu "I2C Encoders, decoders, sensors and other helper chips" visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST comment "Audio decoders, processors and mixers" @@ -187,7 +187,7 @@ comment "Video decoders" config VIDEO_ADV7180 tristate "Analog Devices ADV7180 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7180 video decoder. @@ -295,6 +295,13 @@ config VIDEO_ML86V7667 To compile this driver as a module, choose M here: the module will be called ml86v7667. +config VIDEO_AD5820 + tristate "AD5820 lens voice coil support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + ---help--- + This is a driver for the AD5820 camera lens voice coil. + It is used for example in Nokia N900 (RX-51). + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L2 && I2C @@ -571,6 +578,13 @@ config VIDEO_MT9M032 This driver supports MT9M032 camera sensors from Aptina, monochrome models only. +config VIDEO_MT9M111 + tristate "mt9m111, mt9m112 and mt9m131 support" + depends on I2C && VIDEO_V4L2 + help + This driver supports MT9M111, MT9M112 and MT9M131 cameras from + Micron/Aptina + config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 94f2c99e890d..92773b2e6225 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o +obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o @@ -59,6 +60,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o +obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c new file mode 100644 index 000000000000..beab2f381b81 --- /dev/null +++ b/drivers/media/i2c/ad5820.c @@ -0,0 +1,372 @@ +/* + * drivers/media/i2c/ad5820.c + * + * AD5820 DAC driver for camera voice coil focus. + * + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2007 Texas Instruments + * Copyright (C) 2016 Pavel Machek <pavel@ucw.cz> + * + * Contact: Tuukka Toivonen <tuukkat76@gmail.com> + * Sakari Ailus <sakari.ailus@iki.fi> + * + * Based on af_d88.c by Texas Instruments. + * + * 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. + * + * 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/errno.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#define AD5820_NAME "ad5820" + +/* Register definitions */ +#define AD5820_POWER_DOWN (1 << 15) +#define AD5820_DAC_SHIFT 4 +#define AD5820_RAMP_MODE_LINEAR (0 << 3) +#define AD5820_RAMP_MODE_64_16 (1 << 3) + +#define CODE_TO_RAMP_US(s) ((s) == 0 ? 0 : (1 << ((s) - 1)) * 50) +#define RAMP_US_TO_CODE(c) fls(((c) + ((c)>>1)) / 50) + +#define to_ad5820_device(sd) container_of(sd, struct ad5820_device, subdev) + +struct ad5820_device { + struct v4l2_subdev subdev; + struct ad5820_platform_data *platform_data; + struct regulator *vana; + + struct v4l2_ctrl_handler ctrls; + u32 focus_absolute; + u32 focus_ramp_time; + u32 focus_ramp_mode; + + struct mutex power_lock; + int power_count; + + bool standby; +}; + +static int ad5820_write(struct ad5820_device *coil, u16 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(&coil->subdev); + struct i2c_msg msg; + int r; + + if (!client->adapter) + return -ENODEV; + + data = cpu_to_be16(data); + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = (u8 *)&data; + + r = i2c_transfer(client->adapter, &msg, 1); + if (r < 0) { + dev_err(&client->dev, "write failed, error %d\n", r); + return r; + } + + return 0; +} + +/* + * Calculate status word and write it to the device based on current + * values of V4L2 controls. It is assumed that the stored V4L2 control + * values are properly limited and rounded. + */ +static int ad5820_update_hw(struct ad5820_device *coil) +{ + u16 status; + + status = RAMP_US_TO_CODE(coil->focus_ramp_time); + status |= coil->focus_ramp_mode + ? AD5820_RAMP_MODE_64_16 : AD5820_RAMP_MODE_LINEAR; + status |= coil->focus_absolute << AD5820_DAC_SHIFT; + + if (coil->standby) + status |= AD5820_POWER_DOWN; + + return ad5820_write(coil, status); +} + +/* + * Power handling + */ +static int ad5820_power_off(struct ad5820_device *coil, bool standby) +{ + int ret = 0, ret2; + + /* + * Go to standby first as real power off my be denied by the hardware + * (single power line control for both coil and sensor). + */ + if (standby) { + coil->standby = true; + ret = ad5820_update_hw(coil); + } + + ret2 = regulator_disable(coil->vana); + if (ret) + return ret; + return ret2; +} + +static int ad5820_power_on(struct ad5820_device *coil, bool restore) +{ + int ret; + + ret = regulator_enable(coil->vana); + if (ret < 0) + return ret; + + if (restore) { + /* Restore the hardware settings. */ + coil->standby = false; + ret = ad5820_update_hw(coil); + if (ret) + goto fail; + } + return 0; + +fail: + coil->standby = true; + regulator_disable(coil->vana); + + return ret; +} + +/* + * V4L2 controls + */ +static int ad5820_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ad5820_device *coil = + container_of(ctrl->handler, struct ad5820_device, ctrls); + + switch (ctrl->id) { + case V4L2_CID_FOCUS_ABSOLUTE: + coil->focus_absolute = ctrl->val; + return ad5820_update_hw(coil); + } + + return 0; +} + +static const struct v4l2_ctrl_ops ad5820_ctrl_ops = { + .s_ctrl = ad5820_set_ctrl, +}; + + +static int ad5820_init_controls(struct ad5820_device *coil) +{ + v4l2_ctrl_handler_init(&coil->ctrls, 1); + + /* + * V4L2_CID_FOCUS_ABSOLUTE + * + * Minimum current is 0 mA, maximum is 100 mA. Thus, 1 code is + * equivalent to 100/1023 = 0.0978 mA. Nevertheless, we do not use [mA] + * for focus position, because it is meaningless for user. Meaningful + * would be to use focus distance or even its inverse, but since the + * driver doesn't have sufficiently knowledge to do the conversion, we + * will just use abstract codes here. In any case, smaller value = focus + * position farther from camera. The default zero value means focus at + * infinity, and also least current consumption. + */ + v4l2_ctrl_new_std(&coil->ctrls, &ad5820_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, 0, 1023, 1, 0); + + if (coil->ctrls.error) + return coil->ctrls.error; + + coil->focus_absolute = 0; + coil->focus_ramp_time = 0; + coil->focus_ramp_mode = 0; + + coil->subdev.ctrl_handler = &coil->ctrls; + + return 0; +} + +/* + * V4L2 subdev operations + */ +static int ad5820_registered(struct v4l2_subdev *subdev) +{ + struct ad5820_device *coil = to_ad5820_device(subdev); + + return ad5820_init_controls(coil); +} + +static int +ad5820_set_power(struct v4l2_subdev *subdev, int on) +{ + struct ad5820_device *coil = to_ad5820_device(subdev); + int ret = 0; + + mutex_lock(&coil->power_lock); + + /* + * If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (coil->power_count == !on) { + ret = on ? ad5820_power_on(coil, true) : + ad5820_power_off(coil, true); + if (ret < 0) + goto done; + } + + /* Update the power count. */ + coil->power_count += on ? 1 : -1; + WARN_ON(coil->power_count < 0); + +done: + mutex_unlock(&coil->power_lock); + return ret; +} + +static int ad5820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return ad5820_set_power(sd, 1); +} + +static int ad5820_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return ad5820_set_power(sd, 0); +} + +static const struct v4l2_subdev_core_ops ad5820_core_ops = { + .s_power = ad5820_set_power, +}; + +static const struct v4l2_subdev_ops ad5820_ops = { + .core = &ad5820_core_ops, +}; + +static const struct v4l2_subdev_internal_ops ad5820_internal_ops = { + .registered = ad5820_registered, + .open = ad5820_open, + .close = ad5820_close, +}; + +/* + * I2C driver + */ +static int __maybe_unused ad5820_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ad5820_device *coil = to_ad5820_device(subdev); + + if (!coil->power_count) + return 0; + + return ad5820_power_off(coil, false); +} + +static int __maybe_unused ad5820_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ad5820_device *coil = to_ad5820_device(subdev); + + if (!coil->power_count) + return 0; + + return ad5820_power_on(coil, true); +} + +static int ad5820_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct ad5820_device *coil; + int ret; + + coil = devm_kzalloc(&client->dev, sizeof(*coil), GFP_KERNEL); + if (!coil) + return -ENOMEM; + + coil->vana = devm_regulator_get(&client->dev, "VANA"); + if (IS_ERR(coil->vana)) { + ret = PTR_ERR(coil->vana); + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "could not get regulator for vana\n"); + return ret; + } + + mutex_init(&coil->power_lock); + + v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); + coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + coil->subdev.internal_ops = &ad5820_internal_ops; + strcpy(coil->subdev.name, "ad5820 focus"); + + ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); + if (ret < 0) + goto cleanup2; + + ret = v4l2_async_register_subdev(&coil->subdev); + if (ret < 0) + goto cleanup; + + return ret; + +cleanup2: + mutex_destroy(&coil->power_lock); +cleanup: + media_entity_cleanup(&coil->subdev.entity); + return ret; +} + +static int __exit ad5820_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ad5820_device *coil = to_ad5820_device(subdev); + + v4l2_device_unregister_subdev(&coil->subdev); + v4l2_ctrl_handler_free(&coil->ctrls); + media_entity_cleanup(&coil->subdev.entity); + mutex_destroy(&coil->power_lock); + return 0; +} + +static const struct i2c_device_id ad5820_id_table[] = { + { AD5820_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5820_id_table); + +static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); + +static struct i2c_driver ad5820_i2c_driver = { + .driver = { + .name = AD5820_NAME, + .pm = &ad5820_pm, + }, + .probe = ad5820_probe, + .remove = __exit_p(ad5820_remove), + .id_table = ad5820_id_table, +}; + +module_i2c_driver(ad5820_i2c_driver); + +MODULE_AUTHOR("Tuukka Toivonen"); +MODULE_DESCRIPTION("AD5820 camera lens driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 0462f461e679..50f354144ee7 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -98,7 +98,6 @@ struct ad9389b_state { struct ad9389b_state_edid edid; /* Running counter of the number of detected EDIDs (for debugging) */ unsigned edid_detect_counter; - struct workqueue_struct *work_queue; struct delayed_work edid_handler; /* work entry */ }; @@ -843,8 +842,7 @@ static void ad9389b_edid_handler(struct work_struct *work) v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); ad9389b_s_power(sd, false); ad9389b_s_power(sd, true); - queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + schedule_delayed_work(&state->edid_handler, EDID_DELAY); return; } } @@ -933,8 +931,7 @@ static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd) ad9389b_setup(sd); ad9389b_notify_monitor_detect(sd); state->edid.read_retries = EDID_MAX_RETRIES; - queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + schedule_delayed_work(&state->edid_handler, EDID_DELAY); } else if (!(status & MASK_AD9389B_HPD_DETECT)) { v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); state->have_monitor = false; @@ -1065,8 +1062,7 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) ad9389b_wr(sd, 0xc9, 0xf); ad9389b_wr(sd, 0xc4, state->edid.segments); state->edid.read_retries = EDID_MAX_RETRIES; - queue_delayed_work(state->work_queue, - &state->edid_handler, EDID_DELAY); + schedule_delayed_work(&state->edid_handler, EDID_DELAY); return false; } @@ -1170,13 +1166,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * goto err_entity; } - state->work_queue = create_singlethread_workqueue(sd->name); - if (state->work_queue == NULL) { - v4l2_err(sd, "could not create workqueue\n"); - err = -ENOMEM; - goto err_unreg; - } - INIT_DELAYED_WORK(&state->edid_handler, ad9389b_edid_handler); state->dv_timings = dv1080p60; @@ -1187,8 +1176,6 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * client->addr << 1, client->adapter->name); return 0; -err_unreg: - i2c_unregister_device(state->edid_i2c_client); err_entity: media_entity_cleanup(&sd->entity); err_hdl: @@ -1211,9 +1198,8 @@ static int ad9389b_remove(struct i2c_client *client) ad9389b_s_stream(sd, false); ad9389b_s_audio_stream(sd, false); ad9389b_init_setup(sd); - cancel_delayed_work(&state->edid_handler); + cancel_delayed_work_sync(&state->edid_handler); i2c_unregister_device(state->edid_i2c_client); - destroy_workqueue(state->work_queue); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1231,7 +1217,6 @@ MODULE_DEVICE_TABLE(i2c, ad9389b_id); static struct i2c_driver ad9389b_driver = { .driver = { - .owner = THIS_MODULE, .name = "ad9389b", }, .probe = ad9389b_probe, diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 95cbc857f36e..cbed2bc29325 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -26,6 +26,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/gpio/consumer.h> #include <linux/videodev2.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> @@ -56,10 +57,11 @@ #define ADV7182_REG_INPUT_VIDSEL 0x0002 +#define ADV7180_REG_OUTPUT_CONTROL 0x0003 #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 -#define ADV7180_REG_AUTODETECT_ENABLE 0x07 +#define ADV7180_REG_AUTODETECT_ENABLE 0x0007 #define ADV7180_AUTODETECT_DEFAULT 0x7f /* Contrast */ #define ADV7180_REG_CON 0x0008 /*Unsigned */ @@ -100,6 +102,20 @@ #define ADV7180_REG_IDENT 0x0011 #define ADV7180_ID_7180 0x18 +#define ADV7180_REG_STATUS3 0x0013 +#define ADV7180_REG_ANALOG_CLAMP_CTL 0x0014 +#define ADV7180_REG_SHAP_FILTER_CTL_1 0x0017 +#define ADV7180_REG_CTRL_2 0x001d +#define ADV7180_REG_VSYNC_FIELD_CTL_1 0x0031 +#define ADV7180_REG_MANUAL_WIN_CTL_1 0x003d +#define ADV7180_REG_MANUAL_WIN_CTL_2 0x003e +#define ADV7180_REG_MANUAL_WIN_CTL_3 0x003f +#define ADV7180_REG_LOCK_CNT 0x0051 +#define ADV7180_REG_CVBS_TRIM 0x0052 +#define ADV7180_REG_CLAMP_ADJ 0x005a +#define ADV7180_REG_RES_CIR 0x005f +#define ADV7180_REG_DIFF_MODE 0x0060 + #define ADV7180_REG_ICONF1 0x2040 #define ADV7180_ICONF1_ACTIVE_LOW 0x01 #define ADV7180_ICONF1_PSYNC_ONLY 0x10 @@ -129,9 +145,15 @@ #define ADV7180_REG_VPP_SLAVE_ADDR 0xFD #define ADV7180_REG_CSI_SLAVE_ADDR 0xFE -#define ADV7180_REG_FLCONTROL 0x40e0 +#define ADV7180_REG_ACE_CTRL1 0x4080 +#define ADV7180_REG_ACE_CTRL5 0x4084 +#define ADV7180_REG_FLCONTROL 0x40e0 #define ADV7180_FLCONTROL_FL_ENABLE 0x1 +#define ADV7180_REG_RST_CLAMP 0x809c +#define ADV7180_REG_AGC_ADJ1 0x80b6 +#define ADV7180_REG_AGC_ADJ2 0x80c0 + #define ADV7180_CSI_REG_PWRDN 0x00 #define ADV7180_CSI_PWRDN 0x80 @@ -192,6 +214,7 @@ struct adv7180_state { struct media_pad pad; struct mutex mutex; /* mutual excl. when accessing chip */ int irq; + struct gpio_desc *pwdn_gpio; v4l2_std_id curr_norm; bool powered; bool streaming; @@ -442,6 +465,19 @@ static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } +static void adv7180_set_power_pin(struct adv7180_state *state, bool on) +{ + if (!state->pwdn_gpio) + return; + + if (on) { + gpiod_set_value_cansleep(state->pwdn_gpio, 0); + usleep_range(5000, 10000); + } else { + gpiod_set_value_cansleep(state->pwdn_gpio, 1); + } +} + static int adv7180_set_power(struct adv7180_state *state, bool on) { u8 val; @@ -597,7 +633,7 @@ static int adv7180_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_YUYV8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_2X8; return 0; } @@ -607,7 +643,7 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd, { struct adv7180_state *state = to_state(sd); - fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; fmt->width = 720; fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; @@ -675,6 +711,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, { struct adv7180_state *state = to_state(sd); struct v4l2_mbus_framefmt *framefmt; + int ret; switch (format->format.field) { case V4L2_FIELD_NONE: @@ -686,8 +723,9 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, break; } + ret = adv7180_mbus_fmt(sd, &format->format); + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - framefmt = &format->format; if (state->field != format->format.field) { state->field = format->format.field; adv7180_set_power(state, false); @@ -699,7 +737,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, *framefmt = format->format; } - return adv7180_mbus_fmt(sd, framefmt); + return ret; } static int adv7180_g_mbus_config(struct v4l2_subdev *sd, @@ -725,16 +763,16 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, return 0; } -static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap) +static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect) { struct adv7180_state *state = to_state(sd); if (state->curr_norm & V4L2_STD_525_60) { - cropcap->pixelaspect.numerator = 11; - cropcap->pixelaspect.denominator = 10; + aspect->numerator = 11; + aspect->denominator = 10; } else { - cropcap->pixelaspect.numerator = 54; - cropcap->pixelaspect.denominator = 59; + aspect->numerator = 54; + aspect->denominator = 59; } return 0; @@ -787,7 +825,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, .g_mbus_config = adv7180_g_mbus_config, - .cropcap = adv7180_cropcap, + .g_pixelaspect = adv7180_g_pixelaspect, .g_tvnorms = adv7180_g_tvnorms, .s_stream = adv7180_s_stream, }; @@ -886,16 +924,20 @@ static int adv7182_init(struct adv7180_state *state) /* ADI required writes */ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { - adv7180_write(state, 0x0003, 0x4e); - adv7180_write(state, 0x0004, 0x57); - adv7180_write(state, 0x001d, 0xc0); + adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e); + adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57); + adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0); } else { if (state->chip_info->flags & ADV7180_FLAG_V2) - adv7180_write(state, 0x0004, 0x17); + adv7180_write(state, + ADV7180_REG_EXTENDED_OUTPUT_CONTROL, + 0x17); else - adv7180_write(state, 0x0004, 0x07); - adv7180_write(state, 0x0003, 0x0c); - adv7180_write(state, 0x001d, 0x40); + adv7180_write(state, + ADV7180_REG_EXTENDED_OUTPUT_CONTROL, + 0x07); + adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c); + adv7180_write(state, ADV7180_REG_CTRL_2, 0x40); } adv7180_write(state, 0x0013, 0x00); @@ -972,8 +1014,8 @@ static int adv7182_select_input(struct adv7180_state *state, unsigned int input) return ret; /* Reset clamp circuitry - ADI recommended writes */ - adv7180_write(state, 0x809c, 0x00); - adv7180_write(state, 0x809c, 0xff); + adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00); + adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff); input_type = adv7182_get_input_type(input); @@ -981,10 +1023,10 @@ static int adv7182_select_input(struct adv7180_state *state, unsigned int input) case ADV7182_INPUT_TYPE_CVBS: case ADV7182_INPUT_TYPE_DIFF_CVBS: /* ADI recommends to use the SH1 filter */ - adv7180_write(state, 0x0017, 0x41); + adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41); break; default: - adv7180_write(state, 0x0017, 0x01); + adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01); break; } @@ -994,21 +1036,21 @@ static int adv7182_select_input(struct adv7180_state *state, unsigned int input) lbias = adv7182_lbias_settings[input_type]; for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++) - adv7180_write(state, 0x0052 + i, lbias[i]); + adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]); if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) { /* ADI required writes to make differential CVBS work */ - adv7180_write(state, 0x005f, 0xa8); - adv7180_write(state, 0x005a, 0x90); - adv7180_write(state, 0x0060, 0xb0); - adv7180_write(state, 0x80b6, 0x08); - adv7180_write(state, 0x80c0, 0xa0); + adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8); + adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90); + adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0); + adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08); + adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0); } else { - adv7180_write(state, 0x005f, 0xf0); - adv7180_write(state, 0x005a, 0xd0); - adv7180_write(state, 0x0060, 0x10); - adv7180_write(state, 0x80b6, 0x9c); - adv7180_write(state, 0x80c0, 0x00); + adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0); + adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0); + adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10); + adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c); + adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00); } return 0; @@ -1185,6 +1227,8 @@ static int init_device(struct adv7180_state *state) mutex_lock(&state->mutex); + adv7180_set_power_pin(state, true); + adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); usleep_range(5000, 10000); @@ -1254,6 +1298,14 @@ static int adv7180_probe(struct i2c_client *client, state->field = V4L2_FIELD_INTERLACED; state->chip_info = (struct adv7180_chip_info *)id->driver_data; + state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(state->pwdn_gpio)) { + ret = PTR_ERR(state->pwdn_gpio); + v4l_err(client, "request for power pin failed: %d\n", ret); + return ret; + } + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { state->csi_client = i2c_new_dummy(client->adapter, ADV7180_DEFAULT_CSI_I2C_ADDR); @@ -1345,6 +1397,8 @@ static int adv7180_remove(struct i2c_client *client) if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) i2c_unregister_device(state->csi_client); + adv7180_set_power_pin(state, false); + mutex_destroy(&state->mutex); return 0; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 2bec737881e9..04eecda74d66 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -644,7 +644,6 @@ MODULE_DEVICE_TABLE(i2c, adv7183_id); static struct i2c_driver adv7183_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7183", }, .probe = adv7183_probe, diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index 76d987476e35..f19ad4ecd11e 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -456,7 +456,6 @@ MODULE_DEVICE_TABLE(i2c, adv7393_id); static struct i2c_driver adv7393_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7393", }, .probe = adv7393_probe, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 53030d631653..5ba0f21bcfe4 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1898,6 +1898,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * state->i2c_cec_addr >> 1); if (state->i2c_cec == NULL) { v4l2_err(sd, "failed to register cec i2c client\n"); + err = -ENOMEM; goto err_unreg_edid; } adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index d9f2b6b76d59..3a795dcb7d8e 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -124,21 +124,27 @@ static int ak881x_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +static int ak881x_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ak881x *ak881x = to_ak881x(client); - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = 720; - a->bounds.height = ak881x->lines; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = 720; + sel->r.height = ak881x->lines; + return 0; + default: + return -EINVAL; + } } static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) @@ -207,13 +213,13 @@ static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { }; static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { - .cropcap = ak881x_cropcap, .s_std_output = ak881x_s_std_output, .s_stream = ak881x_s_stream, }; static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = { .enum_mbus_code = ak881x_enum_mbus_code, + .get_selection = ak881x_get_selection, .set_fmt = ak881x_fill_fmt, .get_fmt = ak881x_fill_fmt, }; diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index d28b4f37fe5f..7da5f69cace6 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(i2c, cs3308_id); static struct i2c_driver cs3308_driver = { .driver = { - .owner = THIS_MODULE, .name = "cs3308", }, .probe = cs3308_probe, diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index bf82726fd3f4..f95a6bc839d5 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -35,6 +35,7 @@ * */ +#include <asm/unaligned.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -63,51 +64,80 @@ module_param(debug, int, 0644); /* debug level (0,1,2) */ /* ----------------------------------------------------------------------- */ static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol, - u32 *scancode, u8 *ptoggle, int size, int offset) + u32 *scancode, u8 *ptoggle, int size) { unsigned char buf[6]; - int start, range, toggle, dev, code, ircode; + int start, range, toggle, dev, code, ircode, vendor; /* poll IR chip */ if (size != i2c_master_recv(ir->c, buf, size)) return -EIO; - /* split rc5 data block ... */ - start = (buf[offset] >> 7) & 1; - range = (buf[offset] >> 6) & 1; - toggle = (buf[offset] >> 5) & 1; - dev = buf[offset] & 0x1f; - code = (buf[offset+1] >> 2) & 0x3f; + if (buf[0] & 0x80) { + int offset = (size == 6) ? 3 : 0; - /* rc5 has two start bits - * the first bit must be one - * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) - */ - if (!start) - /* no key pressed */ - return 0; + /* split rc5 data block ... */ + start = (buf[offset] >> 7) & 1; + range = (buf[offset] >> 6) & 1; + toggle = (buf[offset] >> 5) & 1; + dev = buf[offset] & 0x1f; + code = (buf[offset+1] >> 2) & 0x3f; - /* filter out invalid key presses */ - ircode = (start << 12) | (toggle << 11) | (dev << 6) | code; - if ((ircode & 0x1fff) == 0x1fff) - return 0; + /* rc5 has two start bits + * the first bit must be one + * the second bit defines the command range: + * 1 = 0-63, 0 = 64 - 127 + */ + if (!start) + /* no key pressed */ + return 0; - if (!range) - code += 64; + /* filter out invalid key presses */ + ircode = (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff) == 0x1fff) + return 0; - dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", - start, range, toggle, dev, code); + if (!range) + code += 64; - *protocol = RC_TYPE_RC5; - *scancode = RC_SCANCODE_RC5(dev, code); - *ptoggle = toggle; - return 1; + dprintk(1, "ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", + start, range, toggle, dev, code); + + *protocol = RC_TYPE_RC5; + *scancode = RC_SCANCODE_RC5(dev, code); + *ptoggle = toggle; + + return 1; + } else if (size == 6 && (buf[0] & 0x40)) { + code = buf[4]; + dev = buf[3]; + vendor = get_unaligned_be16(buf + 1); + + if (vendor == 0x800f) { + *ptoggle = (dev & 0x80) != 0; + *protocol = RC_TYPE_RC6_MCE; + dev &= 0x7f; + dprintk(1, "ir hauppauge (rc6-mce): t%d vendor=%d dev=%d code=%d\n", + toggle, vendor, dev, code); + } else { + *ptoggle = 0; + *protocol = RC_TYPE_RC6_6A_32; + dprintk(1, "ir hauppauge (rc6-6a-32): vendor=%d dev=%d code=%d\n", + vendor, dev, code); + } + + *scancode = RC_SCANCODE_RC6_6A(vendor, dev, code); + + return 1; + } + + return 0; } static int get_key_haup(struct IR_i2c *ir, enum rc_type *protocol, u32 *scancode, u8 *toggle) { - return get_key_haup_common (ir, protocol, scancode, toggle, 3, 0); + return get_key_haup_common(ir, protocol, scancode, toggle, 3); } static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol, @@ -126,7 +156,7 @@ static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol, if (ret != 1) return (ret < 0) ? ret : -EINVAL; - return get_key_haup_common(ir, protocol, scancode, toggle, 6, 3); + return get_key_haup_common(ir, protocol, scancode, toggle, 6); } static int get_key_pixelview(struct IR_i2c *ir, enum rc_type *protocol, @@ -347,7 +377,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) case 0x71: name = "Hauppauge/Zilog Z8"; ir->get_key = get_key_haup_xvr; - rc_type = RC_BIT_RC5; + rc_type = RC_BIT_RC5 | RC_BIT_RC6_MCE | RC_BIT_RC6_6A_32; ir_codes = RC_MAP_HAUPPAUGE; break; } diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/mt9m111.c index 6dfaead6aaa8..72e71b762827 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -16,10 +16,11 @@ #include <linux/v4l2-mediabus.h> #include <linux/module.h> -#include <media/soc_camera.h> +#include <media/v4l2-async.h> #include <media/v4l2-clk.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> /* * MT9M111, MT9M112 and MT9M131: @@ -187,10 +188,10 @@ struct mt9m111_datafmt { }; static const struct mt9m111_datafmt mt9m111_colour_fmts[] = { - {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_SRGB}, {MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, {MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, @@ -383,30 +384,36 @@ static int mt9m111_reset(struct mt9m111 *mt9m111) return ret; } -static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int mt9m111_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct v4l2_rect rect = a->c; - struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct v4l2_rect rect = sel->r; int width, height; - int ret; + int ret, align = 0; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; if (mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { /* Bayer format - even size lengths */ - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); + align = 1; /* Let the user play with the starting pixel */ } /* FIXME: the datasheet doesn't specify minimum sizes */ - soc_camera_limit_side(&rect.left, &rect.width, - MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT); + v4l_bound_align_image(&rect.width, 2, MT9M111_MAX_WIDTH, align, + &rect.height, 2, MT9M111_MAX_HEIGHT, align, 0); + rect.left = clamp(rect.left, MT9M111_MIN_DARK_COLS, + MT9M111_MIN_DARK_COLS + MT9M111_MAX_WIDTH - + (__s32)rect.width); + rect.top = clamp(rect.top, MT9M111_MIN_DARK_ROWS, + MT9M111_MIN_DARK_ROWS + MT9M111_MAX_HEIGHT - + (__s32)rect.height); width = min(mt9m111->width, rect.width); height = min(mt9m111->height, rect.height); @@ -421,30 +428,30 @@ static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return ret; } -static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m111_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); - - a->c = mt9m111->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m111 *mt9m111 = to_mt9m111(client); -static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - a->bounds.left = MT9M111_MIN_DARK_COLS; - a->bounds.top = MT9M111_MIN_DARK_ROWS; - a->bounds.width = MT9M111_MAX_WIDTH; - a->bounds.height = MT9M111_MAX_HEIGHT; - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = MT9M111_MIN_DARK_COLS; + sel->r.top = MT9M111_MIN_DARK_ROWS; + sel->r.width = MT9M111_MAX_WIDTH; + sel->r.height = MT9M111_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9m111->rect; + return 0; + default: + return -EINVAL; + } } static int mt9m111_get_fmt(struct v4l2_subdev *sd, @@ -775,17 +782,16 @@ static int mt9m111_init(struct mt9m111 *mt9m111) static int mt9m111_power_on(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - ret = soc_camera_power_on(&client->dev, ssdd, mt9m111->clk); + ret = v4l2_clk_enable(mt9m111->clk); if (ret < 0) return ret; ret = mt9m111_resume(mt9m111); if (ret < 0) { dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); - soc_camera_power_off(&client->dev, ssdd, mt9m111->clk); + v4l2_clk_disable(mt9m111->clk); } return ret; @@ -793,11 +799,8 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) static void mt9m111_power_off(struct mt9m111 *mt9m111) { - struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - mt9m111_suspend(mt9m111); - soc_camera_power_off(&client->dev, ssdd, mt9m111->clk); + v4l2_clk_disable(mt9m111->clk); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -854,27 +857,22 @@ static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { - .s_crop = mt9m111_s_crop, - .g_crop = mt9m111_g_crop, - .cropcap = mt9m111_cropcap, .g_mbus_config = mt9m111_g_mbus_config, }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { .enum_mbus_code = mt9m111_enum_mbus_code, + .get_selection = mt9m111_get_selection, + .set_selection = mt9m111_set_selection, .get_fmt = mt9m111_get_fmt, .set_fmt = mt9m111_set_fmt, }; @@ -933,20 +931,8 @@ static int mt9m111_probe(struct i2c_client *client, { struct mt9m111 *mt9m111; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (client->dev.of_node) { - ssdd = devm_kzalloc(&client->dev, sizeof(*ssdd), GFP_KERNEL); - if (!ssdd) - return -ENOMEM; - client->dev.platform_data = ssdd; - } - if (!ssdd) { - dev_err(&client->dev, "mt9m111: driver needs platform data\n"); - return -EINVAL; - } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); @@ -992,10 +978,6 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->lastpage = -1; mutex_init(&mt9m111->power_lock); - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto out_hdlfree; - ret = mt9m111_video_probe(client); if (ret < 0) goto out_hdlfree; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index be5a7fd4f076..502c72238a4a 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -23,6 +23,7 @@ #include <linux/videodev2.h> #include <media/media-entity.h> +#include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -1520,6 +1521,10 @@ static int ov965x_probe(struct i2c_client *client, /* Update exposure time min/max to match frame format */ ov965x_update_exposure_ctrl(ov965x); + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto err_ctrls; + return 0; err_ctrls: v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1532,7 +1537,7 @@ static int ov965x_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - v4l2_device_unregister_subdev(sd); + v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); media_entity_cleanup(&sd->entity); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 08af58fb8e7d..3844853ab0a0 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1706,7 +1706,7 @@ static int s5c73m3_probe(struct i2c_client *client, state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; - oif_sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + oif_sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; ret = media_entity_pads_init(&oif_sd->entity, OIF_NUM_PADS, state->oif_pads); diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 8a0f22da590f..6ebcf254989a 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -1019,7 +1019,6 @@ MODULE_DEVICE_TABLE(i2c, s5k4ecgx_id); static struct i2c_driver v4l2_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = S5K4ECGX_DRIVER_NAME, }, .probe = s5k4ecgx_probe, diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index cbe4711e9b31..769964057881 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -331,6 +331,7 @@ static int s5k6a3_probe(struct i2c_client *client, sensor->format.width = S5K6A3_DEFAULT_WIDTH; sensor->format.height = S5K6A3_DEFAULT_HEIGHT; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) @@ -376,7 +377,6 @@ static struct i2c_driver s5k6a3_driver = { .driver = { .of_match_table = of_match_ptr(s5k6a3_of_match), .name = S5K6A3_DRV_NAME, - .owner = THIS_MODULE, }, .probe = s5k6a3_probe, .remove = s5k6a3_remove, diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index d08ab6c8357c..44f8c7e10a35 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -24,8 +24,8 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/smiapp.h> @@ -328,6 +328,14 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) * orders must be defined. */ static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { MEDIA_BUS_FMT_SGRBG16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_GBRG, }, { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, @@ -625,12 +633,12 @@ static int smiapp_init_late_controls(struct smiapp_sensor *sensor) 0, max_value, 1, max_value); } - for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + for (max = 0; sensor->hwcfg->op_sys_clock[max + 1]; max++); sensor->link_freq = v4l2_ctrl_new_int_menu( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), - __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); + __ffs(*valid_link_freqs), sensor->hwcfg->op_sys_clock); return sensor->src->ctrl_handler.error; } @@ -833,8 +841,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) pll->bits_per_pixel = f->compressed; - for (j = 0; sensor->platform_data->op_sys_clock[j]; j++) { - pll->link_freq = sensor->platform_data->op_sys_clock[j]; + for (j = 0; sensor->hwcfg->op_sys_clock[j]; j++) { + pll->link_freq = sensor->hwcfg->op_sys_clock[j]; rval = smiapp_pll_try(sensor, pll); dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n", @@ -1032,22 +1040,22 @@ static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) int rval; u32 val; - client->addr = sensor->platform_data->i2c_addr_dfl; + client->addr = sensor->hwcfg->i2c_addr_dfl; rval = smiapp_write(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, - sensor->platform_data->i2c_addr_alt << 1); + sensor->hwcfg->i2c_addr_alt << 1); if (rval) return rval; - client->addr = sensor->platform_data->i2c_addr_alt; + client->addr = sensor->hwcfg->i2c_addr_alt; /* verify addr change went ok */ rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); if (rval) return rval; - if (val != sensor->platform_data->i2c_addr_alt << 1) + if (val != sensor->hwcfg->i2c_addr_alt << 1) return -ENODEV; return 0; @@ -1061,13 +1069,13 @@ static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) { struct smiapp_flash_strobe_parms *strobe_setup; - unsigned int ext_freq = sensor->platform_data->ext_clk; + unsigned int ext_freq = sensor->hwcfg->ext_clk; u32 tmp; u32 strobe_adjustment; u32 strobe_width_high_rs; int rval; - strobe_setup = sensor->platform_data->strobe_setup; + strobe_setup = sensor->hwcfg->strobe_setup; /* * How to calculate registers related to strobe length. Please @@ -1179,7 +1187,7 @@ static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) strobe_setup->trigger); out: - sensor->platform_data->strobe_setup->trigger = 0; + sensor->hwcfg->strobe_setup->trigger = 0; return rval; } @@ -1201,21 +1209,16 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } usleep_range(1000, 1000); - if (sensor->platform_data->set_xclk) - rval = sensor->platform_data->set_xclk( - &sensor->src->sd, sensor->platform_data->ext_clk); - else - rval = clk_prepare_enable(sensor->ext_clk); + rval = clk_prepare_enable(sensor->ext_clk); if (rval < 0) { dev_dbg(&client->dev, "failed to enable xclk\n"); goto out_xclk_fail; } usleep_range(1000, 1000); - if (gpio_is_valid(sensor->platform_data->xshutdown)) - gpio_set_value(sensor->platform_data->xshutdown, 1); + gpiod_set_value(sensor->xshutdown, 1); - sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + sleep = SMIAPP_RESET_DELAY(sensor->hwcfg->ext_clk); usleep_range(sleep, sleep); /* @@ -1229,7 +1232,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) * is found. */ - if (sensor->platform_data->i2c_addr_alt) { + if (sensor->hwcfg->i2c_addr_alt) { rval = smiapp_change_cci_addr(sensor); if (rval) { dev_err(&client->dev, "cci address change error\n"); @@ -1244,7 +1247,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) goto out_cci_addr_fail; } - if (sensor->platform_data->i2c_addr_alt) { + if (sensor->hwcfg->i2c_addr_alt) { rval = smiapp_change_cci_addr(sensor); if (rval) { dev_err(&client->dev, "cci address change error\n"); @@ -1261,14 +1264,14 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) rval = smiapp_write( sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, - sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + sensor->hwcfg->ext_clk / (1000000 / (1 << 8))); if (rval) { dev_err(&client->dev, "extclk frequency set failed\n"); goto out_cci_addr_fail; } rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, - sensor->platform_data->lanes - 1); + sensor->hwcfg->lanes - 1); if (rval) { dev_err(&client->dev, "csi lane mode set failed\n"); goto out_cci_addr_fail; @@ -1282,7 +1285,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, - sensor->platform_data->csi_signalling_mode); + sensor->hwcfg->csi_signalling_mode); if (rval) { dev_err(&client->dev, "csi signalling mode set failed\n"); goto out_cci_addr_fail; @@ -1322,12 +1325,8 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) return 0; out_cci_addr_fail: - if (gpio_is_valid(sensor->platform_data->xshutdown)) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable_unprepare(sensor->ext_clk); + gpiod_set_value(sensor->xshutdown, 0); + clk_disable_unprepare(sensor->ext_clk); out_xclk_fail: regulator_disable(sensor->vana); @@ -1343,17 +1342,13 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) * really see a power off and next time the cci address change * will fail. So do a soft reset explicitly here. */ - if (sensor->platform_data->i2c_addr_alt) + if (sensor->hwcfg->i2c_addr_alt) smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, SMIAPP_SOFTWARE_RESET); - if (gpio_is_valid(sensor->platform_data->xshutdown)) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable_unprepare(sensor->ext_clk); + gpiod_set_value(sensor->xshutdown, 0); + clk_disable_unprepare(sensor->ext_clk); usleep_range(5000, 5000); regulator_disable(sensor->vana); sensor->streaming = false; @@ -1491,8 +1486,8 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if ((sensor->limits[SMIAPP_LIMIT_FLASH_MODE_CAPABILITY] & (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && - sensor->platform_data->strobe_setup != NULL && - sensor->platform_data->strobe_setup->trigger != 0) { + sensor->hwcfg->strobe_setup != NULL && + sensor->hwcfg->strobe_setup->trigger != 0) { rval = smiapp_setup_flash_strobe(sensor); if (rval) goto out; @@ -2309,7 +2304,7 @@ smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, if (!sensor->nvm_size) { /* NVM not read yet - read it now */ - sensor->nvm_size = sensor->platform_data->nvm_size; + sensor->nvm_size = sensor->hwcfg->nvm_size; if (smiapp_set_power(subdev, 1) < 0) return -ENODEV; if (smiapp_read_nvm(sensor, sensor->nvm)) { @@ -2554,35 +2549,27 @@ static int smiapp_init(struct smiapp_sensor *sensor) return PTR_ERR(sensor->vana); } - if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = devm_clk_get(&client->dev, NULL); - if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock\n"); - return PTR_ERR(sensor->ext_clk); - } - - rval = clk_set_rate(sensor->ext_clk, - sensor->platform_data->ext_clk); - if (rval < 0) { - dev_err(&client->dev, - "unable to set clock freq to %u\n", - sensor->platform_data->ext_clk); - return rval; - } + sensor->ext_clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock (%ld)\n", + PTR_ERR(sensor->ext_clk)); + return -EPROBE_DEFER; } - if (gpio_is_valid(sensor->platform_data->xshutdown)) { - rval = devm_gpio_request_one( - &client->dev, sensor->platform_data->xshutdown, 0, - "SMIA++ xshutdown"); - if (rval < 0) { - dev_err(&client->dev, - "unable to acquire reset gpio %d\n", - sensor->platform_data->xshutdown); - return rval; - } + rval = clk_set_rate(sensor->ext_clk, + sensor->hwcfg->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock freq to %u\n", + sensor->hwcfg->ext_clk); + return rval; } + sensor->xshutdown = devm_gpiod_get_optional(&client->dev, "xshutdown", + GPIOD_OUT_LOW); + if (IS_ERR(sensor->xshutdown)) + return PTR_ERR(sensor->xshutdown); + rval = smiapp_power_on(sensor); if (rval) return -ENODEV; @@ -2612,7 +2599,7 @@ static int smiapp_init(struct smiapp_sensor *sensor) * * Rotation also changes the bayer pattern. */ - if (sensor->platform_data->module_board_orient == + if (sensor->hwcfg->module_board_orient == SMIAPP_MODULE_BOARD_ORIENT_180) sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | SMIAPP_IMAGE_ORIENTATION_VFLIP; @@ -2661,9 +2648,9 @@ static int smiapp_init(struct smiapp_sensor *sensor) /* SMIA++ NVM initialization - it will be read from the sensor * when it is first requested by userspace. */ - if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + if (sensor->minfo.smiapp_version && sensor->hwcfg->nvm_size) { sensor->nvm = devm_kzalloc(&client->dev, - sensor->platform_data->nvm_size, GFP_KERNEL); + sensor->hwcfg->nvm_size, GFP_KERNEL); if (sensor->nvm == NULL) { dev_err(&client->dev, "nvm buf allocation failed\n"); rval = -ENOMEM; @@ -2706,8 +2693,8 @@ static int smiapp_init(struct smiapp_sensor *sensor) /* prepare PLL configuration input values */ pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; - pll->csi2.lanes = sensor->platform_data->lanes; - pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + pll->csi2.lanes = sensor->hwcfg->lanes; + pll->ext_clk_freq_hz = sensor->hwcfg->ext_clk; pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; /* Profile 0 sensors have no separate OP clock branch. */ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) @@ -2984,9 +2971,9 @@ static int smiapp_resume(struct device *dev) #endif /* CONFIG_PM */ -static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) +static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) { - struct smiapp_platform_data *pdata; + struct smiapp_hwconfig *hwcfg; struct v4l2_of_endpoint *bus_cfg; struct device_node *ep; int i; @@ -3003,58 +2990,55 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) if (IS_ERR(bus_cfg)) goto out_err; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) + hwcfg = devm_kzalloc(dev, sizeof(*hwcfg), GFP_KERNEL); + if (!hwcfg) goto out_err; switch (bus_cfg->bus_type) { case V4L2_MBUS_CSI2: - pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; + hwcfg->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; break; /* FIXME: add CCP2 support. */ default: goto out_err; } - pdata->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes; - dev_dbg(dev, "lanes %u\n", pdata->lanes); - - /* xshutdown GPIO is optional */ - pdata->xshutdown = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + hwcfg->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes; + dev_dbg(dev, "lanes %u\n", hwcfg->lanes); /* NVM size is not mandatory */ of_property_read_u32(dev->of_node, "nokia,nvm-size", - &pdata->nvm_size); + &hwcfg->nvm_size); rval = of_property_read_u32(dev->of_node, "clock-frequency", - &pdata->ext_clk); + &hwcfg->ext_clk); if (rval) { dev_warn(dev, "can't get clock-frequency\n"); goto out_err; } - dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown, - pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode); + dev_dbg(dev, "nvm %d, clk %d, csi %d\n", hwcfg->nvm_size, + hwcfg->ext_clk, hwcfg->csi_signalling_mode); if (!bus_cfg->nr_of_link_frequencies) { dev_warn(dev, "no link frequencies defined\n"); goto out_err; } - pdata->op_sys_clock = devm_kcalloc( + hwcfg->op_sys_clock = devm_kcalloc( dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */, - sizeof(*pdata->op_sys_clock), GFP_KERNEL); - if (!pdata->op_sys_clock) + sizeof(*hwcfg->op_sys_clock), GFP_KERNEL); + if (!hwcfg->op_sys_clock) goto out_err; for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) { - pdata->op_sys_clock[i] = bus_cfg->link_frequencies[i]; - dev_dbg(dev, "freq %d: %lld\n", i, pdata->op_sys_clock[i]); + hwcfg->op_sys_clock[i] = bus_cfg->link_frequencies[i]; + dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]); } v4l2_of_free_endpoint(bus_cfg); of_node_put(ep); - return pdata; + return hwcfg; out_err: v4l2_of_free_endpoint(bus_cfg); @@ -3066,17 +3050,17 @@ static int smiapp_probe(struct i2c_client *client, const struct i2c_device_id *devid) { struct smiapp_sensor *sensor; - struct smiapp_platform_data *pdata = smiapp_get_pdata(&client->dev); + struct smiapp_hwconfig *hwcfg = smiapp_get_hwconfig(&client->dev); int rval; - if (pdata == NULL) + if (hwcfg == NULL) return -ENODEV; sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (sensor == NULL) return -ENOMEM; - sensor->platform_data = pdata; + sensor->hwcfg = hwcfg; mutex_init(&sensor->mutex); mutex_init(&sensor->power_mutex); sensor->src = &sensor->ssds[sensor->ssds_used]; @@ -3119,12 +3103,8 @@ static int smiapp_remove(struct i2c_client *client) v4l2_async_unregister_subdev(subdev); if (sensor->power_count) { - if (gpio_is_valid(sensor->platform_data->xshutdown)) - gpio_set_value(sensor->platform_data->xshutdown, 0); - if (sensor->platform_data->set_xclk) - sensor->platform_data->set_xclk(&sensor->src->sd, 0); - else - clk_disable_unprepare(sensor->ext_clk); + gpiod_set_value(sensor->xshutdown, 0); + clk_disable_unprepare(sensor->ext_clk); sensor->power_count = 0; } diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index abf9ea7a0fb7..cb128eae9c54 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -26,7 +26,7 @@ static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) } static int smiapp_write_8s(struct smiapp_sensor *sensor, - struct smiapp_reg_8 *regs, int len) + const struct smiapp_reg_8 *regs, int len) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; @@ -71,7 +71,7 @@ static int jt8ew9_limits(struct smiapp_sensor *sensor) static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) { - struct smiapp_reg_8 regs[] = { + const struct smiapp_reg_8 regs[] = { { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ @@ -115,7 +115,7 @@ const struct smiapp_quirk smiapp_jt8ew9_quirk = { static int imx125es_post_poweron(struct smiapp_sensor *sensor) { /* Taken from v02. No idea what the other two are. */ - struct smiapp_reg_8 regs[] = { + const struct smiapp_reg_8 regs[] = { /* * 0x3302: clk during frame blanking: * 0x00 - HS mode, 0x01 - LP11 @@ -145,8 +145,7 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - - struct smiapp_reg_8 regs[] = { + const struct smiapp_reg_8 regs[] = { { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ @@ -167,8 +166,7 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) { 0x33cf, 0xec }, /* For Black sun */ { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ }; - - struct smiapp_reg_8 regs_96[] = { + const struct smiapp_reg_8 regs_96[] = { { 0x30ae, 0x00 }, /* For control of ADC clock */ { 0x30af, 0xd0 }, { 0x30b0, 0x01 }, @@ -178,13 +176,13 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) if (rval < 0) return rval; - switch (sensor->platform_data->ext_clk) { + switch (sensor->hwcfg->ext_clk) { case 9600000: return smiapp_write_8s(sensor, regs_96, ARRAY_SIZE(regs_96)); default: dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", - sensor->platform_data->ext_clk); + sensor->hwcfg->ext_clk); return 0; } } diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index 6b6c20b61397..1e501c06d18c 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -188,7 +188,8 @@ int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val) SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); } -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +static int smiapp_read_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val, + bool force8) { int rval; @@ -199,21 +200,20 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) if (rval < 0) return rval; + if (force8) + return __smiapp_read(sensor, reg, val, true); + return smiapp_read_no_quirk(sensor, reg, val); } -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) { - int rval; - - *val = 0; - rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); - if (rval == -ENOIOCTLCMD) - return 0; - if (rval < 0) - return rval; + return smiapp_read_quirk(sensor, reg, val, false); +} - return __smiapp_read(sensor, reg, val, true); +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return smiapp_read_quirk(sensor, reg, val, true); } int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 2174f89a00db..aae72bc87bf7 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -151,7 +151,7 @@ struct smiapp_csi_data_format { #define SMIAPP_PADS 2 #define SMIAPP_COMPRESSED_BASE 8 -#define SMIAPP_COMPRESSED_MAX 12 +#define SMIAPP_COMPRESSED_MAX 16 #define SMIAPP_NR_OF_COMPRESSED (SMIAPP_COMPRESSED_MAX - \ SMIAPP_COMPRESSED_BASE + 1) @@ -197,9 +197,10 @@ struct smiapp_sensor { struct smiapp_subdev *binner; struct smiapp_subdev *scaler; struct smiapp_subdev *pixel_array; - struct smiapp_platform_data *platform_data; + struct smiapp_hwconfig *hwcfg; struct regulator *vana; struct clk *ext_clk; + struct gpio_desc *xshutdown; u32 limits[SMIAPP_LIMIT_LAST]; u8 nbinning_subtypes; struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 23d352f0adf0..7704bcf5cc25 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -14,11 +14,14 @@ config SOC_CAMERA_MT9M001 and colour models. config SOC_CAMERA_MT9M111 - tristate "mt9m111, mt9m112 and mt9m131 support" + tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" depends on SOC_CAMERA && I2C + select VIDEO_MT9M111 help This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina + Micron/Aptina. + This is the legacy configuration which shouldn't be used anymore, + while VIDEO_MT9M111 should be used instead. config SOC_CAMERA_MT9T031 tristate "mt9t031 support" diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index d0421feaa796..6f994f9353a0 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index f68c2352c63c..05b55cfe8147 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -209,31 +209,26 @@ static int imx074_get_fmt(struct v4l2_subdev *sd, return 0; } -static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int imx074_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct v4l2_rect *rect = &a->c; - - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rect->top = 0; - rect->left = 0; - rect->width = IMX074_WIDTH; - rect->height = IMX074_HEIGHT; - - return 0; -} + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; -static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = IMX074_WIDTH; - a->bounds.height = IMX074_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX074_WIDTH; + sel->r.height = IMX074_HEIGHT; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + return 0; + default: + return -EINVAL; + } } static int imx074_enum_mbus_code(struct v4l2_subdev *sd, @@ -278,8 +273,6 @@ static int imx074_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .s_stream = imx074_s_stream, - .g_crop = imx074_g_crop, - .cropcap = imx074_cropcap, .g_mbus_config = imx074_g_mbus_config, }; @@ -289,6 +282,7 @@ static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { .enum_mbus_code = imx074_enum_mbus_code, + .get_selection = imx074_get_selection, .get_fmt = imx074_get_fmt, .set_fmt = imx074_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 69becc358659..3d6378d4491c 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -171,13 +171,19 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int mt9m001_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_rect rect = a->c; - int ret; + struct v4l2_rect rect = sel->r; const u16 hblank = 9, vblank = 25; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; if (mt9m001->fmts == mt9m001_colour_fmts) /* @@ -225,29 +231,30 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return ret; } -static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9m001_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); - a->c = mt9m001->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = MT9M001_COLUMN_SKIP; - a->bounds.top = MT9M001_ROW_SKIP; - a->bounds.width = MT9M001_MAX_WIDTH; - a->bounds.height = MT9M001_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = MT9M001_COLUMN_SKIP; + sel->r.top = MT9M001_ROW_SKIP; + sel->r.width = MT9M001_MAX_WIDTH; + sel->r.height = MT9M001_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9m001->rect; + return 0; + default: + return -EINVAL; + } } static int mt9m001_get_fmt(struct v4l2_subdev *sd, @@ -275,18 +282,18 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_crop a = { - .c = { - .left = mt9m001->rect.left, - .top = mt9m001->rect.top, - .width = mf->width, - .height = mf->height, - }, + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9m001->rect.left, + .r.top = mt9m001->rect.top, + .r.width = mf->width, + .r.height = mf->height, }; int ret; /* No support for scaling so far, just crop. TODO: use skipping */ - ret = mt9m001_s_crop(sd, &a); + ret = mt9m001_set_selection(sd, NULL, &sel); if (!ret) { mf->width = mt9m001->rect.width; mf->height = mt9m001->rect.height; @@ -625,9 +632,6 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, - .s_crop = mt9m001_s_crop, - .g_crop = mt9m001_g_crop, - .cropcap = mt9m001_cropcap, .g_mbus_config = mt9m001_g_mbus_config, .s_mbus_config = mt9m001_s_mbus_config, }; @@ -638,6 +642,8 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { .enum_mbus_code = mt9m001_enum_mbus_code, + .get_selection = mt9m001_get_selection, + .set_selection = mt9m001_set_selection, .get_fmt = mt9m001_get_fmt, .set_fmt = mt9m001_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 5c8e3ffe3b27..3aa5569065ad 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -264,7 +264,7 @@ static int mt9t031_set_params(struct i2c_client *client, /* * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_crop() and soc_camera_cropcap() + * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() */ if (ret >= 0) ret = reg_write(client, MT9T031_COLUMN_START, rect->left); @@ -294,11 +294,17 @@ static int mt9t031_set_params(struct i2c_client *client, return ret < 0 ? ret : 0; } -static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int mt9t031_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct v4l2_rect rect = a->c; struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); + struct v4l2_rect rect = sel->r; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; rect.width = ALIGN(rect.width, 2); rect.height = ALIGN(rect.height, 2); @@ -312,29 +318,30 @@ static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); } -static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t031_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); - a->c = mt9t031->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = MT9T031_COLUMN_SKIP; - a->bounds.top = MT9T031_ROW_SKIP; - a->bounds.width = MT9T031_MAX_WIDTH; - a->bounds.height = MT9T031_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = MT9T031_COLUMN_SKIP; + sel->r.top = MT9T031_ROW_SKIP; + sel->r.width = MT9T031_MAX_WIDTH; + sel->r.height = MT9T031_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9t031->rect; + return 0; + default: + return -EINVAL; + } } static int mt9t031_get_fmt(struct v4l2_subdev *sd, @@ -721,9 +728,6 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .s_stream = mt9t031_s_stream, - .s_crop = mt9t031_s_crop, - .g_crop = mt9t031_g_crop, - .cropcap = mt9t031_cropcap, .g_mbus_config = mt9t031_g_mbus_config, .s_mbus_config = mt9t031_s_mbus_config, }; @@ -734,6 +738,8 @@ static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { .enum_mbus_code = mt9t031_enum_mbus_code, + .get_selection = mt9t031_get_selection, + .set_selection = mt9t031_set_selection, .get_fmt = mt9t031_get_fmt, .set_fmt = mt9t031_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 6a1b2a9f9a09..2ef22241ec14 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -867,39 +867,48 @@ static int mt9t112_set_params(struct mt9t112_priv *priv, return 0; } -static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = MAX_WIDTH; - a->bounds.height = MAX_HEIGHT; - a->defrect.left = 0; - a->defrect.top = 0; - a->defrect.width = VGA_WIDTH; - a->defrect.height = VGA_HEIGHT; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9t112_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - a->c = priv->frame; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = MAX_WIDTH; + sel->r.height = MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = VGA_WIDTH; + sel->r.height = VGA_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->frame; + return 0; + default: + return -EINVAL; + } } -static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int mt9t112_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - const struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &sel->r; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; return mt9t112_set_params(priv, rect, priv->format->code); } @@ -1024,15 +1033,14 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { .s_stream = mt9t112_s_stream, - .cropcap = mt9t112_cropcap, - .g_crop = mt9t112_g_crop, - .s_crop = mt9t112_s_crop, .g_mbus_config = mt9t112_g_mbus_config, .s_mbus_config = mt9t112_s_mbus_config, }; static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { .enum_mbus_code = mt9t112_enum_mbus_code, + .get_selection = mt9t112_get_selection, + .set_selection = mt9t112_set_selection, .get_fmt = mt9t112_get_fmt, .set_fmt = mt9t112_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 2721e583bfa0..6a14ab5e4f2d 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -276,14 +276,20 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int mt9v022_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_rect rect = a->c; + struct v4l2_rect rect = sel->r; int min_row, min_blank; int ret; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Bayer format - even size lengths */ if (mt9v022->fmts == mt9v022_colour_fmts) { rect.width = ALIGN(rect.width, 2); @@ -350,29 +356,30 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return 0; } -static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int mt9v022_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); - a->c = mt9v022->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = MT9V022_COLUMN_SKIP; - a->bounds.top = MT9V022_ROW_SKIP; - a->bounds.width = MT9V022_MAX_WIDTH; - a->bounds.height = MT9V022_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = MT9V022_COLUMN_SKIP; + sel->r.top = MT9V022_ROW_SKIP; + sel->r.width = MT9V022_MAX_WIDTH; + sel->r.height = MT9V022_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9v022->rect; + return 0; + default: + return -EINVAL; + } } static int mt9v022_get_fmt(struct v4l2_subdev *sd, @@ -400,13 +407,13 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_crop a = { - .c = { - .left = mt9v022->rect.left, - .top = mt9v022->rect.top, - .width = mf->width, - .height = mf->height, - }, + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9v022->rect.left, + .r.top = mt9v022->rect.top, + .r.width = mf->width, + .r.height = mf->height, }; int ret; @@ -430,7 +437,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, } /* No support for scaling on this camera, just crop. */ - ret = mt9v022_s_crop(sd, &a); + ret = mt9v022_set_selection(sd, NULL, &sel); if (!ret) { mf->width = mt9v022->rect.width; mf->height = mt9v022->rect.height; @@ -853,9 +860,6 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { .s_stream = mt9v022_s_stream, - .s_crop = mt9v022_s_crop, - .g_crop = mt9v022_g_crop, - .cropcap = mt9v022_cropcap, .g_mbus_config = mt9v022_g_mbus_config, .s_mbus_config = mt9v022_s_mbus_config, }; @@ -866,6 +870,8 @@ static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { .enum_mbus_code = mt9v022_enum_mbus_code, + .get_selection = mt9v022_get_selection, + .set_selection = mt9v022_set_selection, .get_fmt = mt9v022_get_fmt, .set_fmt = mt9v022_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 9b4f5deec748..56de18263359 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -928,29 +928,25 @@ static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - a->c.left = 0; - a->c.top = 0; - a->c.width = UXGA_WIDTH; - a->c.height = UXGA_HEIGHT; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +static int ov2640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = UXGA_WIDTH; - a->bounds.height = UXGA_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = UXGA_WIDTH; + sel->r.height = UXGA_HEIGHT; + return 0; + default: + return -EINVAL; + } } static int ov2640_video_probe(struct i2c_client *client) @@ -1024,13 +1020,12 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { .s_stream = ov2640_s_stream, - .cropcap = ov2640_cropcap, - .g_crop = ov2640_g_crop, .g_mbus_config = ov2640_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { .enum_mbus_code = ov2640_enum_mbus_code, + .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, .set_fmt = ov2640_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index bab9ac0c1764..3d185bd622a3 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -850,13 +850,19 @@ static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int ov5642_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect rect = a->c; + struct v4l2_rect rect = sel->r; int ret; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); @@ -878,32 +884,30 @@ static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return ret; } -static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov5642_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect *rect = &a->c; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - *rect = priv->crop_rect; - - return 0; -} - -static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = OV5642_MAX_WIDTH; - a->bounds.height = OV5642_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV5642_MAX_WIDTH; + sel->r.height = OV5642_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->crop_rect; + return 0; + default: + return -EINVAL; + } } static int ov5642_g_mbus_config(struct v4l2_subdev *sd, @@ -940,14 +944,13 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on) } static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .s_crop = ov5642_s_crop, - .g_crop = ov5642_g_crop, - .cropcap = ov5642_cropcap, .g_mbus_config = ov5642_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { .enum_mbus_code = ov5642_enum_mbus_code, + .get_selection = ov5642_get_selection, + .set_selection = ov5642_set_selection, .get_fmt = ov5642_get_fmt, .set_fmt = ov5642_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 1f8af1ee8352..4bf2995e1cb8 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -432,25 +432,43 @@ static int ov6650_s_power(struct v4l2_subdev *sd, int on) return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); } -static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov6650_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->c = priv->rect; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = DEF_HSTRT << 1; + sel->r.top = DEF_VSTRT << 1; + sel->r.width = W_CIF; + sel->r.height = H_CIF; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->rect; + return 0; + default: + return -EINVAL; + } } -static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int ov6650_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect rect = a->c; + struct v4l2_rect rect = sel->r; int ret; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; rect.left = ALIGN(rect.left, 2); @@ -483,22 +501,6 @@ static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return ret; } -static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - a->bounds.left = DEF_HSTRT << 1; - a->bounds.top = DEF_VSTRT << 1; - a->bounds.width = W_CIF; - a->bounds.height = H_CIF; - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - static int ov6650_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -549,16 +551,15 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) struct soc_camera_sense *sense = icd->sense; struct ov6650 *priv = to_ov6650(client); bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect); - struct v4l2_crop a = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .c = { - .left = priv->rect.left + (priv->rect.width >> 1) - - (mf->width >> (1 - half_scale)), - .top = priv->rect.top + (priv->rect.height >> 1) - - (mf->height >> (1 - half_scale)), - .width = mf->width << half_scale, - .height = mf->height << half_scale, - }, + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = priv->rect.left + (priv->rect.width >> 1) - + (mf->width >> (1 - half_scale)), + .r.top = priv->rect.top + (priv->rect.height >> 1) - + (mf->height >> (1 - half_scale)), + .r.width = mf->width << half_scale, + .r.height = mf->height << half_scale, }; u32 code = mf->code; unsigned long mclk, pclk; @@ -672,7 +673,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", mclk / pclk, 10 * mclk % pclk / pclk); - ret = ov6650_s_crop(sd, &a); + ret = ov6650_set_selection(sd, NULL, &sel); if (!ret) ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); if (!ret) @@ -943,9 +944,6 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, - .cropcap = ov6650_cropcap, - .g_crop = ov6650_g_crop, - .s_crop = ov6650_s_crop, .g_parm = ov6650_g_parm, .s_parm = ov6650_s_parm, .g_mbus_config = ov6650_g_mbus_config, @@ -954,6 +952,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = { static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { .enum_mbus_code = ov6650_enum_mbus_code, + .get_selection = ov6650_get_selection, + .set_selection = ov6650_set_selection, .get_fmt = ov6650_get_fmt, .set_fmt = ov6650_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index a43410c1e254..7e68762b3a4b 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -851,29 +851,28 @@ ov772x_set_fmt_error: return ret; } -static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - a->c.left = 0; - a->c.top = 0; - a->c.width = VGA_WIDTH; - a->c.height = VGA_HEIGHT; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +static int ov772x_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = OV772X_MAX_WIDTH; - a->bounds.height = OV772X_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + sel->r.left = 0; + sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.width = OV772X_MAX_WIDTH; + sel->r.height = OV772X_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r.width = VGA_WIDTH; + sel->r.height = VGA_HEIGHT; + return 0; + default: + return -EINVAL; + } } static int ov772x_get_fmt(struct v4l2_subdev *sd, @@ -1030,13 +1029,12 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .s_stream = ov772x_s_stream, - .cropcap = ov772x_cropcap, - .g_crop = ov772x_g_crop, .g_mbus_config = ov772x_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { .enum_mbus_code = ov772x_enum_mbus_code, + .get_selection = ov772x_get_selection, .get_fmt = ov772x_get_fmt, .set_fmt = ov772x_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 8caae1c07541..8c93c57af71c 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -561,29 +561,25 @@ static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - a->c.left = 0; - a->c.top = 0; - a->c.width = W_SXGA; - a->c.height = H_SXGA; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +static int ov9640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = W_SXGA; - a->bounds.height = H_SXGA; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + sel->r.left = 0; + sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.width = W_SXGA; + sel->r.height = H_SXGA; + return 0; + default: + return -EINVAL; + } } static int ov9640_video_probe(struct i2c_client *client) @@ -667,13 +663,12 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov9640_video_ops = { .s_stream = ov9640_s_stream, - .cropcap = ov9640_cropcap, - .g_crop = ov9640_g_crop, .g_mbus_config = ov9640_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { .enum_mbus_code = ov9640_enum_mbus_code, + .get_selection = ov9640_get_selection, .set_fmt = ov9640_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 03a7fc7316ae..0da632d7d33a 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -737,29 +737,25 @@ static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = OV9740_MAX_WIDTH; - a->bounds.height = OV9740_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - -static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int ov9740_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - a->c.left = 0; - a->c.top = 0; - a->c.width = OV9740_MAX_WIDTH; - a->c.height = OV9740_MAX_HEIGHT; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV9740_MAX_WIDTH; + sel->r.height = OV9740_MAX_HEIGHT; + return 0; + default: + return -EINVAL; + } } /* Set status of additional camera capabilities */ @@ -914,8 +910,6 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops ov9740_video_ops = { .s_stream = ov9740_s_stream, - .cropcap = ov9740_cropcap, - .g_crop = ov9740_g_crop, .g_mbus_config = ov9740_g_mbus_config, }; @@ -929,6 +923,7 @@ static struct v4l2_subdev_core_ops ov9740_core_ops = { static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { .enum_mbus_code = ov9740_enum_mbus_code, + .get_selection = ov9740_get_selection, .set_fmt = ov9740_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index aa7bfbb4ad71..bc8ec59a3fbd 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -538,15 +538,21 @@ static int rj54n1_commit(struct i2c_client *client) static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, s32 *out_w, s32 *out_h); -static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int rj54n1_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &sel->r; int dummy = 0, output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* arbitrary minimum width and height, edges unimportant */ soc_camera_limit_side(&dummy, &input_w, RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); @@ -573,29 +579,30 @@ static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return 0; } -static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int rj54n1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); - a->c = rj54n1->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - a->bounds.left = RJ54N1_COLUMN_SKIP; - a->bounds.top = RJ54N1_ROW_SKIP; - a->bounds.width = RJ54N1_MAX_WIDTH; - a->bounds.height = RJ54N1_MAX_HEIGHT; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = RJ54N1_COLUMN_SKIP; + sel->r.top = RJ54N1_ROW_SKIP; + sel->r.width = RJ54N1_MAX_WIDTH; + sel->r.height = RJ54N1_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = rj54n1->rect; + return 0; + default: + return -EINVAL; + } } static int rj54n1_get_fmt(struct v4l2_subdev *sd, @@ -1246,15 +1253,14 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_stream = rj54n1_s_stream, - .g_crop = rj54n1_g_crop, - .s_crop = rj54n1_s_crop, - .cropcap = rj54n1_cropcap, .g_mbus_config = rj54n1_g_mbus_config, .s_mbus_config = rj54n1_s_mbus_config, }; static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { .enum_mbus_code = rj54n1_enum_mbus_code, + .get_selection = rj54n1_get_selection, + .set_selection = rj54n1_set_selection, .get_fmt = rj54n1_get_fmt, .set_fmt = rj54n1_set_fmt, }; diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 06aff81787a7..4002c07f3857 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -676,44 +676,28 @@ tw9910_set_fmt_error: return ret; } -static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int tw9910_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); - a->c.left = 0; - a->c.top = 0; - if (priv->norm & V4L2_STD_NTSC) { - a->c.width = 640; - a->c.height = 480; - } else { - a->c.width = 768; - a->c.height = 576; - } - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */ + if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) + return -EINVAL; - a->bounds.left = 0; - a->bounds.top = 0; + sel->r.left = 0; + sel->r.top = 0; if (priv->norm & V4L2_STD_NTSC) { - a->bounds.width = 640; - a->bounds.height = 480; + sel->r.width = 640; + sel->r.height = 480; } else { - a->bounds.width = 768; - a->bounds.height = 576; + sel->r.width = 768; + sel->r.height = 576; } - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - return 0; } @@ -921,8 +905,6 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .s_std = tw9910_s_std, .g_std = tw9910_g_std, .s_stream = tw9910_s_stream, - .cropcap = tw9910_cropcap, - .g_crop = tw9910_g_crop, .g_mbus_config = tw9910_g_mbus_config, .s_mbus_config = tw9910_s_mbus_config, .g_tvnorms = tw9910_g_tvnorms, @@ -930,6 +912,7 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { .enum_mbus_code = tw9910_enum_mbus_code, + .get_selection = tw9910_get_selection, .get_fmt = tw9910_get_fmt, .set_fmt = tw9910_set_fmt, }; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 73fc42bc2de6..42340e364cea 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -499,7 +499,6 @@ MODULE_DEVICE_TABLE(of, ths8200_of_match); static struct i2c_driver ths8200_driver = { .driver = { - .owner = THIS_MODULE, .name = "ths8200", .of_match_table = of_match_ptr(ths8200_of_match), }, diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index 0370dd89f1fc..2e06c06cac9b 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -210,7 +210,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); static struct i2c_driver tlv320aic23b_driver = { .driver = { - .owner = THIS_MODULE, .name = "tlv320aic23b", }, .probe = tlv320aic23b_probe, diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 7cdd94842938..d5c9347f4c6d 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -977,7 +977,7 @@ static const struct v4l2_subdev_ops tvp514x_ops = { .pad = &tvp514x_pad_ops, }; -static struct tvp514x_decoder tvp514x_dev = { +static const struct tvp514x_decoder tvp514x_dev = { .streaming = 0, .fmt_list = tvp514x_fmt_list, .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), @@ -1233,7 +1233,6 @@ MODULE_DEVICE_TABLE(of, tvp514x_of_match); static struct i2c_driver tvp514x_driver = { .driver = { .of_match_table = of_match_ptr(tvp514x_of_match), - .owner = THIS_MODULE, .name = TVP514X_MODULE_NAME, }, .probe = tvp514x_probe, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 0b6d46c453bf..4740da39d698 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -871,19 +871,22 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, return 0; } -static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int tvp5150_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct v4l2_rect rect = a->c; struct tvp5150 *decoder = to_tvp5150(sd); + struct v4l2_rect rect = sel->r; v4l2_std_id std; - unsigned int hmax; + int hmax; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", __func__, rect.left, rect.top, rect.width, rect.height); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - /* tvp5150 has some special limits */ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); rect.width = clamp_t(unsigned int, rect.width, @@ -924,44 +927,39 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) return 0; } -static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) -{ - struct tvp5150 *decoder = to_tvp5150(sd); - - a->c = decoder->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +static int tvp5150_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { - struct tvp5150 *decoder = to_tvp5150(sd); + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); v4l2_std_id std; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = TVP5150_H_MAX; - - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - - if (std & V4L2_STD_525_60) - a->bounds.height = TVP5150_V_MAX_525_60; - else - a->bounds.height = TVP5150_V_MAX_OTHERS; - - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + if (std & V4L2_STD_525_60) + sel->r.height = TVP5150_V_MAX_525_60; + else + sel->r.height = TVP5150_V_MAX_OTHERS; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = decoder->rect; + return 0; + default: + return -EINVAL; + } } static int tvp5150_g_mbus_config(struct v4l2_subdev *sd, @@ -1173,7 +1171,7 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int tvp5150_registered_async(struct v4l2_subdev *sd) +static int tvp5150_registered(struct v4l2_subdev *sd) { #ifdef CONFIG_MEDIA_CONTROLLER struct tvp5150 *decoder = to_tvp5150(sd); @@ -1222,7 +1220,6 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .g_register = tvp5150_g_register, .s_register = tvp5150_s_register, #endif - .registered_async = tvp5150_registered_async, }; static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { @@ -1233,9 +1230,6 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_std = tvp5150_s_std, .s_stream = tvp5150_s_stream, .s_routing = tvp5150_s_routing, - .s_crop = tvp5150_s_crop, - .g_crop = tvp5150_g_crop, - .cropcap = tvp5150_cropcap, .g_mbus_config = tvp5150_g_mbus_config, }; @@ -1251,6 +1245,8 @@ static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { .enum_frame_size = tvp5150_enum_frame_size, .set_fmt = tvp5150_fill_fmt, .get_fmt = tvp5150_fill_fmt, + .get_selection = tvp5150_get_selection, + .set_selection = tvp5150_set_selection, }; static const struct v4l2_subdev_ops tvp5150_ops = { @@ -1261,6 +1257,10 @@ static const struct v4l2_subdev_ops tvp5150_ops = { .pad = &tvp5150_pad_ops, }; +static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { + .registered = tvp5150_registered, +}; + /**************************************************************************** I2C Client & Driver @@ -1474,6 +1474,7 @@ static int tvp5150_probe(struct i2c_client *c, } v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + sd->internal_ops = &tvp5150_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; #if defined(CONFIG_MEDIA_CONTROLLER) diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 4df640c3aa40..3dc3341c4896 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1086,7 +1086,6 @@ MODULE_DEVICE_TABLE(of, tvp7002_of_match); static struct i2c_driver tvp7002_driver = { .driver = { .of_match_table = of_match_ptr(tvp7002_of_match), - .owner = THIS_MODULE, .name = TVP7002_MODULE_NAME, }, .probe = tvp7002_probe, diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 4c72a18c0b8c..be4cb7a8bdeb 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -863,7 +863,6 @@ MODULE_DEVICE_TABLE(i2c, vs6624_id); static struct i2c_driver vs6624_driver = { .driver = { - .owner = THIS_MODULE, .name = "vs6624", }, .probe = vs6624_probe, diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 1795abeda658..2783531f9fc0 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -59,27 +59,24 @@ static int media_device_close(struct file *filp) } static int media_device_get_info(struct media_device *dev, - struct media_device_info __user *__info) + struct media_device_info *info) { - struct media_device_info info; - - memset(&info, 0, sizeof(info)); + memset(info, 0, sizeof(*info)); if (dev->driver_name[0]) - strlcpy(info.driver, dev->driver_name, sizeof(info.driver)); + strlcpy(info->driver, dev->driver_name, sizeof(info->driver)); else - strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver)); + strlcpy(info->driver, dev->dev->driver->name, + sizeof(info->driver)); - strlcpy(info.model, dev->model, sizeof(info.model)); - strlcpy(info.serial, dev->serial, sizeof(info.serial)); - strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info)); + strlcpy(info->model, dev->model, sizeof(info->model)); + strlcpy(info->serial, dev->serial, sizeof(info->serial)); + strlcpy(info->bus_info, dev->bus_info, sizeof(info->bus_info)); - info.media_version = MEDIA_API_VERSION; - info.hw_revision = dev->hw_revision; - info.driver_version = dev->driver_version; + info->media_version = MEDIA_API_VERSION; + info->hw_revision = dev->hw_revision; + info->driver_version = dev->driver_version; - if (copy_to_user(__info, &info, sizeof(*__info))) - return -EFAULT; return 0; } @@ -101,29 +98,25 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) } static long media_device_enum_entities(struct media_device *mdev, - struct media_entity_desc __user *uent) + struct media_entity_desc *entd) { struct media_entity *ent; - struct media_entity_desc u_ent; - - memset(&u_ent, 0, sizeof(u_ent)); - if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id))) - return -EFAULT; - - ent = find_entity(mdev, u_ent.id); + ent = find_entity(mdev, entd->id); if (ent == NULL) return -EINVAL; - u_ent.id = media_entity_id(ent); + memset(entd, 0, sizeof(*entd)); + + entd->id = media_entity_id(ent); if (ent->name) - strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); - u_ent.type = ent->function; - u_ent.revision = 0; /* Unused */ - u_ent.flags = ent->flags; - u_ent.group_id = 0; /* Unused */ - u_ent.pads = ent->num_pads; - u_ent.links = ent->num_links - ent->num_backlinks; + strlcpy(entd->name, ent->name, sizeof(entd->name)); + entd->type = ent->function; + entd->revision = 0; /* Unused */ + entd->flags = ent->flags; + entd->group_id = 0; /* Unused */ + entd->pads = ent->num_pads; + entd->links = ent->num_links - ent->num_backlinks; /* * Workaround for a bug at media-ctl <= v1.10 that makes it to @@ -139,14 +132,13 @@ static long media_device_enum_entities(struct media_device *mdev, if (ent->function < MEDIA_ENT_F_OLD_BASE || ent->function > MEDIA_ENT_T_DEVNODE_UNKNOWN) { if (is_media_entity_v4l2_subdev(ent)) - u_ent.type = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + entd->type = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; else if (ent->function != MEDIA_ENT_F_IO_V4L) - u_ent.type = MEDIA_ENT_T_DEVNODE_UNKNOWN; + entd->type = MEDIA_ENT_T_DEVNODE_UNKNOWN; } - memcpy(&u_ent.raw, &ent->info, sizeof(ent->info)); - if (copy_to_user(uent, &u_ent, sizeof(u_ent))) - return -EFAULT; + memcpy(&entd->raw, &ent->info, sizeof(ent->info)); + return 0; } @@ -158,8 +150,8 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, upad->flags = kpad->flags; } -static long __media_device_enum_links(struct media_device *mdev, - struct media_links_enum *links) +static long media_device_enum_links(struct media_device *mdev, + struct media_links_enum *links) { struct media_entity *entity; @@ -206,64 +198,35 @@ static long __media_device_enum_links(struct media_device *mdev, return 0; } -static long media_device_enum_links(struct media_device *mdev, - struct media_links_enum __user *ulinks) -{ - struct media_links_enum links; - int rval; - - if (copy_from_user(&links, ulinks, sizeof(links))) - return -EFAULT; - - rval = __media_device_enum_links(mdev, &links); - if (rval < 0) - return rval; - - if (copy_to_user(ulinks, &links, sizeof(*ulinks))) - return -EFAULT; - - return 0; -} - static long media_device_setup_link(struct media_device *mdev, - struct media_link_desc __user *_ulink) + struct media_link_desc *linkd) { struct media_link *link = NULL; - struct media_link_desc ulink; struct media_entity *source; struct media_entity *sink; - int ret; - - if (copy_from_user(&ulink, _ulink, sizeof(ulink))) - return -EFAULT; /* Find the source and sink entities and link. */ - source = find_entity(mdev, ulink.source.entity); - sink = find_entity(mdev, ulink.sink.entity); + source = find_entity(mdev, linkd->source.entity); + sink = find_entity(mdev, linkd->sink.entity); if (source == NULL || sink == NULL) return -EINVAL; - if (ulink.source.index >= source->num_pads || - ulink.sink.index >= sink->num_pads) + if (linkd->source.index >= source->num_pads || + linkd->sink.index >= sink->num_pads) return -EINVAL; - link = media_entity_find_link(&source->pads[ulink.source.index], - &sink->pads[ulink.sink.index]); + link = media_entity_find_link(&source->pads[linkd->source.index], + &sink->pads[linkd->sink.index]); if (link == NULL) return -EINVAL; /* Setup the link on both entities. */ - ret = __media_entity_setup_link(link, ulink.flags); - - if (copy_to_user(_ulink, &ulink, sizeof(ulink))) - return -EFAULT; - - return ret; + return __media_entity_setup_link(link, linkd->flags); } -static long __media_device_get_topology(struct media_device *mdev, +static long media_device_get_topology(struct media_device *mdev, struct media_v2_topology *topo) { struct media_entity *entity; @@ -400,63 +363,98 @@ static long __media_device_get_topology(struct media_device *mdev, return ret; } -static long media_device_get_topology(struct media_device *mdev, - struct media_v2_topology __user *utopo) +static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) { - struct media_v2_topology ktopo; - int ret; - - if (copy_from_user(&ktopo, utopo, sizeof(ktopo))) + /* All media IOCTLs are _IOWR() */ + if (copy_from_user(karg, uarg, _IOC_SIZE(cmd))) return -EFAULT; - ret = __media_device_get_topology(mdev, &ktopo); - if (ret < 0) - return ret; + return 0; +} - if (copy_to_user(utopo, &ktopo, sizeof(*utopo))) +static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) +{ + /* All media IOCTLs are _IOWR() */ + if (copy_to_user(uarg, karg, _IOC_SIZE(cmd))) return -EFAULT; return 0; } +/* Do acquire the graph mutex */ +#define MEDIA_IOC_FL_GRAPH_MUTEX BIT(0) + +#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \ + [_IOC_NR(MEDIA_IOC_##__cmd)] = { \ + .cmd = MEDIA_IOC_##__cmd, \ + .fn = (long (*)(struct media_device *, void *))func, \ + .flags = fl, \ + .arg_from_user = from_user, \ + .arg_to_user = to_user, \ + } + +#define MEDIA_IOC(__cmd, func, fl) \ + MEDIA_IOC_ARG(__cmd, func, fl, copy_arg_from_user, copy_arg_to_user) + +/* the table is indexed by _IOC_NR(cmd) */ +struct media_ioctl_info { + unsigned int cmd; + unsigned short flags; + long (*fn)(struct media_device *dev, void *arg); + long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd); + long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd); +}; + +static const struct media_ioctl_info ioctl_info[] = { + MEDIA_IOC(DEVICE_INFO, media_device_get_info, MEDIA_IOC_FL_GRAPH_MUTEX), + MEDIA_IOC(ENUM_ENTITIES, media_device_enum_entities, MEDIA_IOC_FL_GRAPH_MUTEX), + MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX), + MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX), + MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX), +}; + static long media_device_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) + unsigned long __arg) { struct media_devnode *devnode = media_devnode_data(filp); struct media_device *dev = devnode->media_dev; + const struct media_ioctl_info *info; + void __user *arg = (void __user *)__arg; + char __karg[256], *karg = __karg; long ret; - mutex_lock(&dev->graph_mutex); - switch (cmd) { - case MEDIA_IOC_DEVICE_INFO: - ret = media_device_get_info(dev, - (struct media_device_info __user *)arg); - break; + if (_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_info) + || ioctl_info[_IOC_NR(cmd)].cmd != cmd) + return -ENOIOCTLCMD; - case MEDIA_IOC_ENUM_ENTITIES: - ret = media_device_enum_entities(dev, - (struct media_entity_desc __user *)arg); - break; + info = &ioctl_info[_IOC_NR(cmd)]; - case MEDIA_IOC_ENUM_LINKS: - ret = media_device_enum_links(dev, - (struct media_links_enum __user *)arg); - break; + if (_IOC_SIZE(info->cmd) > sizeof(__karg)) { + karg = kmalloc(_IOC_SIZE(info->cmd), GFP_KERNEL); + if (!karg) + return -ENOMEM; + } - case MEDIA_IOC_SETUP_LINK: - ret = media_device_setup_link(dev, - (struct media_link_desc __user *)arg); - break; + if (info->arg_from_user) { + ret = info->arg_from_user(karg, arg, cmd); + if (ret) + goto out_free; + } - case MEDIA_IOC_G_TOPOLOGY: - ret = media_device_get_topology(dev, - (struct media_v2_topology __user *)arg); - break; + if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) + mutex_lock(&dev->graph_mutex); - default: - ret = -ENOIOCTLCMD; - } - mutex_unlock(&dev->graph_mutex); + ret = info->fn(dev, karg); + + if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) + mutex_unlock(&dev->graph_mutex); + + if (!ret && info->arg_to_user) + ret = info->arg_to_user(arg, karg, cmd); + +out_free: + if (karg != __karg) + kfree(karg); return ret; } @@ -486,7 +484,7 @@ static long media_device_enum_links32(struct media_device *mdev, links.pads = compat_ptr(pads_ptr); links.links = compat_ptr(links_ptr); - return __media_device_enum_links(mdev, &links); + return media_device_enum_links(mdev, &links); } #define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index d8a2299f0c2a..c68239e60487 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -65,6 +65,8 @@ static inline const char *intf_type(struct media_interface *intf) return "v4l-subdev"; case MEDIA_INTF_T_V4L_SWRADIO: return "v4l-swradio"; + case MEDIA_INTF_T_V4L_TOUCH: + return "v4l-touch"; case MEDIA_INTF_T_ALSA_PCM_CAPTURE: return "alsa-pcm-capture"; case MEDIA_INTF_T_ALSA_PCM_PLAYBACK: @@ -806,17 +808,18 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) mdev = source->graph_obj.mdev; - if (mdev->link_notify) { - ret = mdev->link_notify(link, flags, - MEDIA_DEV_NOTIFY_PRE_LINK_CH); + if (mdev->ops && mdev->ops->link_notify) { + ret = mdev->ops->link_notify(link, flags, + MEDIA_DEV_NOTIFY_PRE_LINK_CH); if (ret < 0) return ret; } ret = __media_entity_setup_link_notify(link, flags); - if (mdev->link_notify) - mdev->link_notify(link, flags, MEDIA_DEV_NOTIFY_POST_LINK_CH); + if (mdev->ops && mdev->ops->link_notify) + mdev->ops->link_notify(link, flags, + MEDIA_DEV_NOTIFY_POST_LINK_CH); return ret; } diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 4f6467fbaeb4..da28e68c87d8 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -13,6 +13,7 @@ if MEDIA_CAMERA_SUPPORT source "drivers/media/pci/meye/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" +source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" source "drivers/media/pci/zoran/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 2e54c36441f7..a7e8af0f64a7 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_COBALT) += cobalt/ +obj-$(CONFIG_VIDEO_TW5864) += tw5864/ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index df54e17ef864..97b91a9f9fa9 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2804,30 +2804,44 @@ static int bttv_cropcap(struct file *file, void *priv, cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - *cap = bttv_tvnorms[btv->tvnorm].cropcap; + /* defrect and bounds are set via g_selection */ + cap->pixelaspect = bttv_tvnorms[btv->tvnorm].cropcap.pixelaspect; return 0; } -static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int bttv_g_selection(struct file *file, void *f, struct v4l2_selection *sel) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - /* No fh->do_crop = 1; because btv->crop[1] may be - inconsistent with fh->width or fh->height and apps - do not expect a change here. */ - - crop->c = btv->crop[!!fh->do_crop].rect; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + /* + * No fh->do_crop = 1; because btv->crop[1] may be + * inconsistent with fh->width or fh->height and apps + * do not expect a change here. + */ + sel->r = btv->crop[!!fh->do_crop].rect; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = bttv_tvnorms[btv->tvnorm].cropcap.defrect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = bttv_tvnorms[btv->tvnorm].cropcap.bounds; + break; + default: + return -EINVAL; + } return 0; } -static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) +static int bttv_s_selection(struct file *file, void *f, struct v4l2_selection *sel) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -2839,8 +2853,11 @@ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) __s32 b_right; __s32 b_bottom; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; /* Make sure tvnorm, vbi_end and the current cropping @@ -2864,22 +2881,24 @@ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) } /* Min. scaled size 48 x 32. */ - c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48); + c.rect.left = clamp_t(s32, sel->r.left, b_left, b_right - 48); c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); - c.rect.width = clamp_t(s32, crop->c.width, + c.rect.width = clamp_t(s32, sel->r.width, 48, b_right - c.rect.left); - c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32); + c.rect.top = clamp_t(s32, sel->r.top, b_top, b_bottom - 32); /* Top and height must be a multiple of two. */ c.rect.top = (c.rect.top + 1) & ~1; - c.rect.height = clamp_t(s32, crop->c.height, + c.rect.height = clamp_t(s32, sel->r.height, 32, b_bottom - c.rect.top); c.rect.height = (c.rect.height + 1) & ~1; bttv_crop_calc_limits(&c); + sel->r = c.rect; + btv->crop[1] = c; fh->do_crop = 1; @@ -3047,10 +3066,10 @@ static int bttv_open(struct file *file) which only change on request. These are stored in btv->crop[1]. However for compatibility with V4L apps and cropping unaware V4L2 apps we now reset the cropping parameters as seen through - this fh, which is to say VIDIOC_G_CROP and scaling limit checks + this fh, which is to say VIDIOC_G_SELECTION and scaling limit checks will use btv->crop[0], the default cropping parameters for the current video standard, and VIDIOC_S_FMT will not implicitely - change the cropping parameters until VIDIOC_S_CROP has been + change the cropping parameters until VIDIOC_S_SELECTION has been called. */ fh->do_crop = !reset_crop; /* module parameter */ @@ -3159,8 +3178,8 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_streamoff = bttv_streamoff, .vidioc_g_tuner = bttv_g_tuner, .vidioc_s_tuner = bttv_s_tuner, - .vidioc_g_crop = bttv_g_crop, - .vidioc_s_crop = bttv_s_crop, + .vidioc_g_selection = bttv_g_selection, + .vidioc_s_selection = bttv_s_selection, .vidioc_g_fbuf = bttv_g_fbuf, .vidioc_s_fbuf = bttv_s_fbuf, .vidioc_overlay = bttv_overlay, diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index b1e0023f923c..9efc4559fa8e 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -232,7 +232,7 @@ struct bttv_fh { const struct bttv_format *ovfmt; struct bttv_overlay ov; - /* Application called VIDIOC_S_CROP. */ + /* Application called VIDIOC_S_SELECTION. */ int do_crop; /* vbi capture */ diff --git a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c index f0bdf10cfd57..49013c6b8646 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c +++ b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c @@ -510,7 +510,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, return vmalloc_to_page(pageptr); } -static struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { +static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { .open = snd_cobalt_pcm_capture_open, .close = snd_cobalt_pcm_capture_close, .ioctl = snd_cobalt_pcm_ioctl, @@ -522,7 +522,7 @@ static struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { .page = snd_pcm_get_vmalloc_page, }; -static struct snd_pcm_ops snd_cobalt_pcm_playback_ops = { +static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = { .open = snd_cobalt_pcm_playback_open, .close = snd_cobalt_pcm_playback_close, .ioctl = snd_cobalt_pcm_ioctl, diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 476f7f0dcf81..979634000597 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -60,30 +60,31 @@ MODULE_DESCRIPTION("cobalt driver"); MODULE_LICENSE("GPL"); static u8 edid[256] = { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, - 0x50, 0x21, 0x9C, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x12, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, - 0x0E, 0x00, 0xB2, 0xA0, 0x57, 0x49, 0x9B, 0x26, - 0x10, 0x48, 0x4F, 0x2F, 0xCF, 0x00, 0x31, 0x59, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x50, 0x21, 0x32, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x1a, 0x01, 0x03, 0x80, 0x30, 0x1b, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x81, 0x99, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, - 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, - 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, - 0x00, 0x00, 0x00, 0xFD, 0x00, 0x31, 0x55, 0x18, - 0x5E, 0x11, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x43, - 0x20, 0x39, 0x30, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, - 0x0A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x10, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x46, 0x00, 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x63, + 0x6f, 0x62, 0x61, 0x6c, 0x74, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9c, + + 0x02, 0x03, 0x1f, 0xf0, 0x4a, 0x90, 0x1f, 0x04, + 0x13, 0x22, 0x21, 0x20, 0x02, 0x11, 0x01, 0x23, + 0x09, 0x07, 0x07, 0x68, 0x03, 0x0c, 0x00, 0x10, + 0x00, 0x00, 0x22, 0x0f, 0xe2, 0x00, 0xea, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, - 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, - 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, - 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, - 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, - 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, - 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -91,7 +92,7 @@ static u8 edid[256] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, }; static void cobalt_set_interrupt(struct cobalt *cobalt, bool enable) diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index d05672fe9ff9..5c76637900d0 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -161,8 +161,11 @@ static void cobalt_enable_output(struct cobalt_stream *s) struct v4l2_subdev_format sd_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; + u64 clk = bt->pixelclock; - if (!cobalt_cpld_set_freq(cobalt, bt->pixelclock)) { + if (bt->flags & V4L2_DV_FL_REDUCED_FPS) + clk = div_u64(clk * 1000ULL, 1001); + if (!cobalt_cpld_set_freq(cobalt, clk)) { cobalt_err("pixelclock out of range\n"); return; } @@ -644,7 +647,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh, return 0; } - if (v4l2_match_dv_timings(timings, &s->timings, 0, false)) + if (v4l2_match_dv_timings(timings, &s->timings, 0, true)) return 0; if (vb2_is_busy(&s->q)) diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c index ffb6acdc575f..5344510fbea3 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.c +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c @@ -311,7 +311,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, return vmalloc_to_page(pageptr); } -static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { +static const struct snd_pcm_ops snd_cx18_pcm_capture_ops = { .open = snd_cx18_pcm_capture_open, .close = snd_cx18_pcm_capture_close, .ioctl = snd_cx18_pcm_ioctl, diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index 4af8cd6df95d..c9329371a3f8 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -98,7 +98,8 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, case CX18_HW_Z8F0811_IR_RX_HAUP: init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_BIT_RC5; + init_data->type = RC_BIT_RC5 | RC_BIT_RC6_MCE | + RC_BIT_RC6_6A_32; init_data->name = cx->card_name; info.platform_data = init_data; break; diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 4d080da7afaf..da892f3e3c29 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1223,7 +1223,7 @@ static void cx23885_stop_streaming(struct vb2_queue *q) cx23885_cancel_buffers(&dev->ts1); } -static struct vb2_ops cx23885_qops = { +static const struct vb2_ops cx23885_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index ae7c2e89ad1c..6115d4e148ba 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -506,7 +506,7 @@ static struct page *snd_cx23885_page(struct snd_pcm_substream *substream, /* * operators */ -static struct snd_pcm_ops snd_cx23885_pcm_ops = { +static const struct snd_pcm_ops snd_cx23885_pcm_ops = { .open = snd_cx23885_pcm_open, .close = snd_cx23885_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 4abf50f2694f..99ba8d6328f0 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -770,6 +770,11 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC] = { + .name = "Hauppauge WinTV-QuadHD-ATSC", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -1073,6 +1078,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x6b28, .card = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB, /* Tuner Pair 2 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x6a18, + .card = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC, /* Tuner Pair 1 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x6b18, + .card = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC, /* Tuner Pair 2 */ }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1278,6 +1291,18 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height, DVB-T/T2/C, DVB-T/T2/C */ break; + case 165100: + /* + * WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height, + * ATSC, ATSC + */ + break; + case 165101: + /* + * WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height, + * ATSC, ATSC + */ + break; default: printk(KERN_WARNING "%s: warning: " "unknown hauppauge model #%d\n", @@ -1751,6 +1776,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_HAUPPAUGE_HVR5525: case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: /* * HVR5525 GPIO Details: * GPIO-00 IR_WIDE @@ -1826,6 +1852,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: /* FIXME: Implement me */ break; case CX23885_BOARD_HAUPPAUGE_HVR1270: @@ -2025,6 +2052,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: case CX23885_BOARD_HAUPPAUGE_HVR5525: case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -2171,6 +2199,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB: + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index e5748a93c479..818f3c2fc98d 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -74,6 +74,7 @@ #include "sp2.h" #include "m88ds3103.h" #include "m88rs6000t.h" +#include "lgdt3306a.h" static unsigned int debug; @@ -172,7 +173,7 @@ static void cx23885_stop_streaming(struct vb2_queue *q) cx23885_cancel_buffers(port); } -static struct vb2_ops dvb_qops = { +static const struct vb2_ops dvb_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, @@ -574,6 +575,30 @@ static struct stb6100_config prof_8000_stb6100_config = { .refclock = 27000000, }; +static struct lgdt3306a_config hauppauge_quadHD_ATSC_a_config = { + .i2c_addr = 0x59, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + +static struct lgdt3306a_config hauppauge_quadHD_ATSC_b_config = { + .i2c_addr = 0x0e, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .deny_i2c_rptr = 1, /* Disabled */ + .spectral_inversion = 0, /* Disabled */ + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, /* 24 or 25 */ +}; + static int p8000_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { @@ -867,12 +892,6 @@ static const struct tda10071_platform_data hauppauge_tda10071_pdata = { .tuner_i2c_addr = 0x54, }; -static const struct si2165_config hauppauge_hvr4400_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_XTAL, - .ref_freq_Hz = 16000000, -}; - static const struct m88ds3103_config dvbsky_t9580_m88ds3103_config = { .i2c_addr = 0x68, .clock = 27000000, @@ -1182,6 +1201,7 @@ static int dvb_register(struct cx23885_tsport *port) struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; struct vb2_dvb_frontend *fe0, *fe1 = NULL; struct si2168_config si2168_config; + struct si2165_platform_data si2165_pdata; struct si2157_config si2157_config; struct ts2020_config ts2020_config; struct i2c_board_info info; @@ -1700,6 +1720,9 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: + if (port->nr > 2) + return 0; + i2c_bus = &dev->i2c_bus[0]; mfe_shared = 1;/* MFE */ port->frontends.gate = 0;/* not clear for me yet */ @@ -1839,9 +1862,26 @@ static int dvb_register(struct cx23885_tsport *port) break; /* port c */ case 2: - fe0->dvb.frontend = dvb_attach(si2165_attach, - &hauppauge_hvr4400_si2165_config, - &i2c_bus->i2c_adap); + /* attach frontend */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &fe0->dvb.frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL, + si2165_pdata.ref_freq_Hz = 16000000, + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (client_demod == NULL || + client_demod->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + if (fe0->dvb.frontend == NULL) break; fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; @@ -2365,6 +2405,81 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC: + switch (port->nr) { + /* port b - Terrestrial/cable */ + case 1: + /* attach frontend */ + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_quadHD_ATSC_a_config, &i2c_bus->i2c_adap); + if (fe0->dvb.frontend == NULL) + break; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; + si2157_config.inversion = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(lgdt3306a_attach, + &hauppauge_quadHD_ATSC_b_config, &i2c_bus->i2c_adap); + if (fe0->dvb.frontend == NULL) + break; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + si2157_config.if_port = 1; + si2157_config.inversion = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x62; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c index ae061b358591..61591225be9a 100644 --- a/drivers/media/pci/cx23885/cx23885-i2c.c +++ b/drivers/media/pci/cx23885/cx23885-i2c.c @@ -258,7 +258,7 @@ static u32 cx23885_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -static struct i2c_algorithm cx23885_i2c_algo_template = { +static const struct i2c_algorithm cx23885_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = cx23885_functionality, }; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 64328d08ac2f..410c3141c163 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -293,7 +293,7 @@ int cx23885_input_init(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: /* Integrated CX23885 IR controller */ driver_type = RC_DRIVER_IR_RAW; - allowed_protos = RC_BIT_NEC; + allowed_protos = RC_BIT_ALL; /* The grey Terratec remote with orange buttons */ rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS; break; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 6d735222a958..33d168ef278d 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -517,7 +517,7 @@ static void cx23885_stop_streaming(struct vb2_queue *q) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops cx23885_video_qops = { +static const struct vb2_ops cx23885_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 24a0a6c5b501..a6735afe2269 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -103,7 +103,8 @@ #define CX23885_BOARD_HAUPPAUGE_STARBURST 53 #define CX23885_BOARD_VIEWCAST_260E 54 #define CX23885_BOARD_VIEWCAST_460E 55 -#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56 +#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56 +#define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC 57 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -256,7 +257,7 @@ struct cx23885_dmaqueue { struct cx23885_tsport { struct cx23885_dev *dev; - int nr; + unsigned nr; int sram_chno; struct vb2_dvb_frontends frontends; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index df189b16af12..4711583de8fe 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -649,7 +649,7 @@ static struct page *snd_cx25821_page(struct snd_pcm_substream *substream, /* * operators */ -static struct snd_pcm_ops snd_cx25821_pcm_ops = { +static const struct snd_pcm_ops snd_cx25821_pcm_ops = { .open = snd_cx25821_pcm_open, .close = snd_cx25821_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c index 68dbc2dbc982..7c8edb6181ec 100644 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.c +++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c @@ -242,8 +242,7 @@ void cx25821_stop_upstream_audio(struct cx25821_dev *dev) dev->_audioframe_count = 0; dev->_audiofile_status = END_OF_FILE; - kfree(dev->_irq_audio_queues); - dev->_irq_audio_queues = NULL; + flush_work(&dev->_audio_work_entry); kfree(dev->_audiofilename); } @@ -446,8 +445,7 @@ static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, dev->_audioframe_index = dev->_last_index_irq; - queue_work(dev->_irq_audio_queues, - &dev->_audio_work_entry); + schedule_work(&dev->_audio_work_entry); } if (dev->_is_first_audio_frame) { @@ -639,14 +637,6 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) /* Work queue */ INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); - dev->_irq_audio_queues = - create_singlethread_workqueue("cx25821_audioworkqueue"); - - if (!dev->_irq_audio_queues) { - printk(KERN_DEBUG - pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n")); - return -ENOMEM; - } dev->_last_index_irq = 0; dev->_audio_is_running = 0; diff --git a/drivers/media/pci/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c index dca37c7dba73..63ba25b82692 100644 --- a/drivers/media/pci/cx25821/cx25821-i2c.c +++ b/drivers/media/pci/cx25821/cx25821-i2c.c @@ -281,7 +281,7 @@ static u32 cx25821_functionality(struct i2c_adapter *adap) I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; } -static struct i2c_algorithm cx25821_i2c_algo_template = { +static const struct i2c_algorithm cx25821_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = cx25821_functionality, #ifdef NEED_ALGO_CONTROL diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index adcd09be347d..7ce352a0f2d3 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -307,7 +307,7 @@ static void cx25821_stop_streaming(struct vb2_queue *q) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops cx25821_video_qops = { +static const struct vb2_ops cx25821_video_qops = { .queue_setup = cx25821_queue_setup, .buf_prepare = cx25821_buffer_prepare, .buf_finish = cx25821_buffer_finish, diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 35c7375e4617..ef61dea982e8 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -293,7 +293,6 @@ struct cx25821_dev { u32 audio_upstream_riscbuf_size; u32 audio_upstream_databuf_size; int _audioframe_index; - struct workqueue_struct *_irq_audio_queues; struct work_struct _audio_work_entry; char *input_audiofilename; diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index f3f13eb0c16e..723f06462104 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -599,7 +599,7 @@ static struct page *snd_cx88_page(struct snd_pcm_substream *substream, /* * operators */ -static struct snd_pcm_ops snd_cx88_pcm_ops = { +static const struct snd_pcm_ops snd_cx88_pcm_ops = { .open = snd_cx88_pcm_open, .close = snd_cx88_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index 04fe9af2a802..b532e49e8f33 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -756,7 +756,7 @@ static void stop_streaming(struct vb2_queue *q) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops blackbird_qops = { +static const struct vb2_ops blackbird_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 5bb63e7a5691..ac2392d8887a 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -156,7 +156,7 @@ static void stop_streaming(struct vb2_queue *q) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops dvb_qops = { +static const struct vb2_ops dvb_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 3f1342c98b46..cd7687183381 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -144,7 +144,8 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) scancode = RC_SCANCODE_NECX(addr, cmd); if (0 == (gpio & ir->mask_keyup)) - rc_keydown_notimeout(ir->dev, RC_TYPE_NEC, scancode, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_NECX, scancode, + 0); else rc_keyup(ir->dev); @@ -345,7 +346,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) * 002-T mini RC, provided with newer PV hardware */ ir_codes = RC_MAP_PIXELVIEW_MK12; - rc_type = RC_BIT_NEC; + rc_type = RC_BIT_NECX; ir->gpio_addr = MO_GP1_IO; ir->mask_keyup = 0x80; ir->polling = 10; /* ms */ @@ -631,7 +632,8 @@ void cx88_i2c_init_ir(struct cx88_core *core) /* Hauppauge XVR */ core->init_data.name = "cx88 Hauppauge XVR remote"; core->init_data.ir_codes = RC_MAP_HAUPPAUGE; - core->init_data.type = RC_BIT_RC5; + core->init_data.type = RC_BIT_RC5 | RC_BIT_RC6_MCE | + RC_BIT_RC6_6A_32; core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; info.platform_data = &core->init_data; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index 5dc1e3f08d50..d83eb3b10f54 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -567,7 +567,7 @@ static void stop_streaming(struct vb2_queue *q) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops cx8800_video_qops = { +static const struct vb2_ops cx8800_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_finish = buffer_finish, diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 47def73b3502..18e3a4deee64 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1643,53 +1643,53 @@ fail: /******************************************************************************/ /******************************************************************************/ -static struct ddb_info ddb_none = { +static const struct ddb_info ddb_none = { .type = DDB_NONE, .name = "Digital Devices PCIe bridge", }; -static struct ddb_info ddb_octopus = { +static const struct ddb_info ddb_octopus = { .type = DDB_OCTOPUS, .name = "Digital Devices Octopus DVB adapter", .port_num = 4, }; -static struct ddb_info ddb_octopus_le = { +static const struct ddb_info ddb_octopus_le = { .type = DDB_OCTOPUS, .name = "Digital Devices Octopus LE DVB adapter", .port_num = 2, }; -static struct ddb_info ddb_octopus_mini = { +static const struct ddb_info ddb_octopus_mini = { .type = DDB_OCTOPUS, .name = "Digital Devices Octopus Mini", .port_num = 4, }; -static struct ddb_info ddb_v6 = { +static const struct ddb_info ddb_v6 = { .type = DDB_OCTOPUS, .name = "Digital Devices Cine S2 V6 DVB adapter", .port_num = 3, }; -static struct ddb_info ddb_v6_5 = { +static const struct ddb_info ddb_v6_5 = { .type = DDB_OCTOPUS, .name = "Digital Devices Cine S2 V6.5 DVB adapter", .port_num = 4, }; -static struct ddb_info ddb_dvbct = { +static const struct ddb_info ddb_dvbct = { .type = DDB_OCTOPUS, .name = "Digital Devices DVBCT V6.1 DVB adapter", .port_num = 3, }; -static struct ddb_info ddb_satixS2v3 = { +static const struct ddb_info ddb_satixS2v3 = { .type = DDB_OCTOPUS, .name = "Mystique SaTiX-S2 V3 DVB adapter", .port_num = 3, }; -static struct ddb_info ddb_octopusv3 = { +static const struct ddb_info ddb_octopusv3 = { .type = DDB_OCTOPUS, .name = "Digital Devices Octopus V3 DVB adapter", .port_num = 4, diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c index f198b9826ed8..a26f9800eca3 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c @@ -318,7 +318,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, return vmalloc_to_page(pageptr); } -static struct snd_pcm_ops snd_ivtv_pcm_capture_ops = { +static const struct snd_pcm_ops snd_ivtv_pcm_capture_ops = { .open = snd_ivtv_pcm_capture_open, .close = snd_ivtv_pcm_capture_close, .ioctl = snd_ivtv_pcm_ioctl, diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index bccbf2d18e30..dea80efd5836 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -215,7 +215,8 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) /* Default to grey remote */ init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_BIT_RC5; + init_data->type = RC_BIT_RC5 | RC_BIT_RC6_MCE | + RC_BIT_RC6_6A_32; init_data->name = itv->card_name; break; case IVTV_HW_I2C_IR_RX_ADAPTEC: @@ -625,7 +626,7 @@ static u32 ivtv_functionality(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm ivtv_algo = { +static const struct i2c_algorithm ivtv_algo = { .master_xfer = ivtv_xfer, .functionality = ivtv_functionality, }; diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index ac547cb84de8..b078ac2a682c 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -353,7 +353,7 @@ static void netup_unidvb_stop_streaming(struct vb2_queue *q) netup_unidvb_queue_cleanup(dma); } -static struct vb2_ops dvb_qops = { +static const struct vb2_ops dvb_qops = { .queue_setup = netup_unidvb_queue_setup, .buf_prepare = netup_unidvb_buf_prepare, .buf_queue = netup_unidvb_buf_queue, diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c index c09c52bc6eab..b49e4f9788e8 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -327,11 +327,8 @@ static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num) i2c->adap.dev.parent = &ndev->pci_dev->dev; i2c_set_adapdata(&i2c->adap, i2c); ret = i2c_add_adapter(&i2c->adap); - if (ret) { - dev_err(&ndev->pci_dev->dev, - "%s(): failed to add I2C adapter\n", __func__); + if (ret) return ret; - } dev_info(&ndev->pci_dev->dev, "%s(): registered I2C bus %d at 0x%x\n", __func__, diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 4e783a68bf4a..423e8c889310 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -613,7 +613,7 @@ static struct stv6110x_config tuner_cineS2_1 = { .clk_div = 1, }; -static struct ngene_info ngene_info_cineS2 = { +static const struct ngene_info ngene_info_cineS2 = { .type = NGENE_SIDEWINDER, .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, @@ -627,7 +627,7 @@ static struct ngene_info ngene_info_cineS2 = { .msi_supported = true, }; -static struct ngene_info ngene_info_satixS2 = { +static const struct ngene_info ngene_info_satixS2 = { .type = NGENE_SIDEWINDER, .name = "Mystique SaTiX-S2 Dual", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, @@ -641,7 +641,7 @@ static struct ngene_info ngene_info_satixS2 = { .msi_supported = true, }; -static struct ngene_info ngene_info_satixS2v2 = { +static const struct ngene_info ngene_info_satixS2v2 = { .type = NGENE_SIDEWINDER, .name = "Mystique SaTiX-S2 Dual (v2)", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, @@ -656,7 +656,7 @@ static struct ngene_info ngene_info_satixS2v2 = { .msi_supported = true, }; -static struct ngene_info ngene_info_cineS2v5 = { +static const struct ngene_info ngene_info_cineS2v5 = { .type = NGENE_SIDEWINDER, .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, @@ -672,7 +672,7 @@ static struct ngene_info ngene_info_cineS2v5 = { }; -static struct ngene_info ngene_info_duoFlex = { +static const struct ngene_info ngene_info_duoFlex = { .type = NGENE_SIDEWINDER, .name = "Digital Devices DuoFlex PCIe or miniPCIe", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, @@ -687,7 +687,7 @@ static struct ngene_info ngene_info_duoFlex = { .msi_supported = true, }; -static struct ngene_info ngene_info_m780 = { +static const struct ngene_info ngene_info_m780 = { .type = NGENE_APP, .name = "Aver M780 ATSC/QAM-B", @@ -727,7 +727,7 @@ static struct drxd_config fe_terratec_dvbt_1 = { .osc_deviation = osc_deviation, }; -static struct ngene_info ngene_info_terratec = { +static const struct ngene_info ngene_info_terratec = { .type = NGENE_TERRATEC, .name = "Terratec Integra/Cinergy2400i Dual DVB-T", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c index eff5e9f51ace..7fb649e523f4 100644 --- a/drivers/media/pci/pt3/pt3.c +++ b/drivers/media/pci/pt3/pt3.c @@ -798,10 +798,8 @@ static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) strlcpy(i2c->name, DRV_NAME, sizeof(i2c->name)); i2c_set_adapdata(i2c, pt3); ret = i2c_add_adapter(i2c); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to add i2c adapter\n"); + if (ret < 0) goto err_i2cbuf; - } for (i = 0; i < PT3_NUM_FE; i++) { ret = pt3_alloc_adapter(pt3, i); diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index 94f816244407..dc0e2fc5f68b 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -877,7 +877,7 @@ static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream, * ALSA capture callbacks definition */ -static struct snd_pcm_ops snd_card_saa7134_capture_ops = { +static const struct snd_pcm_ops snd_card_saa7134_capture_ops = { .open = snd_card_saa7134_capture_open, .close = snd_card_saa7134_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 791a5161809b..f0fe2524259f 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -85,7 +85,7 @@ static void stop_streaming(struct vb2_queue *vq) dev->empress_started = 0; } -static struct vb2_ops saa7134_empress_qops = { +static const struct vb2_ops saa7134_empress_qops = { .queue_setup = saa7134_ts_queue_setup, .buf_init = saa7134_ts_buffer_init, .buf_prepare = saa7134_ts_buffer_prepare, diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c index 8ef6399d794f..2dac48fa1386 100644 --- a/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -338,7 +338,7 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm saa7134_algo = { +static const struct i2c_algorithm saa7134_algo = { .master_xfer = saa7134_i2c_xfer, .functionality = functionality, }; diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index c8042c3888cd..eff52bbbfd66 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -345,7 +345,7 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_type *protocol, if (data[9] != (unsigned char)(~data[8])) return 0; - *protocol = RC_TYPE_NEC; + *protocol = RC_TYPE_NECX; *scancode = RC_SCANCODE_NECX(data[11] << 8 | data[10], data[9]); *toggle = 0; return 1; @@ -1035,7 +1035,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; dev->init_data.ir_codes = RC_MAP_BEHOLD; - dev->init_data.type = RC_BIT_NEC; + dev->init_data.type = RC_BIT_NECX; info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 8a6ebd087889..cbb173d99085 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1054,7 +1054,7 @@ void saa7134_vb2_stop_streaming(struct vb2_queue *vq) pm_qos_remove_request(&dev->qos_request); } -static struct vb2_ops vb2_qops = { +static const struct vb2_ops vb2_qops = { .queue_setup = queue_setup, .buf_init = buffer_init, .buf_prepare = buffer_prepare, @@ -1659,8 +1659,6 @@ static int saa7134_cropcap(struct file *file, void *priv, if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - cap->bounds = dev->crop_bounds; - cap->defrect = dev->crop_defrect; cap->pixelaspect.numerator = 1; cap->pixelaspect.denominator = 1; if (dev->tvnorm->id & V4L2_STD_525_60) { @@ -1674,25 +1672,41 @@ static int saa7134_cropcap(struct file *file, void *priv, return 0; } -static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int saa7134_g_selection(struct file *file, void *f, struct v4l2_selection *sel) { struct saa7134_dev *dev = video_drvdata(file); - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - crop->c = dev->crop_current; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = dev->crop_current; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = dev->crop_defrect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = dev->crop_bounds; + break; + default: + return -EINVAL; + } return 0; } -static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) +static int saa7134_s_selection(struct file *file, void *f, struct v4l2_selection *sel) { struct saa7134_dev *dev = video_drvdata(file); struct v4l2_rect *b = &dev->crop_bounds; struct v4l2_rect *c = &dev->crop_current; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; if (dev->overlay_owner) @@ -1700,7 +1714,7 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr if (vb2_is_streaming(&dev->video_vbq)) return -EBUSY; - *c = crop->c; + *c = sel->r; if (c->top < b->top) c->top = b->top; if (c->top > b->top + b->height) @@ -1714,6 +1728,7 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr c->left = b->left + b->width; if (c->width > b->left - c->left + b->width) c->width = b->left - c->left + b->width; + sel->r = *c; return 0; } @@ -1989,8 +2004,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = saa7134_g_tuner, .vidioc_s_tuner = saa7134_s_tuner, - .vidioc_g_crop = saa7134_g_crop, - .vidioc_s_crop = saa7134_s_crop, + .vidioc_g_selection = saa7134_g_selection, + .vidioc_s_selection = saa7134_s_selection, .vidioc_g_fbuf = saa7134_g_fbuf, .vidioc_s_fbuf = saa7134_s_fbuf, .vidioc_overlay = saa7134_overlay, diff --git a/drivers/media/pci/saa7164/saa7164-i2c.c b/drivers/media/pci/saa7164/saa7164-i2c.c index 0342d84913b8..024f4e29e840 100644 --- a/drivers/media/pci/saa7164/saa7164-i2c.c +++ b/drivers/media/pci/saa7164/saa7164-i2c.c @@ -75,7 +75,7 @@ static u32 saa7164_functionality(struct i2c_adapter *adap) return I2C_FUNC_I2C; } -static struct i2c_algorithm saa7164_i2c_algo_template = { +static const struct i2c_algorithm saa7164_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = saa7164_functionality, }; diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 83981d611a79..6dbe3b4d09ce 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -1060,7 +1060,7 @@ static void smi_remove(struct pci_dev *pdev) } /* DVBSky cards */ -static struct smi_cfg_info dvbsky_s950_cfg = { +static const struct smi_cfg_info dvbsky_s950_cfg = { .type = SMI_DVBSKY_S950, .name = "DVBSky S950 V3", .ts_0 = SMI_TS_NULL, @@ -1070,7 +1070,7 @@ static struct smi_cfg_info dvbsky_s950_cfg = { .rc_map = RC_MAP_DVBSKY, }; -static struct smi_cfg_info dvbsky_s952_cfg = { +static const struct smi_cfg_info dvbsky_s952_cfg = { .type = SMI_DVBSKY_S952, .name = "DVBSky S952 V3", .ts_0 = SMI_TS_DMA_BOTH, @@ -1080,7 +1080,7 @@ static struct smi_cfg_info dvbsky_s952_cfg = { .rc_map = RC_MAP_DVBSKY, }; -static struct smi_cfg_info dvbsky_t9580_cfg = { +static const struct smi_cfg_info dvbsky_t9580_cfg = { .type = SMI_DVBSKY_T9580, .name = "DVBSky T9580 V3", .ts_0 = SMI_TS_DMA_BOTH, @@ -1090,7 +1090,7 @@ static struct smi_cfg_info dvbsky_t9580_cfg = { .rc_map = RC_MAP_DVBSKY, }; -static struct smi_cfg_info technotrend_s2_4200_cfg = { +static const struct smi_cfg_info technotrend_s2_4200_cfg = { .type = SMI_TECHNOTREND_S2_4200, .name = "TechnoTrend TT-budget S2-4200 Twin", .ts_0 = SMI_TS_DMA_BOTH, diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 4a37a1c51c48..6a35107aca25 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -252,7 +252,7 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, return 0; } -static struct snd_pcm_ops snd_solo_pcm_ops = { +static const struct snd_pcm_ops snd_solo_pcm_ops = { .open = snd_solo_pcm_open, .close = snd_solo_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 399164314c28..25a2137ab799 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -759,7 +759,7 @@ static void solo_enc_buf_finish(struct vb2_buffer *vb) } } -static struct vb2_ops solo_enc_video_qops = { +static const struct vb2_ops solo_enc_video_qops = { .queue_setup = solo_enc_queue_setup, .buf_queue = solo_enc_buf_queue, .buf_finish = solo_enc_buf_finish, diff --git a/drivers/media/pci/tw5864/Kconfig b/drivers/media/pci/tw5864/Kconfig new file mode 100644 index 000000000000..87c8f327e2d4 --- /dev/null +++ b/drivers/media/pci/tw5864/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_TW5864 + tristate "Techwell TW5864 video/audio grabber and encoder" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + ---help--- + Support for boards based on Techwell TW5864 chip which provides + multichannel video & audio grabbing and encoding (H.264, MJPEG, + ADPCM G.726). + + To compile this driver as a module, choose M here: the + module will be called tw5864. diff --git a/drivers/media/pci/tw5864/Makefile b/drivers/media/pci/tw5864/Makefile new file mode 100644 index 000000000000..4fc8b3b1a45a --- /dev/null +++ b/drivers/media/pci/tw5864/Makefile @@ -0,0 +1,3 @@ +tw5864-objs := tw5864-core.o tw5864-video.o tw5864-h264.o tw5864-util.o + +obj-$(CONFIG_VIDEO_TW5864) += tw5864.o diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c new file mode 100644 index 000000000000..1d43b96958ea --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -0,0 +1,359 @@ +/* + * TW5864 driver - core functions + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.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. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/sound.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/pm.h> +#include <linux/pci_ids.h> +#include <linux/jiffies.h> +#include <asm/dma.h> +#include <media/v4l2-dev.h> + +#include "tw5864.h" +#include "tw5864-reg.h" + +MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices"); +MODULE_AUTHOR("Bluecherry Maintainers <maintainers@bluecherrydvr.com>"); +MODULE_AUTHOR("Andrey Utkin <andrey.utkin@corp.bluecherry.net>"); +MODULE_LICENSE("GPL"); + +/* + * BEWARE OF KNOWN ISSUES WITH VIDEO QUALITY + * + * This driver was developed by Bluecherry LLC by deducing behaviour of + * original manufacturer's driver, from both source code and execution traces. + * It is known that there are some artifacts on output video with this driver: + * - on all known hardware samples: random pixels of wrong color (mostly + * white, red or blue) appearing and disappearing on sequences of P-frames; + * - on some hardware samples (known with H.264 core version e006:2800): + * total madness on P-frames: blocks of wrong luminance; blocks of wrong + * colors "creeping" across the picture. + * There is a workaround for both issues: avoid P-frames by setting GOP size + * to 1. To do that, run this command on device files created by this driver: + * + * v4l2-ctl --device /dev/videoX --set-ctrl=video_gop_size=1 + * + * These issues are not decoding errors; all produced H.264 streams are decoded + * properly. Streams without P-frames don't have these artifacts so it's not + * analog-to-digital conversion issues nor internal memory errors; we conclude + * it's internal H.264 encoder issues. + * We cannot even check the original driver's behaviour because it has never + * worked properly at all in our development environment. So these issues may + * be actually related to firmware or hardware. However it may be that there's + * just some more register settings missing in the driver which would please + * the hardware. + * Manufacturer didn't help much on our inquiries, but feel free to disturb + * again the support of Intersil (owner of former Techwell). + */ + +/* take first free /dev/videoX indexes by default */ +static unsigned int video_nr[] = {[0 ... (TW5864_INPUTS - 1)] = -1 }; + +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video devices numbers array"); + +/* + * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * the PCI ID database up to date. Note that the entries must be + * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. + */ +static const struct pci_device_id tw5864_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_5864)}, + {0,} +}; + +void tw5864_irqmask_apply(struct tw5864_dev *dev) +{ + tw_writel(TW5864_INTR_ENABLE_L, dev->irqmask & 0xffff); + tw_writel(TW5864_INTR_ENABLE_H, (dev->irqmask >> 16)); +} + +static void tw5864_interrupts_disable(struct tw5864_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + dev->irqmask = 0; + tw5864_irqmask_apply(dev); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void tw5864_timer_isr(struct tw5864_dev *dev); +static void tw5864_h264_isr(struct tw5864_dev *dev); + +static irqreturn_t tw5864_isr(int irq, void *dev_id) +{ + struct tw5864_dev *dev = dev_id; + u32 status; + + status = tw_readl(TW5864_INTR_STATUS_L) | + tw_readl(TW5864_INTR_STATUS_H) << 16; + if (!status) + return IRQ_NONE; + + tw_writel(TW5864_INTR_CLR_L, 0xffff); + tw_writel(TW5864_INTR_CLR_H, 0xffff); + + if (status & TW5864_INTR_VLC_DONE) + tw5864_h264_isr(dev); + + if (status & TW5864_INTR_TIMER) + tw5864_timer_isr(dev); + + if (!(status & (TW5864_INTR_TIMER | TW5864_INTR_VLC_DONE))) { + dev_dbg(&dev->pci->dev, "Unknown interrupt, status 0x%08X\n", + status); + } + + return IRQ_HANDLED; +} + +static void tw5864_h264_isr(struct tw5864_dev *dev) +{ + int channel = tw_readl(TW5864_DSP) & TW5864_DSP_ENC_CHN; + struct tw5864_input *input = &dev->inputs[channel]; + int cur_frame_index, next_frame_index; + struct tw5864_h264_frame *cur_frame, *next_frame; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + cur_frame_index = dev->h264_buf_w_index; + next_frame_index = (cur_frame_index + 1) % H264_BUF_CNT; + cur_frame = &dev->h264_buf[cur_frame_index]; + next_frame = &dev->h264_buf[next_frame_index]; + + if (next_frame_index != dev->h264_buf_r_index) { + cur_frame->vlc_len = tw_readl(TW5864_VLC_LENGTH) << 2; + cur_frame->checksum = tw_readl(TW5864_VLC_CRC_REG); + cur_frame->input = input; + cur_frame->timestamp = ktime_get_ns(); + cur_frame->seqno = input->frame_seqno; + cur_frame->gop_seqno = input->frame_gop_seqno; + + dev->h264_buf_w_index = next_frame_index; + tasklet_schedule(&dev->tasklet); + + cur_frame = next_frame; + + spin_lock(&input->slock); + input->frame_seqno++; + input->frame_gop_seqno++; + if (input->frame_gop_seqno >= input->gop) + input->frame_gop_seqno = 0; + spin_unlock(&input->slock); + } else { + dev_err(&dev->pci->dev, + "Skipped frame on input %d because all buffers busy\n", + channel); + } + + dev->encoder_busy = 0; + + spin_unlock_irqrestore(&dev->slock, flags); + + tw_writel(TW5864_VLC_STREAM_BASE_ADDR, cur_frame->vlc.dma_addr); + tw_writel(TW5864_MV_STREAM_BASE_ADDR, cur_frame->mv.dma_addr); + + /* Additional ack for this interrupt */ + tw_writel(TW5864_VLC_DSP_INTR, 0x00000001); + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_VLC_DONE_INTR); +} + +static void tw5864_input_deadline_update(struct tw5864_input *input) +{ + input->new_frame_deadline = jiffies + msecs_to_jiffies(1000); +} + +static void tw5864_timer_isr(struct tw5864_dev *dev) +{ + unsigned long flags; + int i; + int encoder_busy; + + /* Additional ack for this interrupt */ + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_TIMER_INTR); + + spin_lock_irqsave(&dev->slock, flags); + encoder_busy = dev->encoder_busy; + spin_unlock_irqrestore(&dev->slock, flags); + + if (encoder_busy) + return; + + /* + * Traversing inputs in round-robin fashion, starting from next to the + * last processed one + */ + for (i = 0; i < TW5864_INPUTS; i++) { + int next_input = (i + dev->next_input) % TW5864_INPUTS; + struct tw5864_input *input = &dev->inputs[next_input]; + int raw_buf_id; /* id of internal buf with last raw frame */ + + spin_lock_irqsave(&input->slock, flags); + if (!input->enabled) + goto next; + + /* Check if new raw frame is available */ + raw_buf_id = tw_mask_shift_readl(TW5864_SENIF_ORG_FRM_PTR1, 0x3, + 2 * input->nr); + + if (input->buf_id != raw_buf_id) { + input->buf_id = raw_buf_id; + tw5864_input_deadline_update(input); + spin_unlock_irqrestore(&input->slock, flags); + + spin_lock_irqsave(&dev->slock, flags); + dev->encoder_busy = 1; + dev->next_input = (next_input + 1) % TW5864_INPUTS; + spin_unlock_irqrestore(&dev->slock, flags); + + tw5864_request_encoded_frame(input); + break; + } + + /* No new raw frame; check if channel is stuck */ + if (time_is_after_jiffies(input->new_frame_deadline)) { + /* If stuck, request new raw frames again */ + tw_mask_shift_writel(TW5864_ENC_BUF_PTR_REC1, 0x3, + 2 * input->nr, input->buf_id + 3); + tw5864_input_deadline_update(input); + } +next: + spin_unlock_irqrestore(&input->slock, flags); + } +} + +static int tw5864_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw5864_dev *dev; + int err; + + dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + snprintf(dev->name, sizeof(dev->name), "tw5864:%s", pci_name(pci_dev)); + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + return err; + + /* pci init */ + dev->pci = pci_dev; + err = pci_enable_device(pci_dev); + if (err) { + dev_err(&dev->pci->dev, "pci_enable_device() failed\n"); + goto unreg_v4l2; + } + + pci_set_master(pci_dev); + + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&dev->pci->dev, "32 bit PCI DMA is not supported\n"); + goto disable_pci; + } + + /* get mmio */ + err = pci_request_regions(pci_dev, dev->name); + if (err) { + dev_err(&dev->pci->dev, "Cannot request regions for MMIO\n"); + goto disable_pci; + } + dev->mmio = pci_ioremap_bar(pci_dev, 0); + if (!dev->mmio) { + err = -EIO; + dev_err(&dev->pci->dev, "can't ioremap() MMIO memory\n"); + goto release_mmio; + } + + spin_lock_init(&dev->slock); + + dev_info(&pci_dev->dev, "TW5864 hardware version: %04x\n", + tw_readl(TW5864_HW_VERSION)); + dev_info(&pci_dev->dev, "TW5864 H.264 core version: %04x:%04x\n", + tw_readl(TW5864_H264REV), + tw_readl(TW5864_UNDECLARED_H264REV_PART2)); + + err = tw5864_video_init(dev, video_nr); + if (err) + goto unmap_mmio; + + /* get irq */ + err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw5864_isr, + IRQF_SHARED, "tw5864", dev); + if (err < 0) { + dev_err(&dev->pci->dev, "can't get IRQ %d\n", pci_dev->irq); + goto fini_video; + } + + dev_info(&pci_dev->dev, "Note: there are known video quality issues. For details\n"); + dev_info(&pci_dev->dev, "see the comment in drivers/media/pci/tw5864/tw5864-core.c.\n"); + + return 0; + +fini_video: + tw5864_video_fini(dev); +unmap_mmio: + iounmap(dev->mmio); +release_mmio: + pci_release_regions(pci_dev); +disable_pci: + pci_disable_device(pci_dev); +unreg_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); + return err; +} + +static void tw5864_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw5864_dev *dev = + container_of(v4l2_dev, struct tw5864_dev, v4l2_dev); + + /* shutdown subsystems */ + tw5864_interrupts_disable(dev); + + /* unregister */ + tw5864_video_fini(dev); + + /* release resources */ + iounmap(dev->mmio); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + + v4l2_device_unregister(&dev->v4l2_dev); + devm_kfree(&pci_dev->dev, dev); +} + +static struct pci_driver tw5864_pci_driver = { + .name = "tw5864", + .id_table = tw5864_pci_tbl, + .probe = tw5864_initdev, + .remove = tw5864_finidev, +}; + +module_pci_driver(tw5864_pci_driver); diff --git a/drivers/media/pci/tw5864/tw5864-h264.c b/drivers/media/pci/tw5864/tw5864-h264.c new file mode 100644 index 000000000000..330d200f52cd --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-h264.c @@ -0,0 +1,259 @@ +/* + * TW5864 driver - H.264 headers generation functions + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.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. + */ + +#include <linux/log2.h> + +#include "tw5864.h" + +static u8 marker[] = { 0x00, 0x00, 0x00, 0x01 }; + +/* + * Exponential-Golomb coding functions + * + * These functions are used for generation of H.264 bitstream headers. + * + * This code is derived from tw5864 reference driver by manufacturers, which + * itself apparently was derived from x264 project. + */ + +/* Bitstream writing context */ +struct bs { + u8 *buf; /* pointer to buffer beginning */ + u8 *buf_end; /* pointer to buffer end */ + u8 *ptr; /* pointer to current byte in buffer */ + unsigned int bits_left; /* number of available bits in current byte */ +}; + +static void bs_init(struct bs *s, void *buf, int size) +{ + s->buf = buf; + s->ptr = buf; + s->buf_end = s->ptr + size; + s->bits_left = 8; +} + +static int bs_len(struct bs *s) +{ + return s->ptr - s->buf; +} + +static void bs_write(struct bs *s, int count, u32 bits) +{ + if (s->ptr >= s->buf_end - 4) + return; + while (count > 0) { + if (count < 32) + bits &= (1 << count) - 1; + if (count < s->bits_left) { + *s->ptr = (*s->ptr << count) | bits; + s->bits_left -= count; + break; + } + *s->ptr = (*s->ptr << s->bits_left) | + (bits >> (count - s->bits_left)); + count -= s->bits_left; + s->ptr++; + s->bits_left = 8; + } +} + +static void bs_write1(struct bs *s, u32 bit) +{ + if (s->ptr < s->buf_end) { + *s->ptr <<= 1; + *s->ptr |= bit; + s->bits_left--; + if (s->bits_left == 0) { + s->ptr++; + s->bits_left = 8; + } + } +} + +static void bs_write_ue(struct bs *s, u32 val) +{ + if (val == 0) { + bs_write1(s, 1); + } else { + val++; + bs_write(s, 2 * fls(val) - 1, val); + } +} + +static void bs_write_se(struct bs *s, int val) +{ + bs_write_ue(s, val <= 0 ? -val * 2 : val * 2 - 1); +} + +static void bs_rbsp_trailing(struct bs *s) +{ + bs_write1(s, 1); + if (s->bits_left != 8) + bs_write(s, s->bits_left, 0x00); +} + +/* H.264 headers generation functions */ + +static int tw5864_h264_gen_sps_rbsp(u8 *buf, size_t size, int width, int height) +{ + struct bs bs, *s; + + s = &bs; + bs_init(s, buf, size); + bs_write(s, 8, 0x42); /* profile_idc, baseline */ + bs_write(s, 1, 1); /* constraint_set0_flag */ + bs_write(s, 1, 1); /* constraint_set1_flag */ + bs_write(s, 1, 0); /* constraint_set2_flag */ + bs_write(s, 5, 0); /* reserved_zero_5bits */ + bs_write(s, 8, 0x1e); /* level_idc */ + bs_write_ue(s, 0); /* seq_parameter_set_id */ + bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4); /* log2_max_frame_num_minus4 */ + bs_write_ue(s, 0); /* pic_order_cnt_type */ + /* log2_max_pic_order_cnt_lsb_minus4 */ + bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4); + bs_write_ue(s, 1); /* num_ref_frames */ + bs_write(s, 1, 0); /* gaps_in_frame_num_value_allowed_flag */ + bs_write_ue(s, width / 16 - 1); /* pic_width_in_mbs_minus1 */ + bs_write_ue(s, height / 16 - 1); /* pic_height_in_map_units_minus1 */ + bs_write(s, 1, 1); /* frame_mbs_only_flag */ + bs_write(s, 1, 0); /* direct_8x8_inference_flag */ + bs_write(s, 1, 0); /* frame_cropping_flag */ + bs_write(s, 1, 0); /* vui_parameters_present_flag */ + bs_rbsp_trailing(s); + return bs_len(s); +} + +static int tw5864_h264_gen_pps_rbsp(u8 *buf, size_t size, int qp) +{ + struct bs bs, *s; + + s = &bs; + bs_init(s, buf, size); + bs_write_ue(s, 0); /* pic_parameter_set_id */ + bs_write_ue(s, 0); /* seq_parameter_set_id */ + bs_write(s, 1, 0); /* entropy_coding_mode_flag */ + bs_write(s, 1, 0); /* pic_order_present_flag */ + bs_write_ue(s, 0); /* num_slice_groups_minus1 */ + bs_write_ue(s, 0); /* i_num_ref_idx_l0_active_minus1 */ + bs_write_ue(s, 0); /* i_num_ref_idx_l1_active_minus1 */ + bs_write(s, 1, 0); /* weighted_pred_flag */ + bs_write(s, 2, 0); /* weighted_bipred_idc */ + bs_write_se(s, qp - 26); /* pic_init_qp_minus26 */ + bs_write_se(s, qp - 26); /* pic_init_qs_minus26 */ + bs_write_se(s, 0); /* chroma_qp_index_offset */ + bs_write(s, 1, 0); /* deblocking_filter_control_present_flag */ + bs_write(s, 1, 0); /* constrained_intra_pred_flag */ + bs_write(s, 1, 0); /* redundant_pic_cnt_present_flag */ + bs_rbsp_trailing(s); + return bs_len(s); +} + +static int tw5864_h264_gen_slice_head(u8 *buf, size_t size, + unsigned int idr_pic_id, + unsigned int frame_gop_seqno, + int *tail_nb_bits, u8 *tail) +{ + struct bs bs, *s; + int is_i_frame = frame_gop_seqno == 0; + + s = &bs; + bs_init(s, buf, size); + bs_write_ue(s, 0); /* first_mb_in_slice */ + bs_write_ue(s, is_i_frame ? 2 : 5); /* slice_type - I or P */ + bs_write_ue(s, 0); /* pic_parameter_set_id */ + bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno); /* frame_num */ + if (is_i_frame) + bs_write_ue(s, idr_pic_id); + + /* pic_order_cnt_lsb */ + bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno); + + if (is_i_frame) { + bs_write1(s, 0); /* no_output_of_prior_pics_flag */ + bs_write1(s, 0); /* long_term_reference_flag */ + } else { + bs_write1(s, 0); /* num_ref_idx_active_override_flag */ + bs_write1(s, 0); /* ref_pic_list_reordering_flag_l0 */ + bs_write1(s, 0); /* adaptive_ref_pic_marking_mode_flag */ + } + + bs_write_se(s, 0); /* slice_qp_delta */ + + if (s->bits_left != 8) { + *tail = ((s->ptr[0]) << s->bits_left); + *tail_nb_bits = 8 - s->bits_left; + } else { + *tail = 0; + *tail_nb_bits = 0; + } + + return bs_len(s); +} + +void tw5864_h264_put_stream_header(u8 **buf, size_t *space_left, int qp, + int width, int height) +{ + int nal_len; + + /* SPS */ + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + **buf = 0x67; /* SPS NAL header */ + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_sps_rbsp(*buf, *space_left, width, height); + *buf += nal_len; + *space_left -= nal_len; + + /* PPS */ + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + **buf = 0x68; /* PPS NAL header */ + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_pps_rbsp(*buf, *space_left, qp); + *buf += nal_len; + *space_left -= nal_len; +} + +void tw5864_h264_put_slice_header(u8 **buf, size_t *space_left, + unsigned int idr_pic_id, + unsigned int frame_gop_seqno, + int *tail_nb_bits, u8 *tail) +{ + int nal_len; + + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + /* Frame NAL header */ + **buf = (frame_gop_seqno == 0) ? 0x25 : 0x21; + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_slice_head(*buf, *space_left, idr_pic_id, + frame_gop_seqno, tail_nb_bits, + tail); + *buf += nal_len; + *space_left -= nal_len; +} diff --git a/drivers/media/pci/tw5864/tw5864-reg.h b/drivers/media/pci/tw5864/tw5864-reg.h new file mode 100644 index 000000000000..92a1b077ef8a --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-reg.h @@ -0,0 +1,2133 @@ +/* + * TW5864 driver - registers description + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.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. + */ + +/* According to TW5864_datasheet_0.6d.pdf, tw5864b1-ds.pdf */ + +/* Register Description - Direct Map Space */ +/* 0x0000 ~ 0x1ffc - H264 Register Map */ +/* [15:0] The Version register for H264 core (Read Only) */ +#define TW5864_H264REV 0x0000 + +#define TW5864_EMU 0x0004 +/* Define controls in register TW5864_EMU */ +/* DDR controller enabled */ +#define TW5864_EMU_EN_DDR BIT(0) +/* Enable bit for Inter module */ +#define TW5864_EMU_EN_ME BIT(1) +/* Enable bit for Sensor Interface module */ +#define TW5864_EMU_EN_SEN BIT(2) +/* Enable bit for Host Burst Access */ +#define TW5864_EMU_EN_BHOST BIT(3) +/* Enable bit for Loop Filter module */ +#define TW5864_EMU_EN_LPF BIT(4) +/* Enable bit for PLBK module */ +#define TW5864_EMU_EN_PLBK BIT(5) +/* + * Video Frame mapping in DDR + * 00 CIF + * 01 D1 + * 10 Reserved + * 11 Reserved + * + */ +#define TW5864_DSP_FRAME_TYPE (3 << 6) +#define TW5864_DSP_FRAME_TYPE_D1 BIT(6) + +#define TW5864_UNDECLARED_H264REV_PART2 0x0008 + +#define TW5864_SLICE 0x000c +/* Define controls in register TW5864_SLICE */ +/* VLC Slice end flag */ +#define TW5864_VLC_SLICE_END BIT(0) +/* Master Slice End Flag */ +#define TW5864_MAS_SLICE_END BIT(4) +/* Host to start a new slice Address */ +#define TW5864_START_NSLICE BIT(15) + +/* + * [15:0] Two bit for each channel (channel 0 ~ 7). Each two bits are the buffer + * pointer for the last encoded frame of the corresponding channel. + */ +#define TW5864_ENC_BUF_PTR_REC1 0x0010 + +/* [5:0] DSP_MB_QP and [15:10] DSP_LPF_OFFSET */ +#define TW5864_DSP_QP 0x0018 +/* Define controls in register TW5864_DSP_QP */ +/* [5:0] H264 QP Value for codec */ +#define TW5864_DSP_MB_QP 0x003f +/* + * [15:10] H264 LPF_OFFSET Address + * (Default 0) + */ +#define TW5864_DSP_LPF_OFFSET 0xfc00 + +#define TW5864_DSP_CODEC 0x001c +/* Define controls in register TW5864_DSP_CODEC */ +/* + * 0: Encode (TW5864 Default) + * 1: Decode + */ +#define TW5864_DSP_CODEC_MODE BIT(0) +/* + * 0->3 4 VLC data buffer in DDR (1M each) + * 0->7 8 VLC data buffer in DDR (512k each) + */ +#define TW5864_VLC_BUF_ID (7 << 2) +/* + * 0 4CIF in 1 MB + * 1 1CIF in 1 MB + */ +#define TW5864_CIF_MAP_MD BIT(6) +/* + * 0 2 falf D1 in 1 MB + * 1 1 half D1 in 1 MB + */ +#define TW5864_HD1_MAP_MD BIT(7) +/* VLC Stream valid */ +#define TW5864_VLC_VLD BIT(8) +/* MV Vector Valid */ +#define TW5864_MV_VECT_VLD BIT(9) +/* MV Flag Valid */ +#define TW5864_MV_FLAG_VLD BIT(10) + +#define TW5864_DSP_SEN 0x0020 +/* Define controls in register TW5864_DSP_SEN */ +/* Org Buffer Base for Luma (default 0) */ +#define TW5864_DSP_SEN_PIC_LU 0x000f +/* Org Buffer Base for Chroma (default 4) */ +#define TW5864_DSP_SEN_PIC_CHM 0x00f0 +/* Maximum Number of Buffers (default 4) */ +#define TW5864_DSP_SEN_PIC_MAX 0x0700 +/* + * Original Frame D1 or HD1 switch + * (Default 0) + */ +#define TW5864_DSP_SEN_HFULL 0x1000 + +#define TW5864_DSP_REF_PIC 0x0024 +/* Define controls in register TW5864_DSP_REF_PIC */ +/* Ref Buffer Base for Luma (default 0) */ +#define TW5864_DSP_REF_PIC_LU 0x000f +/* Ref Buffer Base for Chroma (default 4) */ +#define TW5864_DSP_REF_PIC_CHM 0x00f0 +/* Maximum Number of Buffers (default 4) */ +#define TW5864_DSP_REF_PIC_MAX 0x0700 + +/* [15:0] SEN_EN_CH[n] SENIF original frame capture enable for each channel */ +#define TW5864_SEN_EN_CH 0x0028 + +#define TW5864_DSP 0x002c +/* Define controls in register TW5864_DSP */ +/* The ID for channel selected for encoding operation */ +#define TW5864_DSP_ENC_CHN 0x000f +/* See DSP_MB_DELAY below */ +#define TW5864_DSP_MB_WAIT 0x0010 +/* + * DSP Chroma Switch + * 0 DDRB + * 1 DDRA + */ +#define TW5864_DSP_CHROM_SW 0x0020 +/* VLC Flow Control: 1 for enable */ +#define TW5864_DSP_FLW_CNTL 0x0040 +/* + * If DSP_MB_WAIT == 0, MB delay is DSP_MB_DELAY * 16 + * If DSP_MB_DELAY == 1, MB delay is DSP_MB_DELAY * 128 + */ +#define TW5864_DSP_MB_DELAY 0x0f00 + +#define TW5864_DDR 0x0030 +/* Define controls in register TW5864_DDR */ +/* DDR Single Access Page Number */ +#define TW5864_DDR_PAGE_CNTL 0x00ff +/* DDR-DPR Burst Read Enable */ +#define TW5864_DDR_BRST_EN BIT(13) +/* + * DDR A/B Select as HOST access + * 0 Select DDRA + * 1 Select DDRB + */ +#define TW5864_DDR_AB_SEL BIT(14) +/* + * DDR Access Mode Select + * 0 Single R/W Access (Host <-> DDR) + * 1 Burst R/W Access (Host <-> DPR) + */ +#define TW5864_DDR_MODE BIT(15) + +/* The original frame capture pointer. Two bits for each channel */ +/* SENIF_ORG_FRM_PTR [15:0] */ +#define TW5864_SENIF_ORG_FRM_PTR1 0x0038 +/* SENIF_ORG_FRM_PTR [31:16] */ +#define TW5864_SENIF_ORG_FRM_PTR2 0x003c + +#define TW5864_DSP_SEN_MODE 0x0040 +/* Define controls in register TW5864_DSP_SEN_MODE */ +#define TW5864_DSP_SEN_MODE_CH0 0x000f +#define TW5864_DSP_SEN_MODE_CH1 0x00f0 + +/* + * [15:0]: ENC_BUF_PTR_REC[31:16] Two bit for each channel (channel 8 ~ 15). + * Each two bits are the buffer pointer for the last encoded frame of a channel + */ +#define TW5864_ENC_BUF_PTR_REC2 0x004c + +/* Current MV Flag Status Pointer for Channel n. (Read only) */ +/* + * [1:0] CH0_MV_PTR, ..., [15:14] CH7_MV_PTR + */ +#define TW5864_CH_MV_PTR1 0x0060 +/* + * [1:0] CH8_MV_PTR, ..., [15:14] CH15_MV_PTR + */ +#define TW5864_CH_MV_PTR2 0x0064 + +/* + * [15:0] Reset Current MV Flag Status Pointer for Channel n (one bit each) + */ +#define TW5864_RST_MV_PTR 0x0068 +#define TW5864_INTERLACING 0x0200 +/* Define controls in register TW5864_INTERLACING */ +/* + * Inter_Mode Start. 2-nd bit? A guess. Missing in datasheet. Without this bit + * set, the output video is interlaced (stripy). + */ +#define TW5864_DSP_INTER_ST BIT(1) +/* Deinterlacer Enable */ +#define TW5864_DI_EN BIT(2) +/* + * De-interlacer Mode + * 1 Shuffled frame + * 0 Normal Un-Shuffled Frame + */ +#define TW5864_DI_MD BIT(3) +/* + * Down scale original frame in X direction + * 11: Un-used + * 10: down-sample to 1/4 + * 01: down-sample to 1/2 + * 00: down-sample disabled + */ +#define TW5864_DSP_DWN_X (3 << 4) +/* + * Down scale original frame in Y direction + * 11: Un-used + * 10: down-sample to 1/4 + * 01: down-sample to 1/2 + * 00: down-sample disabled + */ +#define TW5864_DSP_DWN_Y (3 << 6) +/* + * 1 Dual Stream + * 0 Single Stream + */ +#define TW5864_DUAL_STR BIT(8) + +#define TW5864_DSP_REF 0x0204 +/* Define controls in register TW5864_DSP_REF */ +/* Number of reference frame (Default 1 for TW5864B) */ +#define TW5864_DSP_REF_FRM 0x000f +/* Window size */ +#define TW5864_DSP_WIN_SIZE 0x02f0 + +#define TW5864_DSP_SKIP 0x0208 +/* Define controls in register TW5864_DSP_SKIP */ +/* + * Skip Offset Enable bit + * 0 DSP_SKIP_OFFSET value is not used (default 8) + * 1 DSP_SKIP_OFFSET value is used in HW + */ +#define TW5864_DSP_SKIP_OFEN 0x0080 +/* Skip mode cost offset (default 8) */ +#define TW5864_DSP_SKIP_OFFSET 0x007f + +#define TW5864_MOTION_SEARCH_ETC 0x020c +/* Define controls in register TW5864_MOTION_SEARCH_ETC */ +/* Enable quarter pel search mode */ +#define TW5864_QPEL_EN BIT(0) +/* Enable half pel search mode */ +#define TW5864_HPEL_EN BIT(1) +/* Enable motion search mode */ +#define TW5864_ME_EN BIT(2) +/* Enable Intra mode */ +#define TW5864_INTRA_EN BIT(3) +/* Enable Skip Mode */ +#define TW5864_SKIP_EN BIT(4) +/* Search Option (Default 2"b01) */ +#define TW5864_SRCH_OPT (3 << 5) + +#define TW5864_DSP_ENC_REC 0x0210 +/* Define controls in register TW5864_DSP_ENC_REC */ +/* Reference Buffer Pointer for encoding */ +#define TW5864_DSP_ENC_REF_PTR 0x0007 +/* Reconstruct Buffer pointer */ +#define TW5864_DSP_REC_BUF_PTR 0x7000 + +/* [15:0] Lambda Value for H264 */ +#define TW5864_DSP_REF_MVP_LAMBDA 0x0214 + +#define TW5864_DSP_PIC_MAX_MB 0x0218 +/* Define controls in register TW5864_DSP_PIC_MAX_MB */ +/* The MB number in Y direction for a frame */ +#define TW5864_DSP_PIC_MAX_MB_Y 0x007f +/* The MB number in X direction for a frame */ +#define TW5864_DSP_PIC_MAX_MB_X 0x7f00 + +/* The original frame pointer for encoding */ +#define TW5864_DSP_ENC_ORG_PTR_REG 0x021c +/* Mask to use with TW5864_DSP_ENC_ORG_PTR */ +#define TW5864_DSP_ENC_ORG_PTR_MASK 0x7000 +/* Number of bits to shift with TW5864_DSP_ENC_ORG_PTR */ +#define TW5864_DSP_ENC_ORG_PTR_SHIFT 12 + +/* DDR base address of OSD rectangle attribute data */ +#define TW5864_DSP_OSD_ATTRI_BASE 0x0220 +/* OSD enable bit for each channel */ +#define TW5864_DSP_OSD_ENABLE 0x0228 + +/* 0x0280 ~ 0x029c – Motion Vector for 1st 4x4 Block, e.g., 80 (X), 84 (Y) */ +#define TW5864_ME_MV_VEC1 0x0280 +/* 0x02a0 ~ 0x02bc – Motion Vector for 2nd 4x4 Block, e.g., A0 (X), A4 (Y) */ +#define TW5864_ME_MV_VEC2 0x02a0 +/* 0x02c0 ~ 0x02dc – Motion Vector for 3rd 4x4 Block, e.g., C0 (X), C4 (Y) */ +#define TW5864_ME_MV_VEC3 0x02c0 +/* 0x02e0 ~ 0x02fc – Motion Vector for 4th 4x4 Block, e.g., E0 (X), E4 (Y) */ +#define TW5864_ME_MV_VEC4 0x02e0 + +/* + * [5:0] + * if (intra16x16_cost < (intra4x4_cost+dsp_i4x4_offset)) + * Intra_mode = intra16x16_mode + * Else + * Intra_mode = intra4x4_mode + */ +#define TW5864_DSP_I4x4_OFFSET 0x040c + +/* + * [6:4] + * 0x5 Only 4x4 + * 0x6 Only 16x16 + * 0x7 16x16 & 4x4 + */ +#define TW5864_DSP_INTRA_MODE 0x0410 +#define TW5864_DSP_INTRA_MODE_SHIFT 4 +#define TW5864_DSP_INTRA_MODE_MASK (7 << 4) +#define TW5864_DSP_INTRA_MODE_4x4 0x5 +#define TW5864_DSP_INTRA_MODE_16x16 0x6 +#define TW5864_DSP_INTRA_MODE_4x4_AND_16x16 0x7 +/* + * [5:0] WEIGHT Factor for I4x4 cost calculation (QP dependent) + */ +#define TW5864_DSP_I4x4_WEIGHT 0x0414 + +/* + * [7:0] Offset used to affect Intra/ME model decision + * If (me_cost < intra_cost + dsp_resid_mode_offset) + * Pred_Mode = me_mode + * Else + * Pred_mode = intra_mode + */ +#define TW5864_DSP_RESID_MODE_OFFSET 0x0604 + +/* 0x0800 ~ 0x09ff - Quantization TABLE Values */ +#define TW5864_QUAN_TAB 0x0800 + +/* Valid channel value [0; f], frame value [0; 3] */ +#define TW5864_RT_CNTR_CH_FRM(channel, frame) \ + (0x0c00 | (channel << 4) | (frame << 2)) + +#define TW5864_FRAME_BUS1 0x0d00 +/* + * 1 Progressive in part A in bus n + * 0 Interlaced in part A in bus n + */ +#define TW5864_PROG_A BIT(0) +/* + * 1 Progressive in part B in bus n + * 0 Interlaced in part B in bus n + */ +#define TW5864_PROG_B BIT(1) +/* + * 1 Frame Mode in bus n + * 0 Field Mode in bus n + */ +#define TW5864_FRAME BIT(2) +/* + * 0 4CIF in bus n + * 1 1D1 + 4 CIF in bus n + * 2 2D1 in bus n + */ +#define TW5864_BUS_D1 (3 << 3) +/* Bus 1 goes in TW5864_FRAME_BUS1 in [4:0] */ +/* Bus 2 goes in TW5864_FRAME_BUS1 in [12:8] */ +#define TW5864_FRAME_BUS2 0x0d04 +/* Bus 3 goes in TW5864_FRAME_BUS2 in [4:0] */ +/* Bus 4 goes in TW5864_FRAME_BUS2 in [12:8] */ + +/* [15:0] Horizontal Mirror for channel n */ +#define TW5864_SENIF_HOR_MIR 0x0d08 +/* [15:0] Vertical Mirror for channel n */ +#define TW5864_SENIF_VER_MIR 0x0d0c + +/* + * FRAME_WIDTH_BUSn_A + * 0x15f: 4 CIF + * 0x2cf: 1 D1 + 3 CIF + * 0x2cf: 2 D1 + * FRAME_WIDTH_BUSn_B + * 0x15f: 4 CIF + * 0x2cf: 1 D1 + 3 CIF + * 0x2cf: 2 D1 + * FRAME_HEIGHT_BUSn_A + * 0x11f: 4CIF (PAL) + * 0x23f: 1D1 + 3CIF (PAL) + * 0x23f: 2 D1 (PAL) + * 0x0ef: 4CIF (NTSC) + * 0x1df: 1D1 + 3CIF (NTSC) + * 0x1df: 2 D1 (NTSC) + * FRAME_HEIGHT_BUSn_B + * 0x11f: 4CIF (PAL) + * 0x23f: 1D1 + 3CIF (PAL) + * 0x23f: 2 D1 (PAL) + * 0x0ef: 4CIF (NTSC) + * 0x1df: 1D1 + 3CIF (NTSC) + * 0x1df: 2 D1 (NTSC) + */ +#define TW5864_FRAME_WIDTH_BUS_A(bus) (0x0d10 + 0x0010 * bus) +#define TW5864_FRAME_WIDTH_BUS_B(bus) (0x0d14 + 0x0010 * bus) +#define TW5864_FRAME_HEIGHT_BUS_A(bus) (0x0d18 + 0x0010 * bus) +#define TW5864_FRAME_HEIGHT_BUS_B(bus) (0x0d1c + 0x0010 * bus) + +/* + * 1: the bus mapped Channel n Full D1 + * 0: the bus mapped Channel n Half D1 + */ +#define TW5864_FULL_HALF_FLAG 0x0d50 + +/* + * 0 The bus mapped Channel select partA Mode + * 1 The bus mapped Channel select partB Mode + */ +#define TW5864_FULL_HALF_MODE_SEL 0x0d54 + +#define TW5864_VLC 0x1000 +/* Define controls in register TW5864_VLC */ +/* QP Value used by H264 CAVLC */ +#define TW5864_VLC_SLICE_QP 0x003f +/* + * Swap byte order of VLC stream in d-word. + * 1 Normal (VLC output= [31:0]) + * 0 Swap (VLC output={[23:16],[31:24],[7:0], [15:8]}) + */ +#define TW5864_VLC_BYTE_SWP BIT(6) +/* Enable Adding 03 circuit for VLC stream */ +#define TW5864_VLC_ADD03_EN BIT(7) +/* Number of bit for VLC bit Align */ +#define TW5864_VLC_BIT_ALIGN_SHIFT 8 +#define TW5864_VLC_BIT_ALIGN_MASK (0x1f << 8) +/* + * Synchronous Interface select for VLC Stream + * 1 CDC_VLCS_MAS read VLC stream + * 0 CPU read VLC stream + */ +#define TW5864_VLC_INF_SEL BIT(13) +/* Enable VLC overflow control */ +#define TW5864_VLC_OVFL_CNTL BIT(14) +/* + * 1 PCI Master Mode + * 0 Non PCI Master Mode + */ +#define TW5864_VLC_PCI_SEL BIT(15) +/* + * 0 Enable Adding 03 to VLC header and stream + * 1 Disable Adding 03 to VLC header of "00000001" + */ +#define TW5864_VLC_A03_DISAB BIT(16) +/* + * Status of VLC stream in DDR (one bit for each buffer) + * 1 VLC is ready in buffer n (HW set) + * 0 VLC is not ready in buffer n (SW clear) + */ +#define TW5864_VLC_BUF_RDY_SHIFT 24 +#define TW5864_VLC_BUF_RDY_MASK (0xff << 24) + +/* Total number of bit in the slice */ +#define TW5864_SLICE_TOTAL_BIT 0x1004 +/* Total number of bit in the residue */ +#define TW5864_RES_TOTAL_BIT 0x1008 + +#define TW5864_VLC_BUF 0x100c +/* Define controls in register TW5864_VLC_BUF */ +/* VLC BK0 full status, write ‘1’ to clear */ +#define TW5864_VLC_BK0_FULL BIT(0) +/* VLC BK1 full status, write ‘1’ to clear */ +#define TW5864_VLC_BK1_FULL BIT(1) +/* VLC end slice status, write ‘1’ to clear */ +#define TW5864_VLC_END_SLICE BIT(2) +/* VLC Buffer overflow status, write ‘1’ to clear */ +#define TW5864_DSP_RD_OF BIT(3) +/* VLC string length in either buffer 0 or 1 at end of frame */ +#define TW5864_VLC_STREAM_LEN_SHIFT 4 +#define TW5864_VLC_STREAM_LEN_MASK (0x1ff << 4) + +/* [15:0] Total coefficient number in a frame */ +#define TW5864_TOTAL_COEF_NO 0x1010 +/* [0] VLC Encoder Interrupt. Write ‘1’ to clear */ +#define TW5864_VLC_DSP_INTR 0x1014 +/* [31:0] VLC stream CRC checksum */ +#define TW5864_VLC_STREAM_CRC 0x1018 + +#define TW5864_VLC_RD 0x101c +/* Define controls in register TW5864_VLC_RD */ +/* + * 1 Read VLC lookup Memory + * 0 Read VLC Stream Memory + */ +#define TW5864_VLC_RD_MEM BIT(0) +/* + * 1 Read VLC Stream Memory in burst mode + * 0 Read VLC Stream Memory in single mode + */ +#define TW5864_VLC_RD_BRST BIT(1) + +/* 0x2000 ~ 0x2ffc -- H264 Stream Memory Map */ +/* + * A word is 4 bytes. I.e., + * VLC_STREAM_MEM[0] address: 0x2000 + * VLC_STREAM_MEM[1] address: 0x2004 + * ... + * VLC_STREAM_MEM[3FF] address: 0x2ffc + */ +#define TW5864_VLC_STREAM_MEM_START 0x2000 +#define TW5864_VLC_STREAM_MEM_MAX_OFFSET 0x3ff +#define TW5864_VLC_STREAM_MEM(offset) (TW5864_VLC_STREAM_MEM_START + 4 * offset) + +/* 0x4000 ~ 0x4ffc -- Audio Register Map */ +/* [31:0] config 1ms cnt = Realtime clk/1000 */ +#define TW5864_CFG_1MS_CNT 0x4000 + +#define TW5864_ADPCM 0x4004 +/* Define controls in register TW5864_ADPCM */ +/* ADPCM decoder enable */ +#define TW5864_ADPCM_DEC BIT(0) +/* ADPCM input data enable */ +#define TW5864_ADPCM_IN_DATA BIT(1) +/* ADPCM encoder enable */ +#define TW5864_ADPCM_ENC BIT(2) + +#define TW5864_AUD 0x4008 +/* Define controls in register TW5864_AUD */ +/* Record path PCM Audio enable bit for each channel */ +#define TW5864_AUD_ORG_CH_EN 0x00ff +/* Speaker path PCM Audio Enable */ +#define TW5864_SPK_ORG_EN BIT(16) +/* + * 0 16bit + * 1 8bit + */ +#define TW5864_AD_BIT_MODE BIT(17) +#define TW5864_AUD_TYPE_SHIFT 18 +/* + * 0 PCM + * 3 ADPCM + */ +#define TW5864_AUD_TYPE (0xf << 18) +#define TW5864_AUD_SAMPLE_RATE_SHIFT 22 +/* + * 0 8K + * 1 16K + */ +#define TW5864_AUD_SAMPLE_RATE (3 << 22) +/* Channel ID used to select audio channel (0 to 16) for loopback */ +#define TW5864_TESTLOOP_CHID_SHIFT 24 +#define TW5864_TESTLOOP_CHID (0x1f << 24) +/* Enable AD Loopback Test */ +#define TW5864_TEST_ADLOOP_EN BIT(30) +/* + * 0 Asynchronous Mode or PCI target mode + * 1 PCI Initiator Mode + */ +#define TW5864_AUD_MODE BIT(31) + +#define TW5864_AUD_ADPCM 0x400c +/* Define controls in register TW5864_AUD_ADPCM */ +/* Record path ADPCM audio channel enable, one bit for each */ +#define TW5864_AUD_ADPCM_CH_EN 0x00ff +/* Speaker path ADPCM audio channel enable */ +#define TW5864_SPK_ADPCM_EN BIT(16) + +#define TW5864_PC_BLOCK_ADPCM_RD_NO 0x4018 +#define TW5864_PC_BLOCK_ADPCM_RD_NO_MASK 0x1f + +/* + * For ADPCM_ENC_WR_PTR, ADPCM_ENC_RD_PTR (see below): + * Bit[2:0] ch0 + * Bit[5:3] ch1 + * Bit[8:6] ch2 + * Bit[11:9] ch3 + * Bit[14:12] ch4 + * Bit[17:15] ch5 + * Bit[20:18] ch6 + * Bit[23:21] ch7 + * Bit[26:24] ch8 + * Bit[29:27] ch9 + * Bit[32:30] ch10 + * Bit[35:33] ch11 + * Bit[38:36] ch12 + * Bit[41:39] ch13 + * Bit[44:42] ch14 + * Bit[47:45] ch15 + * Bit[50:48] ch16 + */ +#define TW5864_ADPCM_ENC_XX_MASK 0x3fff +#define TW5864_ADPCM_ENC_XX_PTR2_SHIFT 30 +/* ADPCM_ENC_WR_PTR[29:0] */ +#define TW5864_ADPCM_ENC_WR_PTR1 0x401c +/* ADPCM_ENC_WR_PTR[50:30] */ +#define TW5864_ADPCM_ENC_WR_PTR2 0x4020 + +/* ADPCM_ENC_RD_PTR[29:0] */ +#define TW5864_ADPCM_ENC_RD_PTR1 0x4024 +/* ADPCM_ENC_RD_PTR[50:30] */ +#define TW5864_ADPCM_ENC_RD_PTR2 0x4028 + +/* [3:0] rd ch0, [7:4] rd ch1, [11:8] wr ch0, [15:12] wr ch1 */ +#define TW5864_ADPCM_DEC_RD_WR_PTR 0x402c + +/* + * For TW5864_AD_ORIG_WR_PTR, TW5864_AD_ORIG_RD_PTR: + * Bit[3:0] ch0 + * Bit[7:4] ch1 + * Bit[11:8] ch2 + * Bit[15:12] ch3 + * Bit[19:16] ch4 + * Bit[23:20] ch5 + * Bit[27:24] ch6 + * Bit[31:28] ch7 + * Bit[35:32] ch8 + * Bit[39:36] ch9 + * Bit[43:40] ch10 + * Bit[47:44] ch11 + * Bit[51:48] ch12 + * Bit[55:52] ch13 + * Bit[59:56] ch14 + * Bit[63:60] ch15 + * Bit[67:64] ch16 + */ +/* AD_ORIG_WR_PTR[31:0] */ +#define TW5864_AD_ORIG_WR_PTR1 0x4030 +/* AD_ORIG_WR_PTR[63:32] */ +#define TW5864_AD_ORIG_WR_PTR2 0x4034 +/* AD_ORIG_WR_PTR[67:64] */ +#define TW5864_AD_ORIG_WR_PTR3 0x4038 + +/* AD_ORIG_RD_PTR[31:0] */ +#define TW5864_AD_ORIG_RD_PTR1 0x403c +/* AD_ORIG_RD_PTR[63:32] */ +#define TW5864_AD_ORIG_RD_PTR2 0x4040 +/* AD_ORIG_RD_PTR[67:64] */ +#define TW5864_AD_ORIG_RD_PTR3 0x4044 + +#define TW5864_PC_BLOCK_ORIG_RD_NO 0x4048 +#define TW5864_PC_BLOCK_ORIG_RD_NO_MASK 0x1f + +#define TW5864_PCI_AUD 0x404c +/* Define controls in register TW5864_PCI_AUD */ +/* + * The register is applicable to PCI initiator mode only. Used to select PCM(0) + * or ADPCM(1) audio data sent to PC. One bit for each channel + */ +#define TW5864_PCI_DATA_SEL 0xffff +/* + * Audio flow control mode selection bit. + * 0 Flow control disabled. TW5864 continuously sends audio frame to PC + * (initiator mode) + * 1 Flow control enabled + */ +#define TW5864_PCI_FLOW_EN BIT(16) +/* + * When PCI_FLOW_EN is set, PCI need to toggle this bit to send an audio frame + * to PC. One toggle to send one frame. + */ +#define TW5864_PCI_AUD_FRM_EN BIT(17) + +/* [1:0] CS valid to data valid CLK cycles when writing operation */ +#define TW5864_CS2DAT_CNT 0x8000 +/* [2:0] Data valid signal width by system clock cycles */ +#define TW5864_DATA_VLD_WIDTH 0x8004 + +#define TW5864_SYNC 0x8008 +/* Define controls in register TW5864_SYNC */ +/* + * 0 vlc stream to syncrous port + * 1 vlc stream to ddr buffers + */ +#define TW5864_SYNC_CFG BIT(7) +/* + * 0 SYNC Address sampled on Rising edge + * 1 SYNC Address sampled on Falling edge + */ +#define TW5864_SYNC_ADR_EDGE BIT(0) +#define TW5864_VLC_STR_DELAY_SHIFT 1 +/* + * 0 No system delay + * 1 One system clock delay + * 2 Two system clock delay + * 3 Three system clock delay + */ +#define TW5864_VLC_STR_DELAY (3 << 1) +/* + * 0 Rising edge output + * 1 Falling edge output + */ +#define TW5864_VLC_OUT_EDGE BIT(3) + +/* + * [1:0] + * 2’b00 phase set to 180 degree + * 2’b01 phase set to 270 degree + * 2’b10 phase set to 0 degree + * 2’b11 phase set to 90 degree + */ +#define TW5864_I2C_PHASE_CFG 0x800c + +/* + * The system / DDR clock (166 MHz) is generated with an on-chip system clock + * PLL (SYSPLL) using input crystal clock of 27 MHz. The system clock PLL + * frequency is controlled with the following equation. + * CLK_OUT = CLK_IN * (M+1) / ((N+1) * P) + * SYSPLL_M M parameter + * SYSPLL_N N parameter + * SYSPLL_P P parameter + */ +/* SYSPLL_M[7:0] */ +#define TW5864_SYSPLL1 0x8018 +/* Define controls in register TW5864_SYSPLL1 */ +#define TW5864_SYSPLL_M_LOW 0x00ff + +/* [2:0]: SYSPLL_M[10:8], [7:3]: SYSPLL_N[4:0] */ +#define TW5864_SYSPLL2 0x8019 +/* Define controls in register TW5864_SYSPLL2 */ +#define TW5864_SYSPLL_M_HI 0x07 +#define TW5864_SYSPLL_N_LOW_SHIFT 3 +#define TW5864_SYSPLL_N_LOW (0x1f << 3) + +/* + * [1:0]: SYSPLL_N[6:5], [3:2]: SYSPLL_P, [4]: SYSPLL_IREF, [7:5]: SYSPLL_CP_SEL + */ +#define TW5864_SYSPLL3 0x8020 +/* Define controls in register TW5864_SYSPLL3 */ +#define TW5864_SYSPLL_N_HI 0x03 +#define TW5864_SYSPLL_P_SHIFT 2 +#define TW5864_SYSPLL_P (0x03 << 2) +/* + * SYSPLL bias current control + * 0 Lower current (default) + * 1 30% higher current + */ +#define TW5864_SYSPLL_IREF BIT(4) +/* + * SYSPLL charge pump current selection + * 0 1,5 uA + * 1 4 uA + * 2 9 uA + * 3 19 uA + * 4 39 uA + * 5 79 uA + * 6 159 uA + * 7 319 uA + */ +#define TW5864_SYSPLL_CP_SEL_SHIFT 5 +#define TW5864_SYSPLL_CP_SEL (0x07 << 5) + +/* + * [1:0]: SYSPLL_VCO, [3:2]: SYSPLL_LP_X8, [5:4]: SYSPLL_ICP_SEL, + * [6]: SYSPLL_LPF_5PF, [7]: SYSPLL_ED_SEL + */ +#define TW5864_SYSPLL4 0x8021 +/* Define controls in register TW5864_SYSPLL4 */ +/* + * SYSPLL_VCO VCO Range selection + * 00 5 ~ 75 MHz + * 01 50 ~ 140 MHz + * 10 110 ~ 320 MHz + * 11 270 ~ 700 MHz + */ +#define TW5864_SYSPLL_VCO 0x03 +#define TW5864_SYSPLL_LP_X8_SHIFT 2 +/* + * Loop resister + * 0 38.5K ohms + * 1 6.6K ohms (default) + * 2 2.2K ohms + * 3 1.1K ohms + */ +#define TW5864_SYSPLL_LP_X8 (0x03 << 2) +#define TW5864_SYSPLL_ICP_SEL_SHIFT 4 +/* + * PLL charge pump fine tune + * 00 x1 (default) + * 01 x1/2 + * 10 x1/7 + * 11 x1/8 + */ +#define TW5864_SYSPLL_ICP_SEL (0x03 << 4) +/* + * PLL low pass filter phase margin adjustment + * 0 no 5pF (default) + * 1 5pF added + */ +#define TW5864_SYSPLL_LPF_5PF BIT(6) +/* + * PFD select edge for detection + * 0 Falling edge (default) + * 1 Rising edge + */ +#define TW5864_SYSPLL_ED_SEL BIT(7) + +/* [0]: SYSPLL_RST, [4]: SYSPLL_PD */ +#define TW5864_SYSPLL5 0x8024 +/* Define controls in register TW5864_SYSPLL5 */ +/* Reset SYSPLL */ +#define TW5864_SYSPLL_RST BIT(0) +/* Power down SYSPLL */ +#define TW5864_SYSPLL_PD BIT(4) + +#define TW5864_PLL_CFG 0x801c +/* Define controls in register TW5864_PLL_CFG */ +/* + * Issue Soft Reset from Async Host Interface / PCI Interface clock domain. + * Become valid after sync to the xtal clock domain. This bit is set only if + * LOAD register bit is also set to 1. + */ +#define TW5864_SRST BIT(0) +/* + * Issue SYSPLL (166 MHz) configuration latch from Async host interface / PCI + * Interface clock domain. The configuration setting becomes effective only if + * LOAD register bit is also set to 1. + */ +#define TW5864_SYSPLL_CFG BIT(2) +/* + * Issue SPLL (108 MHz) configuration load from Async host interface / PCI + * Interface clock domain. The configuration setting becomes effective only if + * the LOAD register bit is also set to 1. + */ +#define TW5864_SPLL_CFG BIT(4) +/* + * Set this bit to latch the SRST, SYSPLL_CFG, SPLL_CFG setting into the xtal + * clock domain to restart the PLL. This bit is self cleared. + */ +#define TW5864_LOAD BIT(3) + +/* SPLL_IREF, SPLL_LPX4, SPLL_CPX4, SPLL_PD, SPLL_DBG */ +#define TW5864_SPLL 0x8028 + +/* 0x8800 ~ 0x88fc -- Interrupt Register Map */ +/* + * Trigger mode of interrupt source 0 ~ 15 + * 1 Edge trigger mode + * 0 Level trigger mode + */ +#define TW5864_TRIGGER_MODE_L 0x8800 +/* Trigger mode of interrupt source 16 ~ 31 */ +#define TW5864_TRIGGER_MODE_H 0x8804 +/* Enable of interrupt source 0 ~ 15 */ +#define TW5864_INTR_ENABLE_L 0x8808 +/* Enable of interrupt source 16 ~ 31 */ +#define TW5864_INTR_ENABLE_H 0x880c +/* Clear interrupt command of interrupt source 0 ~ 15 */ +#define TW5864_INTR_CLR_L 0x8810 +/* Clear interrupt command of interrupt source 16 ~ 31 */ +#define TW5864_INTR_CLR_H 0x8814 +/* + * Assertion of interrupt source 0 ~ 15 + * 1 High level or pos-edge is assertion + * 0 Low level or neg-edge is assertion + */ +#define TW5864_INTR_ASSERT_L 0x8818 +/* Assertion of interrupt source 16 ~ 31 */ +#define TW5864_INTR_ASSERT_H 0x881c +/* + * Output level of interrupt + * 1 Interrupt output is high assertion + * 0 Interrupt output is low assertion + */ +#define TW5864_INTR_OUT_LEVEL 0x8820 +/* + * Status of interrupt source 0 ~ 15 + * Bit[0]: VLC 4k RAM interrupt + * Bit[1]: BURST DDR RAM interrupt + * Bit[2]: MV DSP interrupt + * Bit[3]: video lost interrupt + * Bit[4]: gpio 0 interrupt + * Bit[5]: gpio 1 interrupt + * Bit[6]: gpio 2 interrupt + * Bit[7]: gpio 3 interrupt + * Bit[8]: gpio 4 interrupt + * Bit[9]: gpio 5 interrupt + * Bit[10]: gpio 6 interrupt + * Bit[11]: gpio 7 interrupt + * Bit[12]: JPEG interrupt + * Bit[13:15]: Reserved + */ +#define TW5864_INTR_STATUS_L 0x8838 +/* + * Status of interrupt source 16 ~ 31 + * Bit[0]: Reserved + * Bit[1]: VLC done interrupt + * Bit[2]: Reserved + * Bit[3]: AD Vsync interrupt + * Bit[4]: Preview eof interrupt + * Bit[5]: Preview overflow interrupt + * Bit[6]: Timer interrupt + * Bit[7]: Reserved + * Bit[8]: Audio eof interrupt + * Bit[9]: I2C done interrupt + * Bit[10]: AD interrupt + * Bit[11:15]: Reserved + */ +#define TW5864_INTR_STATUS_H 0x883c + +/* Defines of interrupt bits, united for both low and high word registers */ +#define TW5864_INTR_VLC_RAM BIT(0) +#define TW5864_INTR_BURST BIT(1) +#define TW5864_INTR_MV_DSP BIT(2) +#define TW5864_INTR_VIN_LOST BIT(3) +/* n belongs to [0; 7] */ +#define TW5864_INTR_GPIO(n) (1 << (4 + n)) +#define TW5864_INTR_JPEG BIT(12) +#define TW5864_INTR_VLC_DONE BIT(17) +#define TW5864_INTR_AD_VSYNC BIT(19) +#define TW5864_INTR_PV_EOF BIT(20) +#define TW5864_INTR_PV_OVERFLOW BIT(21) +#define TW5864_INTR_TIMER BIT(22) +#define TW5864_INTR_AUD_EOF BIT(24) +#define TW5864_INTR_I2C_DONE BIT(25) +#define TW5864_INTR_AD BIT(26) + +/* 0x9000 ~ 0x920c -- Video Capture (VIF) Register Map */ +/* + * H264EN_CH_STATUS[n] Status of Vsync synchronized H264EN_CH_EN (Read Only) + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_H264EN_CH_STATUS 0x9000 +/* + * [15:0] H264EN_CH_EN[n] H264 Encoding Path Enable for channel + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_H264EN_CH_EN 0x9004 +/* + * H264EN_CH_DNS[n] H264 Encoding Path Downscale Video Decoder Input for + * channel n + * 1 Downscale Y to 1/2 + * 0 Does not downscale + */ +#define TW5864_H264EN_CH_DNS 0x9008 +/* + * H264EN_CH_PROG[n] H264 Encoding Path channel n is progressive + * 1 Progressive (Not valid for TW5864) + * 0 Interlaced (TW5864 default) + */ +#define TW5864_H264EN_CH_PROG 0x900c +/* + * [3:0] H264EN_BUS_MAX_CH[n] + * H264 Encoding Path maximum number of channel on BUS n + * 0 Max 4 channels + * 1 Max 2 channels + */ +#define TW5864_H264EN_BUS_MAX_CH 0x9010 + +/* + * H264EN_RATE_MAX_LINE_n H264 Encoding path Rate Mapping Maximum Line Number + * on Bus n + */ +#define TW5864_H264EN_RATE_MAX_LINE_EVEN 0x1f +#define TW5864_H264EN_RATE_MAX_LINE_ODD_SHIFT 5 +#define TW5864_H264EN_RATE_MAX_LINE_ODD (0x1f << 5) +/* + * [4:0] H264EN_RATE_MAX_LINE_0 + * [9:5] H264EN_RATE_MAX_LINE_1 + */ +#define TW5864_H264EN_RATE_MAX_LINE_REG1 0x9014 +/* + * [4:0] H264EN_RATE_MAX_LINE_2 + * [9:5] H264EN_RATE_MAX_LINE_3 + */ +#define TW5864_H264EN_RATE_MAX_LINE_REG2 0x9018 + +/* + * H264EN_CHn_FMT H264 Encoding Path Format configuration of Channel n + * 00 D1 (For D1 and hD1 frame) + * 01 (Reserved) + * 10 (Reserved) + * 11 D1 with 1/2 size in X (for CIF frame) + * Note: To be used with 0x9008 register to configure the frame size + */ +/* + * [1:0]: H264EN_CH0_FMT, + * ..., [15:14]: H264EN_CH7_FMT + */ +#define TW5864_H264EN_CH_FMT_REG1 0x9020 +/* + * [1:0]: H264EN_CH8_FMT (?), + * ..., [15:14]: H264EN_CH15_FMT (?) + */ +#define TW5864_H264EN_CH_FMT_REG2 0x9024 + +/* + * H264EN_RATE_CNTL_BUSm_CHn H264 Encoding Path BUS m Rate Control for Channel n + */ +#define TW5864_H264EN_RATE_CNTL_LO_WORD(bus, channel) \ + (0x9100 + bus * 0x20 + channel * 0x08) +#define TW5864_H264EN_RATE_CNTL_HI_WORD(bus, channel) \ + (0x9104 + bus * 0x20 + channel * 0x08) + +/* + * H264EN_BUSm_MAP_CHn The 16-to-1 MUX configuration register for each encoding + * channel (total of 16 channels). Four bits for each channel. + */ +#define TW5864_H264EN_BUS0_MAP 0x9200 +#define TW5864_H264EN_BUS1_MAP 0x9204 +#define TW5864_H264EN_BUS2_MAP 0x9208 +#define TW5864_H264EN_BUS3_MAP 0x920c + +/* This register is not defined in datasheet, but used in reference driver */ +#define TW5864_UNDECLARED_ERROR_FLAGS_0x9218 0x9218 + +#define TW5864_GPIO1 0x9800 +#define TW5864_GPIO2 0x9804 +/* Define controls in registers TW5864_GPIO1, TW5864_GPIO2 */ +/* GPIO DATA of Group n */ +#define TW5864_GPIO_DATA 0x00ff +#define TW5864_GPIO_OEN_SHIFT 8 +/* GPIO Output Enable of Group n */ +#define TW5864_GPIO_OEN (0xff << 8) + +/* 0xa000 ~ 0xa8ff – DDR Controller Register Map */ +/* DDR Controller A */ +/* + * [2:0] Data valid counter after read command to DDR. This is the delay value + * to show how many cycles the data will be back from DDR after we issue a read + * command. + */ +#define TW5864_RD_ACK_VLD_MUX 0xa000 + +#define TW5864_DDR_PERIODS 0xa004 +/* Define controls in register TW5864_DDR_PERIODS */ +/* + * Tras value, the minimum cycle of active to precharge command period, + * default is 7 + */ +#define TW5864_TRAS_CNT_MAX 0x000f +/* + * Trfc value, the minimum cycle of refresh to active or refresh command period, + * default is 4"hf + */ +#define TW5864_RFC_CNT_MAX_SHIFT 8 +#define TW5864_RFC_CNT_MAX (0x0f << 8) +/* + * Trcd value, the minimum cycle of active to internal read/write command + * period, default is 4"h2 + */ +#define TW5864_TCD_CNT_MAX_SHIFT 4 +#define TW5864_TCD_CNT_MAX (0x0f << 4) +/* Twr value, write recovery time, default is 4"h3 */ +#define TW5864_TWR_CNT_MAX_SHIFT 12 +#define TW5864_TWR_CNT_MAX (0x0f << 12) + +/* + * [2:0] CAS latency, the delay cycle between internal read command and the + * availability of the first bit of output data, default is 3 + */ +#define TW5864_CAS_LATENCY 0xa008 +/* + * [15:0] Maximum average periodic refresh, the value is based on the current + * frequency to match 7.8mcs + */ +#define TW5864_DDR_REF_CNTR_MAX 0xa00c +/* + * DDR_ON_CHIP_MAP [1:0] + * 0 256M DDR on board + * 1 512M DDR on board + * 2 1G DDR on board + * DDR_ON_CHIP_MAP [2] + * 0 Only one DDR chip + * 1 Two DDR chips + */ +#define TW5864_DDR_ON_CHIP_MAP 0xa01c +#define TW5864_DDR_SELFTEST_MODE 0xa020 +/* Define controls in register TW5864_DDR_SELFTEST_MODE */ +/* + * 0 Common read/write mode + * 1 DDR self-test mode + */ +#define TW5864_MASTER_MODE BIT(0) +/* + * 0 DDR self-test single read/write + * 1 DDR self-test burst read/write + */ +#define TW5864_SINGLE_PROC BIT(1) +/* + * 0 DDR self-test write command + * 1 DDR self-test read command + */ +#define TW5864_WRITE_FLAG BIT(2) +#define TW5864_DATA_MODE_SHIFT 4 +/* + * 0 write 32'haaaa5555 to DDR + * 1 write 32'hffffffff to DDR + * 2 write 32'hha5a55a5a to DDR + * 3 write increasing data to DDR + */ +#define TW5864_DATA_MODE (0x3 << 4) + +/* [7:0] The maximum data of one burst in DDR self-test mode */ +#define TW5864_BURST_CNTR_MAX 0xa024 +/* [15:0] The maximum burst counter (bit 15~0) in DDR self-test mode */ +#define TW5864_DDR_PROC_CNTR_MAX_L 0xa028 +/* The maximum burst counter (bit 31~16) in DDR self-test mode */ +#define TW5864_DDR_PROC_CNTR_MAX_H 0xa02c +/* [0]: Start one DDR self-test */ +#define TW5864_DDR_SELF_TEST_CMD 0xa030 +/* The maximum error counter (bit 15 ~ 0) in DDR self-test */ +#define TW5864_ERR_CNTR_L 0xa034 + +#define TW5864_ERR_CNTR_H_AND_FLAG 0xa038 +/* Define controls in register TW5864_ERR_CNTR_H_AND_FLAG */ +/* The maximum error counter (bit 30 ~ 16) in DDR self-test */ +#define TW5864_ERR_CNTR_H_MASK 0x3fff +/* DDR self-test end flag */ +#define TW5864_END_FLAG 0x8000 + +/* + * DDR Controller B: same as 0xa000 ~ 0xa038, but add TW5864_DDR_B_OFFSET to all + * addresses + */ +#define TW5864_DDR_B_OFFSET 0x0800 + +/* 0xb004 ~ 0xb018 – HW version/ARB12 Register Map */ +/* [15:0] Default is C013 */ +#define TW5864_HW_VERSION 0xb004 + +#define TW5864_REQS_ENABLE 0xb010 +/* Define controls in register TW5864_REQS_ENABLE */ +/* Audio data in to DDR enable (default 1) */ +#define TW5864_AUD_DATA_IN_ENB BIT(0) +/* Audio encode request to DDR enable (default 1) */ +#define TW5864_AUD_ENC_REQ_ENB BIT(1) +/* Audio decode request0 to DDR enable (default 1) */ +#define TW5864_AUD_DEC_REQ0_ENB BIT(2) +/* Audio decode request1 to DDR enable (default 1) */ +#define TW5864_AUD_DEC_REQ1_ENB BIT(3) +/* VLC stream request to DDR enable (default 1) */ +#define TW5864_VLC_STRM_REQ_ENB BIT(4) +/* H264 MV request to DDR enable (default 1) */ +#define TW5864_DVM_MV_REQ_ENB BIT(5) +/* mux_core MVD request to DDR enable (default 1) */ +#define TW5864_MVD_REQ_ENB BIT(6) +/* mux_core MVD temp data request to DDR enable (default 1) */ +#define TW5864_MVD_TMP_REQ_ENB BIT(7) +/* JPEG request to DDR enable (default 1) */ +#define TW5864_JPEG_REQ_ENB BIT(8) +/* mv_flag request to DDR enable (default 1) */ +#define TW5864_MV_FLAG_REQ_ENB BIT(9) + +#define TW5864_ARB12 0xb018 +/* Define controls in register TW5864_ARB12 */ +/* ARB12 Enable (default 1) */ +#define TW5864_ARB12_ENB BIT(15) +/* ARB12 maximum value of time out counter (default 15"h1FF) */ +#define TW5864_ARB12_TIME_OUT_CNT 0x7fff + +/* 0xb800 ~ 0xb80c -- Indirect Access Register Map */ +/* + * Spec says: + * In order to access the indirect register space, the following procedure is + * followed. + * But reference driver implementation, and current driver, too, does it + * differently. + * + * Write Registers: + * (1) Write IND_DATA at 0xb804 ~ 0xb807 + * (2) Read BUSY flag from 0xb803. Wait until BUSY signal is 0. + * (3) Write IND_ADDR at 0xb800 ~ 0xb801. Set R/W to "1", ENABLE to "1" + * Read Registers: + * (1) Read BUSY flag from 0xb803. Wait until BUSY signal is 0. + * (2) Write IND_ADDR at 0xb800 ~ 0xb801. Set R/W to "0", ENABLE to "1" + * (3) Read BUSY flag from 0xb803. Wait until BUSY signal is 0. + * (4) Read IND_DATA from 0xb804 ~ 0xb807 + */ +#define TW5864_IND_CTL 0xb800 +/* Define controls in register TW5864_IND_CTL */ +/* Address used to access indirect register space */ +#define TW5864_IND_ADDR 0x0000ffff +/* Wait until this bit is "0" before using indirect access */ +#define TW5864_BUSY BIT(31) +/* Activate the indirect access. This bit is self cleared */ +#define TW5864_ENABLE BIT(25) +/* Read/Write command */ +#define TW5864_RW BIT(24) + +/* [31:0] Data used to read/write indirect register space */ +#define TW5864_IND_DATA 0xb804 + +/* 0xc000 ~ 0xc7fc -- Preview Register Map */ +/* Mostly skipped this section. */ +/* + * [15:0] Status of Vsync Synchronized PCI_PV_CH_EN (Read Only) + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_PCI_PV_CH_STATUS 0xc000 +/* + * [15:0] PCI Preview Path Enable for channel n + * 1 Channel Enable + * 0 Channel Disable + */ +#define TW5864_PCI_PV_CH_EN 0xc004 + +/* 0xc800 ~ 0xc804 -- JPEG Capture Register Map */ +/* Skipped. */ +/* 0xd000 ~ 0xd0fc -- JPEG Control Register Map */ +/* Skipped. */ + +/* 0xe000 ~ 0xfc04 – Motion Vector Register Map */ + +/* ME Motion Vector data (Four Byte Each) 0xe000 ~ 0xe7fc */ +#define TW5864_ME_MV_VEC_START 0xe000 +#define TW5864_ME_MV_VEC_MAX_OFFSET 0x1ff +#define TW5864_ME_MV_VEC(offset) (TW5864_ME_MV_VEC_START + 4 * offset) + +#define TW5864_MV 0xfc00 +/* Define controls in register TW5864_MV */ +/* mv bank0 full status , write "1" to clear */ +#define TW5864_MV_BK0_FULL BIT(0) +/* mv bank1 full status , write "1" to clear */ +#define TW5864_MV_BK1_FULL BIT(1) +/* slice end status; write "1" to clear */ +#define TW5864_MV_EOF BIT(2) +/* mv encode interrupt status; write "1" to clear */ +#define TW5864_MV_DSP_INTR BIT(3) +/* mv write memory overflow, write "1" to clear */ +#define TW5864_DSP_WR_OF BIT(4) +#define TW5864_MV_LEN_SHIFT 5 +/* mv stream length */ +#define TW5864_MV_LEN (0xff << 5) +/* The configured status bit written into bit 15 of 0xfc04 */ +#define TW5864_MPI_DDR_SEL BIT(13) + +#define TW5864_MPI_DDR_SEL_REG 0xfc04 +/* Define controls in register TW5864_MPI_DDR_SEL_REG */ +/* + * SW configure register + * 0 MV is saved in internal DPR + * 1 MV is saved in DDR + */ +#define TW5864_MPI_DDR_SEL2 BIT(15) + +/* 0x18000 ~ 0x181fc – PCI Master/Slave Control Map */ +#define TW5864_PCI_INTR_STATUS 0x18000 +/* Define controls in register TW5864_PCI_INTR_STATUS */ +/* vlc done */ +#define TW5864_VLC_DONE_INTR BIT(1) +/* ad vsync */ +#define TW5864_AD_VSYNC_INTR BIT(3) +/* preview eof */ +#define TW5864_PREV_EOF_INTR BIT(4) +/* preview overflow interrupt */ +#define TW5864_PREV_OVERFLOW_INTR BIT(5) +/* timer interrupt */ +#define TW5864_TIMER_INTR BIT(6) +/* audio eof */ +#define TW5864_AUDIO_EOF_INTR BIT(8) +/* IIC done */ +#define TW5864_IIC_DONE_INTR BIT(24) +/* ad interrupt (e.g.: video lost, video format changed) */ +#define TW5864_AD_INTR_REG BIT(25) + +#define TW5864_PCI_INTR_CTL 0x18004 +/* Define controls in register TW5864_PCI_INTR_CTL */ +/* master enable */ +#define TW5864_PCI_MAST_ENB BIT(0) +/* mvd&vlc master enable */ +#define TW5864_MVD_VLC_MAST_ENB 0x06 +/* (Need to set 0 in TW5864A) */ +#define TW5864_AD_MAST_ENB BIT(3) +/* preview master enable */ +#define TW5864_PREV_MAST_ENB BIT(4) +/* preview overflow enable */ +#define TW5864_PREV_OVERFLOW_ENB BIT(5) +/* timer interrupt enable */ +#define TW5864_TIMER_INTR_ENB BIT(6) +/* JPEG master (push mode) enable */ +#define TW5864_JPEG_MAST_ENB BIT(7) +#define TW5864_AU_MAST_ENB_CHN_SHIFT 8 +/* audio master channel enable */ +#define TW5864_AU_MAST_ENB_CHN (0xffff << 8) +/* IIC interrupt enable */ +#define TW5864_IIC_INTR_ENB BIT(24) +/* ad interrupt enable */ +#define TW5864_AD_INTR_ENB BIT(25) +/* target burst enable */ +#define TW5864_PCI_TAR_BURST_ENB BIT(26) +/* vlc stream burst enable */ +#define TW5864_PCI_VLC_BURST_ENB BIT(27) +/* ddr burst enable (1 enable, and must set DDR_BRST_EN) */ +#define TW5864_PCI_DDR_BURST_ENB BIT(28) + +/* + * Because preview and audio have 16 channels separately, so using this + * registers to indicate interrupt status for every channels. This is secondary + * interrupt status register. OR operating of the PREV_INTR_REG is + * PREV_EOF_INTR, OR operating of the AU_INTR_REG bits is AUDIO_EOF_INTR + */ +#define TW5864_PREV_AND_AU_INTR 0x18008 +/* Define controls in register TW5864_PREV_AND_AU_INTR */ +/* preview eof interrupt flag */ +#define TW5864_PREV_INTR_REG 0x0000ffff +#define TW5864_AU_INTR_REG_SHIFT 16 +/* audio eof interrupt flag */ +#define TW5864_AU_INTR_REG (0xffff << 16) + +#define TW5864_MASTER_ENB_REG 0x1800c +/* Define controls in register TW5864_MASTER_ENB_REG */ +/* master enable */ +#define TW5864_PCI_VLC_INTR_ENB BIT(1) +/* mvd and vlc master enable */ +#define TW5864_PCI_PREV_INTR_ENB BIT(4) +/* ad vsync master enable */ +#define TW5864_PCI_PREV_OF_INTR_ENB BIT(5) +/* jpeg master enable */ +#define TW5864_PCI_JPEG_INTR_ENB BIT(7) +/* preview master enable */ +#define TW5864_PCI_AUD_INTR_ENB BIT(8) + +/* + * Every channel of preview and audio have ping-pong buffers in system memory, + * this register is the buffer flag to notify software which buffer is been + * operated. + */ +#define TW5864_PREV_AND_AU_BUF_FLAG 0x18010 +/* Define controls in register TW5864_PREV_AND_AU_BUF_FLAG */ +/* preview buffer A/B flag */ +#define TW5864_PREV_BUF_FLAG 0xffff +#define TW5864_AUDIO_BUF_FLAG_SHIFT 16 +/* audio buffer A/B flag */ +#define TW5864_AUDIO_BUF_FLAG (0xffff << 16) + +#define TW5864_IIC 0x18014 +/* Define controls in register TW5864_IIC */ +/* register data */ +#define TW5864_IIC_DATA 0x00ff +#define TW5864_IIC_REG_ADDR_SHIFT 8 +/* register addr */ +#define TW5864_IIC_REG_ADDR (0xff << 8) +/* rd/wr flag rd=1,wr=0 */ +#define TW5864_IIC_RW BIT(16) +#define TW5864_IIC_DEV_ADDR_SHIFT 17 +/* device addr */ +#define TW5864_IIC_DEV_ADDR (0x7f << 17) +/* + * iic done, software kick off one time iic transaction through setting this + * bit to 1. Then poll this bit, value 1 indicate iic transaction have + * completed, if read, valid data have been stored in iic_data + */ +#define TW5864_IIC_DONE BIT(24) + +#define TW5864_RST_AND_IF_INFO 0x18018 +/* Define controls in register TW5864_RST_AND_IF_INFO */ +/* application software soft reset */ +#define TW5864_APP_SOFT_RST BIT(0) +#define TW5864_PCI_INF_VERSION_SHIFT 16 +/* PCI interface version, read only */ +#define TW5864_PCI_INF_VERSION (0xffff << 16) + +/* vlc stream crc value, it is calculated in pci module */ +#define TW5864_VLC_CRC_REG 0x1801c +/* + * vlc max length, it is defined by software based on software assign memory + * space for vlc + */ +#define TW5864_VLC_MAX_LENGTH 0x18020 +/* vlc length of one frame */ +#define TW5864_VLC_LENGTH 0x18024 +/* vlc original crc value */ +#define TW5864_VLC_INTRA_CRC_I_REG 0x18028 +/* vlc original crc value */ +#define TW5864_VLC_INTRA_CRC_O_REG 0x1802c +/* mv stream crc value, it is calculated in pci module */ +#define TW5864_VLC_PAR_CRC_REG 0x18030 +/* mv length */ +#define TW5864_VLC_PAR_LENGTH_REG 0x18034 +/* mv original crc value */ +#define TW5864_VLC_PAR_I_REG 0x18038 +/* mv original crc value */ +#define TW5864_VLC_PAR_O_REG 0x1803c + +/* + * Configuration register for 9[or 10] CIFs or 1D1+15QCIF Preview mode. + * PREV_PCI_ENB_CHN[0] Enable 9th preview channel (9CIF prev) or 1D1 channel in + * (1D1+15QCIF prev) + * PREV_PCI_ENB_CHN[1] Enable 10th preview channel + */ +#define TW5864_PREV_PCI_ENB_CHN 0x18040 +/* Description skipped. */ +#define TW5864_PREV_FRAME_FORMAT_IN 0x18044 +/* IIC enable */ +#define TW5864_IIC_ENB 0x18048 +/* + * Timer interrupt interval + * 0 1ms + * 1 2ms + * 2 4ms + * 3 8ms + */ +#define TW5864_PCI_INTTM_SCALE 0x1804c + +/* + * The above register is pci base address registers. Application software will + * initialize them to tell chip where the corresponding stream will be dumped + * to. Application software will select appropriate base address interval based + * on the stream length. + */ +/* VLC stream base address */ +#define TW5864_VLC_STREAM_BASE_ADDR 0x18080 +/* MV stream base address */ +#define TW5864_MV_STREAM_BASE_ADDR 0x18084 +/* 0x180a0 – 0x180bc: audio burst base address. Skipped. */ +/* 0x180c0 ~ 0x180dc – JPEG Push Mode Buffer Base Address. Skipped. */ +/* 0x18100 – 0x1817c: preview burst base address. Skipped. */ + +/* 0x80000 ~ 0x87fff -- DDR Burst RW Register Map */ +#define TW5864_DDR_CTL 0x80000 +/* Define controls in register TW5864_DDR_CTL */ +#define TW5864_BRST_LENGTH_SHIFT 2 +/* Length of 32-bit data burst */ +#define TW5864_BRST_LENGTH (0x3fff << 2) +/* + * Burst Read/Write + * 0 Read Burst from DDR + * 1 Write Burst to DDR + */ +#define TW5864_BRST_RW BIT(16) +/* Begin a new DDR Burst. This bit is self cleared */ +#define TW5864_NEW_BRST_CMD BIT(17) +/* DDR Burst End Flag */ +#define TW5864_BRST_END BIT(24) +/* Enable Error Interrupt for Single DDR Access */ +#define TW5864_SING_ERR_INTR BIT(25) +/* Enable Error Interrupt for Burst DDR Access */ +#define TW5864_BRST_ERR_INTR BIT(26) +/* Enable Interrupt for End of DDR Burst Access */ +#define TW5864_BRST_END_INTR BIT(27) +/* DDR Single Access Error Flag */ +#define TW5864_SINGLE_ERR BIT(28) +/* DDR Single Access Busy Flag */ +#define TW5864_SINGLE_BUSY BIT(29) +/* DDR Burst Access Error Flag */ +#define TW5864_BRST_ERR BIT(30) +/* DDR Burst Access Busy Flag */ +#define TW5864_BRST_BUSY BIT(31) + +/* [27:0] DDR Access Address. Bit [1:0] has to be 0 */ +#define TW5864_DDR_ADDR 0x80004 +/* DDR Access Internal Buffer Address. Bit [1:0] has to be 0 */ +#define TW5864_DPR_BUF_ADDR 0x80008 +/* SRAM Buffer MPI Access Space. Totally 16 KB */ +#define TW5864_DPR_BUF_START 0x84000 +/* 0x84000 - 0x87ffc */ +#define TW5864_DPR_BUF_SIZE 0x4000 + +/* Indirect Map Space */ +/* + * The indirect space is accessed through 0xb800 ~ 0xb807 registers in direct + * access space + */ +/* Analog Video / Audio Decoder / Encoder */ +/* Allowed channel values: [0; 3] */ +/* Read-only register */ +#define TW5864_INDIR_VIN_0(channel) (0x000 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_0 */ +/* + * 1 Video not present. (sync is not detected in number of consecutive line + * periods specified by MISSCNT register) + * 0 Video detected. + */ +#define TW5864_INDIR_VIN_0_VDLOSS BIT(7) +/* + * 1 Horizontal sync PLL is locked to the incoming video source. + * 0 Horizontal sync PLL is not locked. + */ +#define TW5864_INDIR_VIN_0_HLOCK BIT(6) +/* + * 1 Sub-carrier PLL is locked to the incoming video source. + * 0 Sub-carrier PLL is not locked. + */ +#define TW5864_INDIR_VIN_0_SLOCK BIT(5) +/* + * 1 Even field is being decoded. + * 0 Odd field is being decoded. + */ +#define TW5864_INDIR_VIN_0_FLD BIT(4) +/* + * 1 Vertical logic is locked to the incoming video source. + * 0 Vertical logic is not locked. + */ +#define TW5864_INDIR_VIN_0_VLOCK BIT(3) +/* + * 1 No color burst signal detected. + * 0 Color burst signal detected. + */ +#define TW5864_INDIR_VIN_0_MONO BIT(1) +/* + * 0 60Hz source detected + * 1 50Hz source detected + * The actual vertical scanning frequency depends on the current standard + * invoked. + */ +#define TW5864_INDIR_VIN_0_DET50 BIT(0) + +#define TW5864_INDIR_VIN_1(channel) (0x001 + channel * 0x010) +/* VCR signal indicator. Read-only. */ +#define TW5864_INDIR_VIN_1_VCR BIT(7) +/* Weak signal indicator 2. Read-only. */ +#define TW5864_INDIR_VIN_1_WKAIR BIT(6) +/* Weak signal indicator controlled by WKTH. Read-only. */ +#define TW5864_INDIR_VIN_1_WKAIR1 BIT(5) +/* + * 1 = Standard signal + * 0 = Non-standard signal + * Read-only + */ +#define TW5864_INDIR_VIN_1_VSTD BIT(4) +/* + * 1 = Non-interlaced signal + * 0 = interlaced signal + * Read-only + */ +#define TW5864_INDIR_VIN_1_NINTL BIT(3) +/* + * Vertical Sharpness Control. Writable. + * 0 = None (default) + * 7 = Highest + * **Note: VSHP must be set to ‘0’ if COMB = 0 + */ +#define TW5864_INDIR_VIN_1_VSHP 0x07 + +/* HDELAY_XY[7:0] */ +#define TW5864_INDIR_VIN_2_HDELAY_XY_LO(channel) (0x002 + channel * 0x010) +/* HACTIVE_XY[7:0] */ +#define TW5864_INDIR_VIN_3_HACTIVE_XY_LO(channel) (0x003 + channel * 0x010) +/* VDELAY_XY[7:0] */ +#define TW5864_INDIR_VIN_4_VDELAY_XY_LO(channel) (0x004 + channel * 0x010) +/* VACTIVE_XY[7:0] */ +#define TW5864_INDIR_VIN_5_VACTIVE_XY_LO(channel) (0x005 + channel * 0x010) + +#define TW5864_INDIR_VIN_6(channel) (0x006 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_6 */ +#define TW5864_INDIR_VIN_6_HDELAY_XY_HI 0x03 +#define TW5864_INDIR_VIN_6_HACTIVE_XY_HI_SHIFT 2 +#define TW5864_INDIR_VIN_6_HACTIVE_XY_HI (0x03 << 2) +#define TW5864_INDIR_VIN_6_VDELAY_XY_HI BIT(4) +#define TW5864_INDIR_VIN_6_VACTIVE_XY_HI BIT(5) + +/* + * HDELAY_XY This 10bit register defines the starting location of horizontal + * active pixel for display / record path. A unit is 1 pixel. The default value + * is 0x00f for NTSC and 0x00a for PAL. + * + * HACTIVE_XY This 10bit register defines the number of horizontal active pixel + * for display / record path. A unit is 1 pixel. The default value is decimal + * 720. + * + * VDELAY_XY This 9bit register defines the starting location of vertical + * active for display / record path. A unit is 1 line. The default value is + * decimal 6. + * + * VACTIVE_XY This 9bit register defines the number of vertical active lines + * for display / record path. A unit is 1 line. The default value is decimal + * 240. + */ + +/* HUE These bits control the color hue as 2's complement number. They have + * value from +36o (7Fh) to -36o (80h) with an increment of 2.8o. The 2 LSB has + * no effect. The positive value gives greenish tone and negative value gives + * purplish tone. The default value is 0o (00h). This is effective only on NTSC + * system. The default is 00h. + */ +#define TW5864_INDIR_VIN_7_HUE(channel) (0x007 + channel * 0x010) + +#define TW5864_INDIR_VIN_8(channel) (0x008 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_8 */ +/* + * This bit controls the center frequency of the peaking filter. + * The corresponding gain adjustment is HFLT. + * 0 Low + * 1 center + */ +#define TW5864_INDIR_VIN_8_SCURVE BIT(7) +/* CTI level selection. The default is 1. + * 0 None + * 3 Highest + */ +#define TW5864_INDIR_VIN_8_CTI_SHIFT 4 +#define TW5864_INDIR_VIN_8_CTI (0x03 << 4) + +/* + * These bits control the amount of sharpness enhancement on the luminance + * signals. There are 16 levels of control with "0" having no effect on the + * output image. 1 through 15 provides sharpness enhancement with "F" being the + * strongest. The default is 1. + */ +#define TW5864_INDIR_VIN_8_SHARPNESS 0x0f + +/* + * These bits control the luminance contrast gain. A value of 100 (64h) has a + * gain of 1. The range adjustment is from 0% to 255% at 1% per step. The + * default is 64h. + */ +#define TW5864_INDIR_VIN_9_CNTRST(channel) (0x009 + channel * 0x010) + +/* + * These bits control the brightness. They have value of –128 to 127 in 2's + * complement form. Positive value increases brightness. A value 0 has no + * effect on the data. The default is 00h. + */ +#define TW5864_INDIR_VIN_A_BRIGHT(channel) (0x00a + channel * 0x010) + +/* + * These bits control the digital gain adjustment to the U (or Cb) component of + * the digital video signal. The color saturation can be adjusted by adjusting + * the U and V color gain components by the same amount in the normal + * situation. The U and V can also be adjusted independently to provide greater + * flexibility. The range of adjustment is 0 to 200%. A value of 128 (80h) has + * gain of 100%. The default is 80h. + */ +#define TW5864_INDIR_VIN_B_SAT_U(channel) (0x00b + channel * 0x010) + +/* + * These bits control the digital gain adjustment to the V (or Cr) component of + * the digital video signal. The color saturation can be adjusted by adjusting + * the U and V color gain components by the same amount in the normal + * situation. The U and V can also be adjusted independently to provide greater + * flexibility. The range of adjustment is 0 to 200%. A value of 128 (80h) has + * gain of 100%. The default is 80h. + */ +#define TW5864_INDIR_VIN_C_SAT_V(channel) (0x00c + channel * 0x010) + +/* Read-only */ +#define TW5864_INDIR_VIN_D(channel) (0x00d + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_D */ +/* Macrovision color stripe detection may be un-reliable */ +#define TW5864_INDIR_VIN_D_CSBAD BIT(3) +/* Macrovision AGC pulse detected */ +#define TW5864_INDIR_VIN_D_MCVSN BIT(2) +/* Macrovision color stripe protection burst detected */ +#define TW5864_INDIR_VIN_D_CSTRIPE BIT(1) +/* + * This bit is valid only when color stripe protection is detected, i.e. if + * CSTRIPE=1, + * 1 Type 2 color stripe protection + * 0 Type 3 color stripe protection + */ +#define TW5864_INDIR_VIN_D_CTYPE2 BIT(0) + +/* Read-only */ +#define TW5864_INDIR_VIN_E(channel) (0x00e + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_E */ +/* + * Read-only. + * 0 Idle + * 1 Detection in progress + */ +#define TW5864_INDIR_VIN_E_DETSTUS BIT(7) +/* + * STDNOW Current standard invoked + * 0 NTSC (M) + * 1 PAL (B, D, G, H, I) + * 2 SECAM + * 3 NTSC4.43 + * 4 PAL (M) + * 5 PAL (CN) + * 6 PAL 60 + * 7 Not valid + */ +#define TW5864_INDIR_VIN_E_STDNOW_SHIFT 4 +#define TW5864_INDIR_VIN_E_STDNOW (0x07 << 4) + +/* + * 1 Disable the shadow registers + * 0 Enable VACTIVE and HDELAY shadow registers value depending on STANDARD. + * (Default) + */ +#define TW5864_INDIR_VIN_E_ATREG BIT(3) +/* + * STANDARD Standard selection + * 0 NTSC (M) + * 1 PAL (B, D, G, H, I) + * 2 SECAM + * 3 NTSC4.43 + * 4 PAL (M) + * 5 PAL (CN) + * 6 PAL 60 + * 7 Auto detection (Default) + */ +#define TW5864_INDIR_VIN_E_STANDARD 0x07 + +#define TW5864_INDIR_VIN_F(channel) (0x00f + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_F */ +/* + * 1 Writing 1 to this bit will manually initiate the auto format detection + * process. This bit is a self-clearing bit + * 0 Manual initiation of auto format detection is done. (Default) + */ +#define TW5864_INDIR_VIN_F_ATSTART BIT(7) +/* Enable recognition of PAL60 (Default) */ +#define TW5864_INDIR_VIN_F_PAL60EN BIT(6) +/* Enable recognition of PAL (CN). (Default) */ +#define TW5864_INDIR_VIN_F_PALCNEN BIT(5) +/* Enable recognition of PAL (M). (Default) */ +#define TW5864_INDIR_VIN_F_PALMEN BIT(4) +/* Enable recognition of NTSC 4.43. (Default) */ +#define TW5864_INDIR_VIN_F_NTSC44EN BIT(3) +/* Enable recognition of SECAM. (Default) */ +#define TW5864_INDIR_VIN_F_SECAMEN BIT(2) +/* Enable recognition of PAL (B, D, G, H, I). (Default) */ +#define TW5864_INDIR_VIN_F_PALBEN BIT(1) +/* Enable recognition of NTSC (M). (Default) */ +#define TW5864_INDIR_VIN_F_NTSCEN BIT(0) + +/* Some registers skipped. */ + +/* Use falling edge to sample VD1-VD4 from 54 MHz to 108 MHz */ +#define TW5864_INDIR_VD_108_POL 0x041 +#define TW5864_INDIR_VD_108_POL_VD12 BIT(0) +#define TW5864_INDIR_VD_108_POL_VD34 BIT(1) +#define TW5864_INDIR_VD_108_POL_BOTH \ + (TW5864_INDIR_VD_108_POL_VD12 | TW5864_INDIR_VD_108_POL_VD34) + +/* Some registers skipped. */ + +/* + * Audio Input ADC gain control + * 0 0.25 + * 1 0.31 + * 2 0.38 + * 3 0.44 + * 4 0.50 + * 5 0.63 + * 6 0.75 + * 7 0.88 + * 8 1.00 (default) + * 9 1.25 + * 10 1.50 + * 11 1.75 + * 12 2.00 + * 13 2.25 + * 14 2.50 + * 15 2.75 + */ +/* [3:0] channel 0, [7:4] channel 1 */ +#define TW5864_INDIR_AIGAIN1 0x060 +/* [3:0] channel 2, [7:4] channel 3 */ +#define TW5864_INDIR_AIGAIN2 0x061 + +/* Some registers skipped */ + +#define TW5864_INDIR_AIN_0x06D 0x06d +/* Define controls in register TW5864_INDIR_AIN_0x06D */ +/* + * LAWMD Select u-Law/A-Law/PCM/SB data output format on ADATR and ADATM pin. + * 0 PCM output (default) + * 1 SB (Signed MSB bit in PCM data is inverted) output + * 2 u-Law output + * 3 A-Law output + */ +#define TW5864_INDIR_AIN_LAWMD_SHIFT 6 +#define TW5864_INDIR_AIN_LAWMD (0x03 << 6) +/* + * Disable the mixing ratio value for all audio. + * 0 Apply individual mixing ratio value for each audio (default) + * 1 Apply nominal value for all audio commonly + */ +#define TW5864_INDIR_AIN_MIX_DERATIO BIT(5) +/* + * Enable the mute function for audio channel AINn when n is 0 to 3. It effects + * only for mixing. When n = 4, it enable the mute function of the playback + * audio input. It effects only for single chip or the last stage chip + * 0 Normal + * 1 Muted (default) + */ +#define TW5864_INDIR_AIN_MIX_MUTE 0x1f + +/* Some registers skipped */ + +#define TW5864_INDIR_AIN_0x0E3 0x0e3 +/* Define controls in register TW5864_INDIR_AIN_0x0E3 */ +/* + * ADATP signal is coming from external ADPCM decoder, instead of on-chip ADPCM + * decoder + */ +#define TW5864_INDIR_AIN_0x0E3_EXT_ADATP BIT(7) +/* ACLKP output signal polarity inverse */ +#define TW5864_INDIR_AIN_0x0E3_ACLKPPOLO BIT(6) +/* + * ACLKR input signal polarity inverse. + * 0 Not inversed (Default) + * 1 Inversed + */ +#define TW5864_INDIR_AIN_0x0E3_ACLKRPOL BIT(5) +/* + * ACLKP input signal polarity inverse. + * 0 Not inversed (Default) + * 1 Inversed + */ +#define TW5864_INDIR_AIN_0x0E3_ACLKPPOLI BIT(4) +/* + * ACKI [21:0] control automatic set up with AFMD registers + * This mode is only effective when ACLKRMASTER=1 + * 0 ACKI [21:0] registers set up ACKI control + * 1 ACKI control is automatically set up by AFMD register values + */ +#define TW5864_INDIR_AIN_0x0E3_AFAUTO BIT(3) +/* + * AFAUTO control mode + * 0 8kHz setting (Default) + * 1 16kHz setting + * 2 32kHz setting + * 3 44.1kHz setting + * 4 48kHz setting + */ +#define TW5864_INDIR_AIN_0x0E3_AFMD 0x07 + +#define TW5864_INDIR_AIN_0x0E4 0x0e4 +/* Define controls in register TW5864_INDIR_AIN_0x0ED */ +/* + * 8bit I2S Record output mode. + * 0 L/R half length separated output (Default). + * 1 One continuous packed output equal to DSP output format. + */ +#define TW5864_INDIR_AIN_0x0E4_I2S8MODE BIT(7) +/* + * Audio Clock Master ACLKR output wave format. + * 0 High periods is one 27MHz clock period (default). + * 1 Almost duty 50-50% clock output on ACLKR pin. If this mode is selected, two + * times bigger number value need to be set up on the ACKI register. If + * AFAUTO=1, ACKI control is automatically set up even if MASCKMD=1. + */ +#define TW5864_INDIR_AIN_0x0E4_MASCKMD BIT(6) +/* Playback ACLKP/ASYNP/ADATP input data MSB-LSB swapping */ +#define TW5864_INDIR_AIN_0x0E4_PBINSWAP BIT(5) +/* + * ASYNR input signal delay. + * 0 No delay + * 1 Add one 27MHz period delay in ASYNR signal input + */ +#define TW5864_INDIR_AIN_0x0E4_ASYNRDLY BIT(4) +/* + * ASYNP input signal delay. + * 0 no delay + * 1 add one 27MHz period delay in ASYNP signal input + */ +#define TW5864_INDIR_AIN_0x0E4_ASYNPDLY BIT(3) +/* + * ADATP input data delay by one ACLKP clock. + * 0 No delay (Default). This is for I2S type 1T delay input interface. + * 1 Add 1 ACLKP clock delay in ADATP input data. This is for left-justified + * type 0T delay input interface. + */ +#define TW5864_INDIR_AIN_0x0E4_ADATPDLY BIT(2) +/* + * Select u-Law/A-Law/PCM/SB data input format on ADATP pin. + * 0 PCM input (Default) + * 1 SB (Signed MSB bit in PCM data is inverted) input + * 2 u-Law input + * 3 A-Law input + */ +#define TW5864_INDIR_AIN_0x0E4_INLAWMD 0x03 + +/* + * Enable state register updating and interrupt request of audio AIN5 detection + * for each input + */ +#define TW5864_INDIR_AIN_A5DETENA 0x0e5 + +/* Some registers skipped */ + +/* + * [7:3]: DEV_ID The TW5864 product ID code is 01000 + * [2:0]: REV_ID The revision number is 0h + */ +#define TW5864_INDIR_ID 0x0fe + +#define TW5864_INDIR_IN_PIC_WIDTH(channel) (0x200 + 4 * channel) +#define TW5864_INDIR_IN_PIC_HEIGHT(channel) (0x201 + 4 * channel) +#define TW5864_INDIR_OUT_PIC_WIDTH(channel) (0x202 + 4 * channel) +#define TW5864_INDIR_OUT_PIC_HEIGHT(channel) (0x203 + 4 * channel) +/* + * Interrupt status register from the front-end. Write "1" to each bit to clear + * the interrupt + * 15:0 Motion detection interrupt for channel 0 ~ 15 + * 31:16 Night detection interrupt for channel 0 ~ 15 + * 47:32 Blind detection interrupt for channel 0 ~ 15 + * 63:48 No video interrupt for channel 0 ~ 15 + * 79:64 Line mode underflow interrupt for channel 0 ~ 15 + * 95:80 Line mode overflow interrupt for channel 0 ~ 15 + */ +/* 0x2d0~0x2d7: [63:0] bits */ +#define TW5864_INDIR_INTERRUPT1 0x2d0 +/* 0x2e0~0x2e3: [95:64] bits */ +#define TW5864_INDIR_INTERRUPT2 0x2e0 + +/* + * Interrupt mask register for interrupts in 0x2d0 ~ 0x2d7 + * 15:0 Motion detection interrupt for channel 0 ~ 15 + * 31:16 Night detection interrupt for channel 0 ~ 15 + * 47:32 Blind detection interrupt for channel 0 ~ 15 + * 63:48 No video interrupt for channel 0 ~ 15 + * 79:64 Line mode underflow interrupt for channel 0 ~ 15 + * 95:80 Line mode overflow interrupt for channel 0 ~ 15 + */ +/* 0x2d8~0x2df: [63:0] bits */ +#define TW5864_INDIR_INTERRUPT_MASK1 0x2d8 +/* 0x2e8~0x2eb: [95:64] bits */ +#define TW5864_INDIR_INTERRUPT_MASK2 0x2e8 + +/* [11:0]: Interrupt summary register for interrupts & interrupt mask from in + * 0x2d0 ~ 0x2d7 and 0x2d8 ~ 0x2df + * bit 0: interrupt occurs in 0x2d0 & 0x2d8 + * bit 1: interrupt occurs in 0x2d1 & 0x2d9 + * bit 2: interrupt occurs in 0x2d2 & 0x2da + * bit 3: interrupt occurs in 0x2d3 & 0x2db + * bit 4: interrupt occurs in 0x2d4 & 0x2dc + * bit 5: interrupt occurs in 0x2d5 & 0x2dd + * bit 6: interrupt occurs in 0x2d6 & 0x2de + * bit 7: interrupt occurs in 0x2d7 & 0x2df + * bit 8: interrupt occurs in 0x2e0 & 0x2e8 + * bit 9: interrupt occurs in 0x2e1 & 0x2e9 + * bit 10: interrupt occurs in 0x2e2 & 0x2ea + * bit 11: interrupt occurs in 0x2e3 & 0x2eb + */ +#define TW5864_INDIR_INTERRUPT_SUMMARY 0x2f0 + +/* Motion / Blind / Night Detection */ +/* valid value for channel is [0:15] */ +#define TW5864_INDIR_DETECTION_CTL0(channel) (0x300 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL0 */ +/* + * Disable the motion and blind detection. + * 0 Enable motion and blind detection (default) + * 1 Disable motion and blind detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_DIS BIT(5) +/* + * Request to start motion detection on manual trigger mode + * 0 None Operation (default) + * 1 Request to start motion detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_STRB BIT(3) +/* + * Select the trigger mode of motion detection + * 0 Automatic trigger mode of motion detection (default) + * 1 Manual trigger mode for motion detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_STRB_EN BIT(2) +/* + * Define the threshold of cell for blind detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 3 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL0_BD_CELSENS 0x03 + +#define TW5864_INDIR_DETECTION_CTL1(channel) (0x301 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL1 */ +/* + * Control the temporal sensitivity of motion detector. + * 0 More Sensitive (default) + * : : + * 15 Less Sensitive + */ +#define TW5864_INDIR_DETECTION_CTL1_MD_TMPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL1_MD_TMPSENS (0x0f << 4) +/* + * Adjust the horizontal starting position for motion detection + * 0 0 pixel (default) + * : : + * 15 15 pixels + */ +#define TW5864_INDIR_DETECTION_CTL1_MD_PIXEL_OS 0x0f + +#define TW5864_INDIR_DETECTION_CTL2(channel) (0x302 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL2 */ +/* + * Control the updating time of reference field for motion detection. + * 0 Update reference field every field (default) + * 1 Update reference field according to MD_SPEED + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_REFFLD BIT(7) +/* + * Select the field for motion detection. + * 0 Detecting motion for only odd field (default) + * 1 Detecting motion for only even field + * 2 Detecting motion for any field + * 3 Detecting motion for both odd and even field + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_FIELD_SHIFT 5 +#define TW5864_INDIR_DETECTION_CTL2_MD_FIELD (0x03 << 5) +/* + * Control the level sensitivity of motion detector. + * 0 More sensitive (default) + * : : + * 15 Less sensitive + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_LVSENS 0x1f + +#define TW5864_INDIR_DETECTION_CTL3(channel) (0x303 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL3 */ +/* + * Define the threshold of sub-cell number for motion detection. + * 0 Motion is detected if 1 sub-cell has motion (More sensitive) (default) + * 1 Motion is detected if 2 sub-cells have motion + * 2 Motion is detected if 3 sub-cells have motion + * 3 Motion is detected if 4 sub-cells have motion (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL3_MD_CELSENS_SHIFT 6 +#define TW5864_INDIR_DETECTION_CTL3_MD_CELSENS (0x03 << 6) +/* + * Control the velocity of motion detector. + * Large value is suitable for slow motion detection. + * In MD_DUAL_EN = 1, MD_SPEED should be limited to 0 ~ 31. + * 0 1 field intervals (default) + * 1 2 field intervals + * : : + * 61 62 field intervals + * 62 63 field intervals + * 63 Not supported + */ +#define TW5864_INDIR_DETECTION_CTL3_MD_SPEED 0x3f + +#define TW5864_INDIR_DETECTION_CTL4(channel) (0x304 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL4 */ +/* + * Control the spatial sensitivity of motion detector. + * 0 More Sensitive (default) + * : : + * 15 Less Sensitive + */ +#define TW5864_INDIR_DETECTION_CTL4_MD_SPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL4_MD_SPSENS (0x0f << 4) +/* + * Define the threshold of level for blind detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 15 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL4_BD_LVSENS 0x0f + +#define TW5864_INDIR_DETECTION_CTL5(channel) (0x305 + channel * 0x08) +/* + * Define the threshold of temporal sensitivity for night detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 15 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL5_ND_TMPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL5_ND_TMPSENS (0x0f << 4) +/* + * Define the threshold of level for night detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 3 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL5_ND_LVSENS 0x0f + +/* + * [11:0] The base address of the motion detection buffer. This address is in + * unit of 64K bytes. The generated DDR address will be {MD_BASE_ADDR, + * 16"h0000}. The default value should be 12"h000 + */ +#define TW5864_INDIR_MD_BASE_ADDR 0x380 + +/* + * This controls the channel of the motion detection result shown in register + * 0x3a0 ~ 0x3b7. Before reading back motion result, always set this first. + */ +#define TW5864_INDIR_RGR_MOTION_SEL 0x382 + +/* [15:0] MD strobe has been performed at channel n (read only) */ +#define TW5864_INDIR_MD_STRB 0x386 +/* NO_VIDEO Detected from channel n (read only) */ +#define TW5864_INDIR_NOVID_DET 0x388 +/* Motion Detected from channel n (read only) */ +#define TW5864_INDIR_MD_DET 0x38a +/* Blind Detected from channel n (read only) */ +#define TW5864_INDIR_BD_DET 0x38c +/* Night Detected from channel n (read only) */ +#define TW5864_INDIR_ND_DET 0x38e + +/* 192 bit motion flag of the channel specified by RGR_MOTION_SEL in 0x382 */ +#define TW5864_INDIR_MOTION_FLAG 0x3a0 +#define TW5864_INDIR_MOTION_FLAG_BYTE_COUNT 24 + +/* + * [9:0] The motion cell count of a specific channel selected by 0x382. This is + * for DI purpose + */ +#define TW5864_INDIR_MD_DI_CNT 0x3b8 +/* The motion detection cell sensitivity for DI purpose */ +#define TW5864_INDIR_MD_DI_CELLSENS 0x3ba +/* The motion detection threshold level for DI purpose */ +#define TW5864_INDIR_MD_DI_LVSENS 0x3bb + +/* 192 bit motion mask of the channel specified by MASK_CH_SEL in 0x3fe */ +#define TW5864_INDIR_MOTION_MASK 0x3e0 +#define TW5864_INDIR_MOTION_MASK_BYTE_COUNT 24 + +/* [4:0] The channel selection to access masks in 0x3e0 ~ 0x3f7 */ +#define TW5864_INDIR_MASK_CH_SEL 0x3fe + +/* Clock PLL / Analog IP Control */ +/* Some registers skipped */ + +#define TW5864_INDIR_DDRA_DLL_DQS_SEL0 0xee6 +#define TW5864_INDIR_DDRA_DLL_DQS_SEL1 0xee7 +#define TW5864_INDIR_DDRA_DLL_CLK90_SEL 0xee8 +#define TW5864_INDIR_DDRA_DLL_TEST_SEL_AND_TAP_S 0xee9 + +#define TW5864_INDIR_DDRB_DLL_DQS_SEL0 0xeeb +#define TW5864_INDIR_DDRB_DLL_DQS_SEL1 0xeec +#define TW5864_INDIR_DDRB_DLL_CLK90_SEL 0xeed +#define TW5864_INDIR_DDRB_DLL_TEST_SEL_AND_TAP_S 0xeee + +#define TW5864_INDIR_RESET 0xef0 +#define TW5864_INDIR_RESET_VD BIT(7) +#define TW5864_INDIR_RESET_DLL BIT(6) +#define TW5864_INDIR_RESET_MUX_CORE BIT(5) + +#define TW5864_INDIR_PV_VD_CK_POL 0xefd +#define TW5864_INDIR_PV_VD_CK_POL_PV(channel) BIT(channel) +#define TW5864_INDIR_PV_VD_CK_POL_VD(channel) BIT(channel + 4) + +#define TW5864_INDIR_CLK0_SEL 0xefe +#define TW5864_INDIR_CLK0_SEL_VD_SHIFT 0 +#define TW5864_INDIR_CLK0_SEL_VD_MASK 0x3 +#define TW5864_INDIR_CLK0_SEL_PV_SHIFT 2 +#define TW5864_INDIR_CLK0_SEL_PV_MASK (0x3 << 2) +#define TW5864_INDIR_CLK0_SEL_PV2_SHIFT 4 +#define TW5864_INDIR_CLK0_SEL_PV2_MASK (0x3 << 4) diff --git a/drivers/media/pci/tw5864/tw5864-util.c b/drivers/media/pci/tw5864/tw5864-util.c new file mode 100644 index 000000000000..771eef235755 --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-util.c @@ -0,0 +1,37 @@ +#include "tw5864.h" + +void tw5864_indir_writeb(struct tw5864_dev *dev, u16 addr, u8 data) +{ + int retries = 30000; + + while (tw_readl(TW5864_IND_CTL) & BIT(31) && --retries) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_writel() retries exhausted before writing\n"); + + tw_writel(TW5864_IND_DATA, data); + tw_writel(TW5864_IND_CTL, addr << 2 | TW5864_RW | TW5864_ENABLE); +} + +u8 tw5864_indir_readb(struct tw5864_dev *dev, u16 addr) +{ + int retries = 30000; + + while (tw_readl(TW5864_IND_CTL) & BIT(31) && --retries) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_readl() retries exhausted before reading\n"); + + tw_writel(TW5864_IND_CTL, addr << 2 | TW5864_ENABLE); + + retries = 30000; + while (tw_readl(TW5864_IND_CTL) & BIT(31) && --retries) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_readl() retries exhausted at reading\n"); + + return tw_readl(TW5864_IND_DATA); +} diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c new file mode 100644 index 000000000000..652a059b2e0a --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -0,0 +1,1510 @@ +/* + * TW5864 driver - video encoding functions + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.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. + */ + +#include <linux/module.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-contig.h> + +#include "tw5864.h" +#include "tw5864-reg.h" + +#define QUANTIZATION_TABLE_LEN 96 +#define VLC_LOOKUP_TABLE_LEN 1024 + +static const u16 forward_quantization_table[QUANTIZATION_TABLE_LEN] = { + 0x3333, 0x1f82, 0x3333, 0x1f82, 0x1f82, 0x147b, 0x1f82, 0x147b, + 0x3333, 0x1f82, 0x3333, 0x1f82, 0x1f82, 0x147b, 0x1f82, 0x147b, + 0x2e8c, 0x1d42, 0x2e8c, 0x1d42, 0x1d42, 0x1234, 0x1d42, 0x1234, + 0x2e8c, 0x1d42, 0x2e8c, 0x1d42, 0x1d42, 0x1234, 0x1d42, 0x1234, + 0x2762, 0x199a, 0x2762, 0x199a, 0x199a, 0x1062, 0x199a, 0x1062, + 0x2762, 0x199a, 0x2762, 0x199a, 0x199a, 0x1062, 0x199a, 0x1062, + 0x2492, 0x16c1, 0x2492, 0x16c1, 0x16c1, 0x0e3f, 0x16c1, 0x0e3f, + 0x2492, 0x16c1, 0x2492, 0x16c1, 0x16c1, 0x0e3f, 0x16c1, 0x0e3f, + 0x2000, 0x147b, 0x2000, 0x147b, 0x147b, 0x0d1b, 0x147b, 0x0d1b, + 0x2000, 0x147b, 0x2000, 0x147b, 0x147b, 0x0d1b, 0x147b, 0x0d1b, + 0x1c72, 0x11cf, 0x1c72, 0x11cf, 0x11cf, 0x0b4d, 0x11cf, 0x0b4d, + 0x1c72, 0x11cf, 0x1c72, 0x11cf, 0x11cf, 0x0b4d, 0x11cf, 0x0b4d +}; + +static const u16 inverse_quantization_table[QUANTIZATION_TABLE_LEN] = { + 0x800a, 0x800d, 0x800a, 0x800d, 0x800d, 0x8010, 0x800d, 0x8010, + 0x800a, 0x800d, 0x800a, 0x800d, 0x800d, 0x8010, 0x800d, 0x8010, + 0x800b, 0x800e, 0x800b, 0x800e, 0x800e, 0x8012, 0x800e, 0x8012, + 0x800b, 0x800e, 0x800b, 0x800e, 0x800e, 0x8012, 0x800e, 0x8012, + 0x800d, 0x8010, 0x800d, 0x8010, 0x8010, 0x8014, 0x8010, 0x8014, + 0x800d, 0x8010, 0x800d, 0x8010, 0x8010, 0x8014, 0x8010, 0x8014, + 0x800e, 0x8012, 0x800e, 0x8012, 0x8012, 0x8017, 0x8012, 0x8017, + 0x800e, 0x8012, 0x800e, 0x8012, 0x8012, 0x8017, 0x8012, 0x8017, + 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, 0x8019, 0x8014, 0x8019, + 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, 0x8019, 0x8014, 0x8019, + 0x8012, 0x8017, 0x8012, 0x8017, 0x8017, 0x801d, 0x8017, 0x801d, + 0x8012, 0x8017, 0x8012, 0x8017, 0x8017, 0x801d, 0x8017, 0x801d +}; + +static const u16 encoder_vlc_lookup_table[VLC_LOOKUP_TABLE_LEN] = { + 0x011, 0x000, 0x000, 0x000, 0x065, 0x021, 0x000, 0x000, 0x087, 0x064, + 0x031, 0x000, 0x097, 0x086, 0x075, 0x053, 0x0a7, 0x096, 0x085, 0x063, + 0x0b7, 0x0a6, 0x095, 0x074, 0x0df, 0x0b6, 0x0a5, 0x084, 0x0db, 0x0de, + 0x0b5, 0x094, 0x0d8, 0x0da, 0x0dd, 0x0a4, 0x0ef, 0x0ee, 0x0d9, 0x0b4, + 0x0eb, 0x0ea, 0x0ed, 0x0dc, 0x0ff, 0x0fe, 0x0e9, 0x0ec, 0x0fb, 0x0fa, + 0x0fd, 0x0e8, 0x10f, 0x0f1, 0x0f9, 0x0fc, 0x10b, 0x10e, 0x10d, 0x0f8, + 0x107, 0x10a, 0x109, 0x10c, 0x104, 0x106, 0x105, 0x108, 0x023, 0x000, + 0x000, 0x000, 0x06b, 0x022, 0x000, 0x000, 0x067, 0x057, 0x033, 0x000, + 0x077, 0x06a, 0x069, 0x045, 0x087, 0x066, 0x065, 0x044, 0x084, 0x076, + 0x075, 0x056, 0x097, 0x086, 0x085, 0x068, 0x0bf, 0x096, 0x095, 0x064, + 0x0bb, 0x0be, 0x0bd, 0x074, 0x0cf, 0x0ba, 0x0b9, 0x094, 0x0cb, 0x0ce, + 0x0cd, 0x0bc, 0x0c8, 0x0ca, 0x0c9, 0x0b8, 0x0df, 0x0de, 0x0dd, 0x0cc, + 0x0db, 0x0da, 0x0d9, 0x0dc, 0x0d7, 0x0eb, 0x0d6, 0x0d8, 0x0e9, 0x0e8, + 0x0ea, 0x0d1, 0x0e7, 0x0e6, 0x0e5, 0x0e4, 0x04f, 0x000, 0x000, 0x000, + 0x06f, 0x04e, 0x000, 0x000, 0x06b, 0x05f, 0x04d, 0x000, 0x068, 0x05c, + 0x05e, 0x04c, 0x07f, 0x05a, 0x05b, 0x04b, 0x07b, 0x058, 0x059, 0x04a, + 0x079, 0x06e, 0x06d, 0x049, 0x078, 0x06a, 0x069, 0x048, 0x08f, 0x07e, + 0x07d, 0x05d, 0x08b, 0x08e, 0x07a, 0x06c, 0x09f, 0x08a, 0x08d, 0x07c, + 0x09b, 0x09e, 0x089, 0x08c, 0x098, 0x09a, 0x09d, 0x088, 0x0ad, 0x097, + 0x099, 0x09c, 0x0a9, 0x0ac, 0x0ab, 0x0aa, 0x0a5, 0x0a8, 0x0a7, 0x0a6, + 0x0a1, 0x0a4, 0x0a3, 0x0a2, 0x021, 0x000, 0x000, 0x000, 0x067, 0x011, + 0x000, 0x000, 0x064, 0x066, 0x031, 0x000, 0x063, 0x073, 0x072, 0x065, + 0x062, 0x083, 0x082, 0x070, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x011, 0x010, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x011, 0x021, 0x020, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x023, 0x022, 0x021, 0x020, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x022, 0x021, 0x031, + 0x030, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x023, 0x022, 0x033, 0x032, 0x031, 0x030, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x030, + 0x031, 0x033, 0x032, 0x035, 0x034, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x037, 0x036, 0x035, 0x034, 0x033, 0x032, + 0x031, 0x041, 0x051, 0x061, 0x071, 0x081, 0x091, 0x0a1, 0x0b1, 0x000, + 0x002, 0x000, 0x0e4, 0x011, 0x0f4, 0x002, 0x024, 0x003, 0x005, 0x012, + 0x034, 0x013, 0x065, 0x024, 0x013, 0x063, 0x015, 0x022, 0x075, 0x034, + 0x044, 0x023, 0x023, 0x073, 0x054, 0x033, 0x033, 0x004, 0x043, 0x014, + 0x011, 0x043, 0x014, 0x001, 0x025, 0x015, 0x035, 0x025, 0x064, 0x055, + 0x045, 0x035, 0x074, 0x065, 0x085, 0x0d5, 0x012, 0x095, 0x055, 0x045, + 0x095, 0x0e5, 0x084, 0x075, 0x022, 0x0a5, 0x094, 0x085, 0x032, 0x0b5, + 0x003, 0x0c5, 0x001, 0x044, 0x0a5, 0x032, 0x0b5, 0x094, 0x0c5, 0x0a4, + 0x0a4, 0x054, 0x0d5, 0x0b4, 0x0b4, 0x064, 0x0f5, 0x0f5, 0x053, 0x0d4, + 0x0e5, 0x0c4, 0x105, 0x105, 0x0c4, 0x074, 0x063, 0x0e4, 0x0d4, 0x084, + 0x073, 0x0f4, 0x004, 0x005, 0x000, 0x053, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x011, 0x021, 0x031, 0x030, 0x011, 0x021, 0x020, 0x000, + 0x011, 0x010, 0x000, 0x000, 0x011, 0x033, 0x032, 0x043, 0x042, 0x053, + 0x052, 0x063, 0x062, 0x073, 0x072, 0x083, 0x082, 0x093, 0x092, 0x091, + 0x037, 0x036, 0x035, 0x034, 0x033, 0x045, 0x044, 0x043, 0x042, 0x053, + 0x052, 0x063, 0x062, 0x061, 0x060, 0x000, 0x045, 0x037, 0x036, 0x035, + 0x044, 0x043, 0x034, 0x033, 0x042, 0x053, 0x052, 0x061, 0x051, 0x060, + 0x000, 0x000, 0x053, 0x037, 0x045, 0x044, 0x036, 0x035, 0x034, 0x043, + 0x033, 0x042, 0x052, 0x051, 0x050, 0x000, 0x000, 0x000, 0x045, 0x044, + 0x043, 0x037, 0x036, 0x035, 0x034, 0x033, 0x042, 0x051, 0x041, 0x050, + 0x000, 0x000, 0x000, 0x000, 0x061, 0x051, 0x037, 0x036, 0x035, 0x034, + 0x033, 0x032, 0x041, 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x061, 0x051, 0x035, 0x034, 0x033, 0x023, 0x032, 0x041, 0x031, 0x060, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x061, 0x041, 0x051, 0x033, + 0x023, 0x022, 0x032, 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x061, 0x060, 0x041, 0x023, 0x022, 0x031, 0x021, 0x051, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x051, 0x050, + 0x031, 0x023, 0x022, 0x021, 0x041, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x040, 0x041, 0x031, 0x032, 0x011, 0x033, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x040, 0x041, 0x021, 0x011, 0x031, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x030, 0x031, 0x011, 0x021, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x020, 0x021, 0x011, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x010, 0x011, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000 +}; + +static const unsigned int lambda_lookup_table[] = { + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0040, 0x0040, 0x0040, 0x0040, 0x0060, 0x0060, 0x0060, 0x0080, + 0x0080, 0x0080, 0x00a0, 0x00c0, 0x00c0, 0x00e0, 0x0100, 0x0120, + 0x0140, 0x0160, 0x01a0, 0x01c0, 0x0200, 0x0240, 0x0280, 0x02e0, + 0x0320, 0x03a0, 0x0400, 0x0480, 0x0500, 0x05a0, 0x0660, 0x0720, + 0x0800, 0x0900, 0x0a20, 0x0b60 +}; + +static const unsigned int intra4x4_lambda3[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 4, + 4, 4, 5, 6, 6, 7, 8, 9, + 10, 11, 13, 14, 16, 18, 20, 23, + 25, 29, 32, 36, 40, 45, 51, 57, + 64, 72, 81, 91 +}; + +static v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); +static enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); + +static void tw5864_handle_frame_task(unsigned long data); +static void tw5864_handle_frame(struct tw5864_h264_frame *frame); +static void tw5864_frame_interval_set(struct tw5864_input *input); + +static int tw5864_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + if (*num_planes) + return sizes[0] < H264_VLC_BUF_SIZE ? -EINVAL : 0; + + sizes[0] = H264_VLC_BUF_SIZE; + *num_planes = 1; + + return 0; +} + +static void tw5864_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct tw5864_input *dev = vb2_get_drv_priv(vq); + struct tw5864_buf *buf = container_of(vbuf, struct tw5864_buf, vb); + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &dev->active); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static int tw5864_input_std_get(struct tw5864_input *input, + enum tw5864_vid_std *std) +{ + struct tw5864_dev *dev = input->root; + u8 std_reg = tw_indir_readb(TW5864_INDIR_VIN_E(input->nr)); + + *std = (std_reg & 0x70) >> 4; + + if (std_reg & 0x80) { + dev_dbg(&dev->pci->dev, + "Video format detection is in progress, please wait\n"); + return -EAGAIN; + } + + return 0; +} + +static int tw5864_enable_input(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + int nr = input->nr; + unsigned long flags; + int d1_width = 720; + int d1_height; + int frame_width_bus_value = 0; + int frame_height_bus_value = 0; + int reg_frame_bus = 0x1c; + int fmt_reg_value = 0; + int downscale_enabled = 0; + + dev_dbg(&dev->pci->dev, "Enabling channel %d\n", nr); + + input->frame_seqno = 0; + input->frame_gop_seqno = 0; + input->h264_idr_pic_id = 0; + + input->reg_dsp_qp = input->qp; + input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; + input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; + input->reg_emu = TW5864_EMU_EN_LPF | TW5864_EMU_EN_BHOST + | TW5864_EMU_EN_SEN | TW5864_EMU_EN_ME | TW5864_EMU_EN_DDR; + input->reg_dsp = nr /* channel id */ + | TW5864_DSP_CHROM_SW + | ((0xa << 8) & TW5864_DSP_MB_DELAY) + ; + + input->resolution = D1; + + d1_height = (input->std == STD_NTSC) ? 480 : 576; + + input->width = d1_width; + input->height = d1_height; + + input->reg_interlacing = 0x4; + + switch (input->resolution) { + case D1: + frame_width_bus_value = 0x2cf; + frame_height_bus_value = input->height - 1; + reg_frame_bus = 0x1c; + fmt_reg_value = 0; + downscale_enabled = 0; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD; + input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; + input->reg_interlacing = TW5864_DI_EN | TW5864_DSP_INTER_ST; + + tw_setl(TW5864_FULL_HALF_FLAG, 1 << nr); + break; + case HD1: + input->height /= 2; + input->width /= 2; + frame_width_bus_value = 0x2cf; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x1c; + fmt_reg_value = 0; + downscale_enabled = 0; + input->reg_dsp_codec |= TW5864_HD1_MAP_MD; + input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); + + break; + case CIF: + input->height /= 4; + input->width /= 2; + frame_width_bus_value = 0x15f; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x07; + fmt_reg_value = 1; + downscale_enabled = 1; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); + break; + case QCIF: + input->height /= 4; + input->width /= 4; + frame_width_bus_value = 0x15f; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x07; + fmt_reg_value = 1; + downscale_enabled = 1; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); + break; + } + + /* analog input width / 4 */ + tw_indir_writeb(TW5864_INDIR_IN_PIC_WIDTH(nr), d1_width / 4); + tw_indir_writeb(TW5864_INDIR_IN_PIC_HEIGHT(nr), d1_height / 4); + + /* output width / 4 */ + tw_indir_writeb(TW5864_INDIR_OUT_PIC_WIDTH(nr), input->width / 4); + tw_indir_writeb(TW5864_INDIR_OUT_PIC_HEIGHT(nr), input->height / 4); + + tw_writel(TW5864_DSP_PIC_MAX_MB, + ((input->width / 16) << 8) | (input->height / 16)); + + tw_writel(TW5864_FRAME_WIDTH_BUS_A(nr), + frame_width_bus_value); + tw_writel(TW5864_FRAME_WIDTH_BUS_B(nr), + frame_width_bus_value); + tw_writel(TW5864_FRAME_HEIGHT_BUS_A(nr), + frame_height_bus_value); + tw_writel(TW5864_FRAME_HEIGHT_BUS_B(nr), + (frame_height_bus_value + 1) / 2 - 1); + + tw5864_frame_interval_set(input); + + if (downscale_enabled) + tw_setl(TW5864_H264EN_CH_DNS, 1 << nr); + + tw_mask_shift_writel(TW5864_H264EN_CH_FMT_REG1, 0x3, 2 * nr, + fmt_reg_value); + + tw_mask_shift_writel((nr < 2 + ? TW5864_H264EN_RATE_MAX_LINE_REG1 + : TW5864_H264EN_RATE_MAX_LINE_REG2), + 0x1f, 5 * (nr % 2), + input->std == STD_NTSC ? 29 : 24); + + tw_mask_shift_writel((nr < 2) ? TW5864_FRAME_BUS1 : + TW5864_FRAME_BUS2, 0xff, (nr % 2) * 8, + reg_frame_bus); + + spin_lock_irqsave(&dev->slock, flags); + input->enabled = 1; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} + +void tw5864_request_encoded_frame(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + u32 enc_buf_id_new; + + tw_setl(TW5864_DSP_CODEC, TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD); + tw_writel(TW5864_EMU, input->reg_emu); + tw_writel(TW5864_INTERLACING, input->reg_interlacing); + tw_writel(TW5864_DSP, input->reg_dsp); + + tw_writel(TW5864_DSP_QP, input->reg_dsp_qp); + tw_writel(TW5864_DSP_REF_MVP_LAMBDA, input->reg_dsp_ref_mvp_lambda); + tw_writel(TW5864_DSP_I4x4_WEIGHT, input->reg_dsp_i4x4_weight); + tw_mask_shift_writel(TW5864_DSP_INTRA_MODE, TW5864_DSP_INTRA_MODE_MASK, + TW5864_DSP_INTRA_MODE_SHIFT, + TW5864_DSP_INTRA_MODE_16x16); + + if (input->frame_gop_seqno == 0) { + /* Produce I-frame */ + tw_writel(TW5864_MOTION_SEARCH_ETC, TW5864_INTRA_EN); + input->h264_idr_pic_id++; + input->h264_idr_pic_id &= TW5864_DSP_REF_FRM; + } else { + /* Produce P-frame */ + tw_writel(TW5864_MOTION_SEARCH_ETC, TW5864_INTRA_EN | + TW5864_ME_EN | BIT(5) /* SRCH_OPT default */); + } + tw5864_prepare_frame_headers(input); + tw_writel(TW5864_VLC, + TW5864_VLC_PCI_SEL | + ((input->tail_nb_bits + 24) << TW5864_VLC_BIT_ALIGN_SHIFT) | + input->reg_dsp_qp); + + enc_buf_id_new = tw_mask_shift_readl(TW5864_ENC_BUF_PTR_REC1, 0x3, + 2 * input->nr); + tw_writel(TW5864_DSP_ENC_ORG_PTR_REG, + enc_buf_id_new << TW5864_DSP_ENC_ORG_PTR_SHIFT); + tw_writel(TW5864_DSP_ENC_REC, + enc_buf_id_new << 12 | ((enc_buf_id_new + 3) & 3)); + + tw_writel(TW5864_SLICE, TW5864_START_NSLICE); + tw_writel(TW5864_SLICE, 0); +} + +static int tw5864_disable_input(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + unsigned long flags; + + dev_dbg(&dev->pci->dev, "Disabling channel %d\n", input->nr); + + spin_lock_irqsave(&dev->slock, flags); + input->enabled = 0; + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} + +static int tw5864_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct tw5864_input *input = vb2_get_drv_priv(q); + int ret; + + ret = tw5864_enable_input(input); + if (!ret) + return 0; + + while (!list_empty(&input->active)) { + struct tw5864_buf *buf = list_entry(input->active.next, + struct tw5864_buf, list); + + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + return ret; +} + +static void tw5864_stop_streaming(struct vb2_queue *q) +{ + unsigned long flags; + struct tw5864_input *input = vb2_get_drv_priv(q); + + tw5864_disable_input(input); + + spin_lock_irqsave(&input->slock, flags); + if (input->vb) { + vb2_buffer_done(&input->vb->vb.vb2_buf, VB2_BUF_STATE_ERROR); + input->vb = NULL; + } + while (!list_empty(&input->active)) { + struct tw5864_buf *buf = list_entry(input->active.next, + struct tw5864_buf, list); + + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&input->slock, flags); +} + +static const struct vb2_ops tw5864_video_qops = { + .queue_setup = tw5864_queue_setup, + .buf_queue = tw5864_buf_queue, + .start_streaming = tw5864_start_streaming, + .stop_streaming = tw5864_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int tw5864_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw5864_input *input = + container_of(ctrl->handler, struct tw5864_input, hdl); + struct tw5864_dev *dev = input->root; + unsigned long flags; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tw_indir_writeb(TW5864_INDIR_VIN_A_BRIGHT(input->nr), + (u8)ctrl->val); + break; + case V4L2_CID_HUE: + tw_indir_writeb(TW5864_INDIR_VIN_7_HUE(input->nr), + (u8)ctrl->val); + break; + case V4L2_CID_CONTRAST: + tw_indir_writeb(TW5864_INDIR_VIN_9_CNTRST(input->nr), + (u8)ctrl->val); + break; + case V4L2_CID_SATURATION: + tw_indir_writeb(TW5864_INDIR_VIN_B_SAT_U(input->nr), + (u8)ctrl->val); + tw_indir_writeb(TW5864_INDIR_VIN_C_SAT_V(input->nr), + (u8)ctrl->val); + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + input->gop = ctrl->val; + return 0; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + spin_lock_irqsave(&input->slock, flags); + input->qp = ctrl->val; + input->reg_dsp_qp = input->qp; + input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; + input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; + spin_unlock_irqrestore(&input->slock, flags); + return 0; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: + memset(input->md_threshold_grid_values, ctrl->val, + sizeof(input->md_threshold_grid_values)); + return 0; + case V4L2_CID_DETECT_MD_MODE: + return 0; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + /* input->md_threshold_grid_ctrl->p_new.p_u16 contains data */ + memcpy(input->md_threshold_grid_values, + input->md_threshold_grid_ctrl->p_new.p_u16, + sizeof(input->md_threshold_grid_values)); + return 0; + } + return 0; +} + +static int tw5864_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw5864_input *input = video_drvdata(file); + + f->fmt.pix.width = 720; + switch (input->std) { + default: + WARN_ON_ONCE(1); + case STD_NTSC: + f->fmt.pix.height = 480; + break; + case STD_PAL: + case STD_SECAM: + f->fmt.pix.height = 576; + break; + } + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix.sizeimage = H264_VLC_BUF_SIZE; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int tw5864_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tw5864_input *input = video_drvdata(file); + struct tw5864_dev *dev = input->root; + + u8 indir_0x000 = tw_indir_readb(TW5864_INDIR_VIN_0(input->nr)); + u8 indir_0x00d = tw_indir_readb(TW5864_INDIR_VIN_D(input->nr)); + u8 v1 = indir_0x000; + u8 v2 = indir_0x00d; + + if (i->index) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + snprintf(i->name, sizeof(i->name), "Encoder %d", input->nr); + i->std = TW5864_NORMS; + if (v1 & (1 << 7)) + i->status |= V4L2_IN_ST_NO_SYNC; + if (!(v1 & (1 << 6))) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (v1 & (1 << 2)) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (v1 & (1 << 1)) + i->status |= V4L2_IN_ST_NO_COLOR; + if (v2 & (1 << 2)) + i->status |= V4L2_IN_ST_MACROVISION; + + return 0; +} + +static int tw5864_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int tw5864_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i) + return -EINVAL; + return 0; +} + +static int tw5864_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw5864_input *input = video_drvdata(file); + + strcpy(cap->driver, "tw5864"); + snprintf(cap->card, sizeof(cap->card), "TW5864 Encoder %d", + input->nr); + sprintf(cap->bus_info, "PCI:%s", pci_name(input->root->pci)); + return 0; +} + +static int tw5864_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct tw5864_input *input = video_drvdata(file); + enum tw5864_vid_std tw_std; + int ret; + + ret = tw5864_input_std_get(input, &tw_std); + if (ret) + return ret; + *std = tw5864_get_v4l2_std(tw_std); + + return 0; +} + +static int tw5864_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct tw5864_input *input = video_drvdata(file); + + *std = input->v4l2_std; + return 0; +} + +static int tw5864_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct tw5864_input *input = video_drvdata(file); + struct tw5864_dev *dev = input->root; + + input->v4l2_std = std; + input->std = tw5864_from_v4l2_std(std); + tw_indir_writeb(TW5864_INDIR_VIN_E(input->nr), input->std); + return 0; +} + +static int tw5864_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_H264; + + return 0; +} + +static int tw5864_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* + * Allow for up to 30 events (1 second for NTSC) to be stored. + */ + return v4l2_event_subscribe(fh, sub, 30, NULL); + } + return -EINVAL; +} + +static void tw5864_frame_interval_set(struct tw5864_input *input) +{ + /* + * This register value seems to follow such approach: In each second + * interval, when processing Nth frame, it checks Nth bit of register + * value and, if the bit is 1, it processes the frame, otherwise the + * frame is discarded. + * So unary representation would work, but more or less equal gaps + * between the frames should be preserved. + * + * For 1 FPS - 0x00000001 + * 00000000 00000000 00000000 00000001 + * + * For max FPS - set all 25/30 lower bits: + * 00111111 11111111 11111111 11111111 (NTSC) + * 00000001 11111111 11111111 11111111 (PAL) + * + * For half of max FPS - use such pattern: + * 00010101 01010101 01010101 01010101 (NTSC) + * 00000001 01010101 01010101 01010101 (PAL) + * + * Et cetera. + * + * The value supplied to hardware is capped by mask of 25/30 lower bits. + */ + struct tw5864_dev *dev = input->root; + u32 unary_framerate = 0; + int shift = 0; + int std_max_fps = input->std == STD_NTSC ? 30 : 25; + + for (shift = 0; shift < std_max_fps; shift += input->frame_interval) + unary_framerate |= 0x00000001 << shift; + + tw_writel(TW5864_H264EN_RATE_CNTL_LO_WORD(input->nr, 0), + unary_framerate >> 16); + tw_writel(TW5864_H264EN_RATE_CNTL_HI_WORD(input->nr, 0), + unary_framerate & 0xffff); +} + +static int tw5864_frameinterval_get(struct tw5864_input *input, + struct v4l2_fract *frameinterval) +{ + switch (input->std) { + case STD_NTSC: + frameinterval->numerator = 1001; + frameinterval->denominator = 30000; + break; + case STD_PAL: + case STD_SECAM: + frameinterval->numerator = 1; + frameinterval->denominator = 25; + break; + default: + WARN(1, "tw5864_frameinterval_get requested for unknown std %d\n", + input->std); + return -EINVAL; + } + + return 0; +} + +static int tw5864_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct tw5864_input *input = video_drvdata(file); + + if (fsize->index > 0) + return -EINVAL; + if (fsize->pixel_format != V4L2_PIX_FMT_H264) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = 720; + fsize->discrete.height = input->std == STD_NTSC ? 480 : 576; + + return 0; +} + +static int tw5864_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct tw5864_input *input = video_drvdata(file); + struct v4l2_fract frameinterval; + int std_max_fps = input->std == STD_NTSC ? 30 : 25; + struct v4l2_frmsizeenum fsize = { .index = fintv->index, + .pixel_format = fintv->pixel_format }; + int ret; + + ret = tw5864_enum_framesizes(file, priv, &fsize); + if (ret) + return ret; + + if (fintv->width != fsize.discrete.width || + fintv->height != fsize.discrete.height) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + ret = tw5864_frameinterval_get(input, &frameinterval); + fintv->stepwise.step = frameinterval; + fintv->stepwise.min = frameinterval; + fintv->stepwise.max = frameinterval; + fintv->stepwise.max.numerator *= std_max_fps; + + return ret; +} + +static int tw5864_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw5864_input *input = video_drvdata(file); + struct v4l2_captureparm *cp = &sp->parm.capture; + int ret; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + + ret = tw5864_frameinterval_get(input, &cp->timeperframe); + cp->timeperframe.numerator *= input->frame_interval; + cp->capturemode = 0; + cp->readbuffers = 2; + + return ret; +} + +static int tw5864_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw5864_input *input = video_drvdata(file); + struct v4l2_fract *t = &sp->parm.capture.timeperframe; + struct v4l2_fract time_base; + int ret; + + ret = tw5864_frameinterval_get(input, &time_base); + if (ret) + return ret; + + if (!t->numerator || !t->denominator) { + t->numerator = time_base.numerator * input->frame_interval; + t->denominator = time_base.denominator; + } else if (t->denominator != time_base.denominator) { + t->numerator = t->numerator * time_base.denominator / + t->denominator; + t->denominator = time_base.denominator; + } + + input->frame_interval = t->numerator / time_base.numerator; + if (input->frame_interval < 1) + input->frame_interval = 1; + tw5864_frame_interval_set(input); + return tw5864_g_parm(file, priv, sp); +} + +static const struct v4l2_ctrl_ops tw5864_ctrl_ops = { + .s_ctrl = tw5864_s_ctrl, +}; + +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + +#define INDIR_SPACE_MAP_SHIFT 0x100000 + +static int tw5864_g_reg(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct tw5864_input *input = video_drvdata(file); + struct tw5864_dev *dev = input->root; + + if (reg->reg < INDIR_SPACE_MAP_SHIFT) { + if (reg->reg > 0x87fff) + return -EINVAL; + reg->size = 4; + reg->val = tw_readl(reg->reg); + } else { + __u64 indir_addr = reg->reg - INDIR_SPACE_MAP_SHIFT; + + if (indir_addr > 0xefe) + return -EINVAL; + reg->size = 1; + reg->val = tw_indir_readb(reg->reg); + } + return 0; +} + +static int tw5864_s_reg(struct file *file, void *fh, + const struct v4l2_dbg_register *reg) +{ + struct tw5864_input *input = video_drvdata(file); + struct tw5864_dev *dev = input->root; + + if (reg->reg < INDIR_SPACE_MAP_SHIFT) { + if (reg->reg > 0x87fff) + return -EINVAL; + tw_writel(reg->reg, reg->val); + } else { + __u64 indir_addr = reg->reg - INDIR_SPACE_MAP_SHIFT; + + if (indir_addr > 0xefe) + return -EINVAL; + tw_indir_writeb(reg->reg, reg->val); + } + return 0; +} +#endif + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = tw5864_querycap, + .vidioc_enum_fmt_vid_cap = tw5864_enum_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_querystd = tw5864_querystd, + .vidioc_s_std = tw5864_s_std, + .vidioc_g_std = tw5864_g_std, + .vidioc_enum_input = tw5864_enum_input, + .vidioc_g_input = tw5864_g_input, + .vidioc_s_input = tw5864_s_input, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_try_fmt_vid_cap = tw5864_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw5864_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = tw5864_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = tw5864_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_enum_framesizes = tw5864_enum_framesizes, + .vidioc_enum_frameintervals = tw5864_enum_frameintervals, + .vidioc_s_parm = tw5864_s_parm, + .vidioc_g_parm = tw5864_g_parm, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = tw5864_g_reg, + .vidioc_s_register = tw5864_s_reg, +#endif +}; + +static const struct video_device tw5864_video_template = { + .name = "tw5864_video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TW5864_NORMS, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, +}; + +/* Motion Detection Threshold matrix */ +static const struct v4l2_ctrl_config tw5864_md_thresholds = { + .ops = &tw5864_ctrl_ops, + .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, + .dims = {MD_CELLS_HOR, MD_CELLS_VERT}, + .def = 14, + /* See tw5864_md_metric_from_mvd() */ + .max = 2 * 0x0f, + .step = 1, +}; + +static int tw5864_video_input_init(struct tw5864_input *dev, int video_nr); +static void tw5864_video_input_fini(struct tw5864_input *dev); +static void tw5864_encoder_tables_upload(struct tw5864_dev *dev); + +int tw5864_video_init(struct tw5864_dev *dev, int *video_nr) +{ + int i; + int ret; + unsigned long flags; + int last_dma_allocated = -1; + int last_input_nr_registered = -1; + + for (i = 0; i < H264_BUF_CNT; i++) { + struct tw5864_h264_frame *frame = &dev->h264_buf[i]; + + frame->vlc.addr = dma_alloc_coherent(&dev->pci->dev, + H264_VLC_BUF_SIZE, + &frame->vlc.dma_addr, + GFP_KERNEL | GFP_DMA32); + if (!frame->vlc.addr) { + dev_err(&dev->pci->dev, "dma alloc fail\n"); + ret = -ENOMEM; + goto free_dma; + } + frame->mv.addr = dma_alloc_coherent(&dev->pci->dev, + H264_MV_BUF_SIZE, + &frame->mv.dma_addr, + GFP_KERNEL | GFP_DMA32); + if (!frame->mv.addr) { + dev_err(&dev->pci->dev, "dma alloc fail\n"); + ret = -ENOMEM; + dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + frame->vlc.addr, frame->vlc.dma_addr); + goto free_dma; + } + last_dma_allocated = i; + } + + tw5864_encoder_tables_upload(dev); + + /* Picture is distorted without this block */ + /* use falling edge to sample 54M to 108M */ + tw_indir_writeb(TW5864_INDIR_VD_108_POL, TW5864_INDIR_VD_108_POL_BOTH); + tw_indir_writeb(TW5864_INDIR_CLK0_SEL, 0x00); + + tw_indir_writeb(TW5864_INDIR_DDRA_DLL_DQS_SEL0, 0x02); + tw_indir_writeb(TW5864_INDIR_DDRA_DLL_DQS_SEL1, 0x02); + tw_indir_writeb(TW5864_INDIR_DDRA_DLL_CLK90_SEL, 0x02); + tw_indir_writeb(TW5864_INDIR_DDRB_DLL_DQS_SEL0, 0x02); + tw_indir_writeb(TW5864_INDIR_DDRB_DLL_DQS_SEL1, 0x02); + tw_indir_writeb(TW5864_INDIR_DDRB_DLL_CLK90_SEL, 0x02); + + /* video input reset */ + tw_indir_writeb(TW5864_INDIR_RESET, 0); + tw_indir_writeb(TW5864_INDIR_RESET, TW5864_INDIR_RESET_VD | + TW5864_INDIR_RESET_DLL | TW5864_INDIR_RESET_MUX_CORE); + msleep(20); + + /* + * Select Part A mode for all channels. + * tw_setl instead of tw_clearl for Part B mode. + * + * I guess "Part B" is primarily for downscaled version of same channel + * which goes in Part A of same bus + */ + tw_writel(TW5864_FULL_HALF_MODE_SEL, 0); + + tw_indir_writeb(TW5864_INDIR_PV_VD_CK_POL, + TW5864_INDIR_PV_VD_CK_POL_VD(0) | + TW5864_INDIR_PV_VD_CK_POL_VD(1) | + TW5864_INDIR_PV_VD_CK_POL_VD(2) | + TW5864_INDIR_PV_VD_CK_POL_VD(3)); + + spin_lock_irqsave(&dev->slock, flags); + dev->encoder_busy = 0; + dev->h264_buf_r_index = 0; + dev->h264_buf_w_index = 0; + tw_writel(TW5864_VLC_STREAM_BASE_ADDR, + dev->h264_buf[dev->h264_buf_w_index].vlc.dma_addr); + tw_writel(TW5864_MV_STREAM_BASE_ADDR, + dev->h264_buf[dev->h264_buf_w_index].mv.dma_addr); + spin_unlock_irqrestore(&dev->slock, flags); + + tw_writel(TW5864_SEN_EN_CH, 0x000f); + tw_writel(TW5864_H264EN_CH_EN, 0x000f); + + tw_writel(TW5864_H264EN_BUS0_MAP, 0x00000000); + tw_writel(TW5864_H264EN_BUS1_MAP, 0x00001111); + tw_writel(TW5864_H264EN_BUS2_MAP, 0x00002222); + tw_writel(TW5864_H264EN_BUS3_MAP, 0x00003333); + + /* + * Quote from Intersil (manufacturer): + * 0x0038 is managed by HW, and by default it won't pass the pointer set + * at 0x0010. So if you don't do encoding, 0x0038 should stay at '3' + * (with 4 frames in buffer). If you encode one frame and then move + * 0x0010 to '1' for example, HW will take one more frame and set it to + * buffer #0, and then you should see 0x0038 is set to '0'. There is + * only one HW encoder engine, so 4 channels cannot get encoded + * simultaneously. But each channel does have its own buffer (for + * original frames and reconstructed frames). So there is no problem to + * manage encoding for 4 channels at same time and no need to force + * I-frames in switching channels. + * End of quote. + * + * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0 (for any channel), we + * have no "rolling" (until we change this value). + * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0x3, it starts to roll + * continuously together with 0x0038. + */ + tw_writel(TW5864_ENC_BUF_PTR_REC1, 0x00ff); + tw_writel(TW5864_PCI_INTTM_SCALE, 0); + + tw_writel(TW5864_INTERLACING, TW5864_DI_EN); + tw_writel(TW5864_MASTER_ENB_REG, TW5864_PCI_VLC_INTR_ENB); + tw_writel(TW5864_PCI_INTR_CTL, + TW5864_TIMER_INTR_ENB | TW5864_PCI_MAST_ENB | + TW5864_MVD_VLC_MAST_ENB); + + dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; + tw5864_irqmask_apply(dev); + + tasklet_init(&dev->tasklet, tw5864_handle_frame_task, + (unsigned long)dev); + + for (i = 0; i < TW5864_INPUTS; i++) { + dev->inputs[i].root = dev; + dev->inputs[i].nr = i; + ret = tw5864_video_input_init(&dev->inputs[i], video_nr[i]); + if (ret) + goto fini_video_inputs; + last_input_nr_registered = i; + } + + return 0; + +fini_video_inputs: + for (i = last_input_nr_registered; i >= 0; i--) + tw5864_video_input_fini(&dev->inputs[i]); + + tasklet_kill(&dev->tasklet); + +free_dma: + for (i = last_dma_allocated; i >= 0; i--) { + dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + dev->h264_buf[i].vlc.addr, + dev->h264_buf[i].vlc.dma_addr); + dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, + dev->h264_buf[i].mv.addr, + dev->h264_buf[i].mv.dma_addr); + } + + return ret; +} + +static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) +{ + struct tw5864_dev *dev = input->root; + int ret; + struct v4l2_ctrl_handler *hdl = &input->hdl; + + mutex_init(&input->lock); + spin_lock_init(&input->slock); + + /* setup video buffers queue */ + INIT_LIST_HEAD(&input->active); + input->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + input->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + input->vidq.io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + input->vidq.ops = &tw5864_video_qops; + input->vidq.mem_ops = &vb2_dma_contig_memops; + input->vidq.drv_priv = input; + input->vidq.gfp_flags = 0; + input->vidq.buf_struct_size = sizeof(struct tw5864_buf); + input->vidq.lock = &input->lock; + input->vidq.min_buffers_needed = 2; + input->vidq.dev = &input->root->pci->dev; + ret = vb2_queue_init(&input->vidq); + if (ret) + goto free_mutex; + + input->vdev = tw5864_video_template; + input->vdev.v4l2_dev = &input->root->v4l2_dev; + input->vdev.lock = &input->lock; + input->vdev.queue = &input->vidq; + video_set_drvdata(&input->vdev, input); + + /* Initialize the device control structures */ + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 100); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, MAX_GOP_SIZE, 1, GOP_SIZE); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 28, 51, 1, QP_VALUE); + v4l2_ctrl_new_std_menu(hdl, &tw5864_ctrl_ops, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, + V4L2_DETECT_MD_MODE_DISABLED); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, + tw5864_md_thresholds.min, tw5864_md_thresholds.max, + tw5864_md_thresholds.step, tw5864_md_thresholds.def); + input->md_threshold_grid_ctrl = + v4l2_ctrl_new_custom(hdl, &tw5864_md_thresholds, NULL); + if (hdl->error) { + ret = hdl->error; + goto free_v4l2_hdl; + } + input->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + input->qp = QP_VALUE; + input->gop = GOP_SIZE; + input->frame_interval = 1; + + ret = video_register_device(&input->vdev, VFL_TYPE_GRABBER, video_nr); + if (ret) + goto free_v4l2_hdl; + + dev_info(&input->root->pci->dev, "Registered video device %s\n", + video_device_node_name(&input->vdev)); + + /* + * Set default video standard. Doesn't matter which, the detected value + * will be found out by VIDIOC_QUERYSTD handler. + */ + input->v4l2_std = V4L2_STD_NTSC_M; + input->std = STD_NTSC; + + tw_indir_writeb(TW5864_INDIR_VIN_E(video_nr), 0x07); + /* to initiate auto format recognition */ + tw_indir_writeb(TW5864_INDIR_VIN_F(video_nr), 0xff); + + return 0; + +free_v4l2_hdl: + v4l2_ctrl_handler_free(hdl); + vb2_queue_release(&input->vidq); +free_mutex: + mutex_destroy(&input->lock); + + return ret; +} + +static void tw5864_video_input_fini(struct tw5864_input *dev) +{ + video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + vb2_queue_release(&dev->vidq); +} + +void tw5864_video_fini(struct tw5864_dev *dev) +{ + int i; + + tasklet_kill(&dev->tasklet); + + for (i = 0; i < TW5864_INPUTS; i++) + tw5864_video_input_fini(&dev->inputs[i]); + + for (i = 0; i < H264_BUF_CNT; i++) { + dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + dev->h264_buf[i].vlc.addr, + dev->h264_buf[i].vlc.dma_addr); + dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, + dev->h264_buf[i].mv.addr, + dev->h264_buf[i].mv.dma_addr); + } +} + +void tw5864_prepare_frame_headers(struct tw5864_input *input) +{ + struct tw5864_buf *vb = input->vb; + u8 *dst; + size_t dst_space; + unsigned long flags; + + if (!vb) { + spin_lock_irqsave(&input->slock, flags); + if (list_empty(&input->active)) { + spin_unlock_irqrestore(&input->slock, flags); + input->vb = NULL; + return; + } + vb = list_first_entry(&input->active, struct tw5864_buf, list); + list_del(&vb->list); + spin_unlock_irqrestore(&input->slock, flags); + } + + dst = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); + dst_space = vb2_plane_size(&vb->vb.vb2_buf, 0); + + /* + * Low-level bitstream writing functions don't have a fine way to say + * correctly that supplied buffer is too small. So we just check there + * and warn, and don't care at lower level. + * Currently all headers take below 32 bytes. + * The buffer is supposed to have plenty of free space at this point, + * anyway. + */ + if (WARN_ON_ONCE(dst_space < 128)) + return; + + /* + * Generate H264 headers: + * If this is first frame, put SPS and PPS + */ + if (input->frame_gop_seqno == 0) + tw5864_h264_put_stream_header(&dst, &dst_space, input->qp, + input->width, input->height); + + /* Put slice header */ + tw5864_h264_put_slice_header(&dst, &dst_space, input->h264_idr_pic_id, + input->frame_gop_seqno, + &input->tail_nb_bits, &input->tail); + input->vb = vb; + input->buf_cur_ptr = dst; + input->buf_cur_space_left = dst_space; +} + +/* + * Returns heuristic motion detection metric value from known components of + * hardware-provided Motion Vector Data. + */ +static unsigned int tw5864_md_metric_from_mvd(u32 mvd) +{ + /* + * Format of motion vector data exposed by tw5864, according to + * manufacturer: + * mv_x 10 bits + * mv_y 10 bits + * non_zero_members 8 bits + * mb_type 3 bits + * reserved 1 bit + * + * non_zero_members: number of non-zero residuals in each macro block + * after quantization + * + * unsigned int reserved = mvd >> 31; + * unsigned int mb_type = (mvd >> 28) & 0x7; + * unsigned int non_zero_members = (mvd >> 20) & 0xff; + */ + unsigned int mv_y = (mvd >> 10) & 0x3ff; + unsigned int mv_x = mvd & 0x3ff; + + /* heuristic: */ + mv_x &= 0x0f; + mv_y &= 0x0f; + + return mv_y + mv_x; +} + +static int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) +{ + struct tw5864_input *input = frame->input; + u32 *mv = (u32 *)frame->mv.addr; + int i; + int detected = 0; + + for (i = 0; i < MD_CELLS; i++) { + const u16 thresh = input->md_threshold_grid_values[i]; + const unsigned int metric = tw5864_md_metric_from_mvd(mv[i]); + + if (metric > thresh) + detected = 1; + + if (detected) + break; + } + return detected; +} + +static void tw5864_handle_frame_task(unsigned long data) +{ + struct tw5864_dev *dev = (struct tw5864_dev *)data; + unsigned long flags; + int batch_size = H264_BUF_CNT; + + spin_lock_irqsave(&dev->slock, flags); + while (dev->h264_buf_r_index != dev->h264_buf_w_index && batch_size--) { + struct tw5864_h264_frame *frame = + &dev->h264_buf[dev->h264_buf_r_index]; + + spin_unlock_irqrestore(&dev->slock, flags); + dma_sync_single_for_cpu(&dev->pci->dev, frame->vlc.dma_addr, + H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(&dev->pci->dev, frame->mv.dma_addr, + H264_MV_BUF_SIZE, DMA_FROM_DEVICE); + tw5864_handle_frame(frame); + dma_sync_single_for_device(&dev->pci->dev, frame->vlc.dma_addr, + H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_device(&dev->pci->dev, frame->mv.dma_addr, + H264_MV_BUF_SIZE, DMA_FROM_DEVICE); + spin_lock_irqsave(&dev->slock, flags); + + dev->h264_buf_r_index++; + dev->h264_buf_r_index %= H264_BUF_CNT; + } + spin_unlock_irqrestore(&dev->slock, flags); +} + +#ifdef DEBUG +static u32 tw5864_vlc_checksum(u32 *data, int len) +{ + u32 val, count_len = len; + + val = *data++; + while (((count_len >> 2) - 1) > 0) { + val ^= *data++; + count_len -= 4; + } + val ^= htonl((len >> 2)); + return val; +} +#endif + +static void tw5864_handle_frame(struct tw5864_h264_frame *frame) +{ +#define SKIP_VLCBUF_BYTES 3 + struct tw5864_input *input = frame->input; + struct tw5864_dev *dev = input->root; + struct tw5864_buf *vb; + struct vb2_v4l2_buffer *v4l2_buf; + int frame_len = frame->vlc_len - SKIP_VLCBUF_BYTES; + u8 *dst = input->buf_cur_ptr; + u8 tail_mask, vlc_mask = 0; + int i; + u8 vlc_first_byte = ((u8 *)(frame->vlc.addr + SKIP_VLCBUF_BYTES))[0]; + unsigned long flags; + int zero_run; + u8 *src; + u8 *src_end; + +#ifdef DEBUG + if (frame->checksum != + tw5864_vlc_checksum((u32 *)frame->vlc.addr, frame_len)) + dev_err(&dev->pci->dev, + "Checksum of encoded frame doesn't match!\n"); +#endif + + spin_lock_irqsave(&input->slock, flags); + vb = input->vb; + input->vb = NULL; + spin_unlock_irqrestore(&input->slock, flags); + + v4l2_buf = to_vb2_v4l2_buffer(&vb->vb.vb2_buf); + + if (!vb) { /* Gone because of disabling */ + dev_dbg(&dev->pci->dev, "vb is empty, dropping frame\n"); + return; + } + + /* + * Check for space. + * Mind the overhead of startcode emulation prevention. + */ + if (input->buf_cur_space_left < frame_len * 5 / 4) { + dev_err_once(&dev->pci->dev, + "Left space in vb2 buffer, %d bytes, is less than considered safely enough to put frame of length %d. Dropping this frame.\n", + input->buf_cur_space_left, frame_len); + return; + } + + for (i = 0; i < 8 - input->tail_nb_bits; i++) + vlc_mask |= 1 << i; + tail_mask = (~vlc_mask) & 0xff; + + dst[0] = (input->tail & tail_mask) | (vlc_first_byte & vlc_mask); + frame_len--; + dst++; + + /* H.264 startcode emulation prevention */ + src = frame->vlc.addr + SKIP_VLCBUF_BYTES + 1; + src_end = src + frame_len; + zero_run = 0; + for (; src < src_end; src++) { + if (zero_run < 2) { + if (*src == 0) + ++zero_run; + else + zero_run = 0; + } else { + if ((*src & ~0x03) == 0) + *dst++ = 0x03; + zero_run = *src == 0; + } + *dst++ = *src; + } + + vb2_set_plane_payload(&vb->vb.vb2_buf, 0, + dst - (u8 *)vb2_plane_vaddr(&vb->vb.vb2_buf, 0)); + + vb->vb.vb2_buf.timestamp = frame->timestamp; + v4l2_buf->field = V4L2_FIELD_INTERLACED; + v4l2_buf->sequence = frame->seqno; + + /* Check for motion flags */ + if (frame->gop_seqno /* P-frame */ && + tw5864_is_motion_triggered(frame)) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = v4l2_buf->sequence, + }, + }; + + v4l2_event_queue(&input->vdev, &ev); + } + + vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std) +{ + switch (std) { + case STD_NTSC: return V4L2_STD_NTSC_M; + case STD_PAL: return V4L2_STD_PAL_B; + case STD_SECAM: return V4L2_STD_SECAM_B; + case STD_NTSC443: return V4L2_STD_NTSC_443; + case STD_PAL_M: return V4L2_STD_PAL_M; + case STD_PAL_CN: return V4L2_STD_PAL_Nc; + case STD_PAL_60: return V4L2_STD_PAL_60; + case STD_INVALID: return V4L2_STD_UNKNOWN; + } + return 0; +} + +static enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std) +{ + if (v4l2_std & V4L2_STD_NTSC_M) + return STD_NTSC; + if (v4l2_std & V4L2_STD_PAL_B) + return STD_PAL; + if (v4l2_std & V4L2_STD_SECAM_B) + return STD_SECAM; + if (v4l2_std & V4L2_STD_NTSC_443) + return STD_NTSC443; + if (v4l2_std & V4L2_STD_PAL_M) + return STD_PAL_M; + if (v4l2_std & V4L2_STD_PAL_Nc) + return STD_PAL_CN; + if (v4l2_std & V4L2_STD_PAL_60) + return STD_PAL_60; + + return STD_INVALID; +} + +static void tw5864_encoder_tables_upload(struct tw5864_dev *dev) +{ + int i; + + tw_writel(TW5864_VLC_RD, 0x1); + for (i = 0; i < VLC_LOOKUP_TABLE_LEN; i++) { + tw_writel((TW5864_VLC_STREAM_MEM_START + i * 4), + encoder_vlc_lookup_table[i]); + } + tw_writel(TW5864_VLC_RD, 0x0); + + for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { + tw_writel((TW5864_QUAN_TAB + i * 4), + forward_quantization_table[i]); + } + + for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { + tw_writel((TW5864_QUAN_TAB + i * 4), + inverse_quantization_table[i]); + } +} diff --git a/drivers/media/pci/tw5864/tw5864.h b/drivers/media/pci/tw5864/tw5864.h new file mode 100644 index 000000000000..f5de9f6ef119 --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864.h @@ -0,0 +1,205 @@ +/* + * TW5864 driver - common header file + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.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. + */ + +#include <linux/pci.h> +#include <linux/videodev2.h> +#include <linux/notifier.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/interrupt.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-sg.h> + +#include "tw5864-reg.h" + +#define PCI_DEVICE_ID_TECHWELL_5864 0x5864 + +#define TW5864_NORMS V4L2_STD_ALL + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define TW5864_INPUTS 4 + +/* The TW5864 uses 192 (16x12) detection cells in full screen for motion + * detection. Each detection cell is composed of 44 pixels and 20 lines for + * NTSC and 24 lines for PAL. + */ +#define MD_CELLS_HOR 16 +#define MD_CELLS_VERT 12 +#define MD_CELLS (MD_CELLS_HOR * MD_CELLS_VERT) + +#define H264_VLC_BUF_SIZE 0x80000 +#define H264_MV_BUF_SIZE 0x2000 /* device writes 5396 bytes */ +#define QP_VALUE 28 +#define MAX_GOP_SIZE 255 +#define GOP_SIZE MAX_GOP_SIZE + +enum resolution { + D1 = 1, + HD1 = 2, /* half d1 - 360x(240|288) */ + CIF = 3, + QCIF = 4, +}; + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +struct tw5864_dev; /* forward delclaration */ + +/* buffer for one video/vbi/ts frame */ +struct tw5864_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + + unsigned int size; +}; + +struct tw5864_dma_buf { + void *addr; + dma_addr_t dma_addr; +}; + +enum tw5864_vid_std { + STD_NTSC = 0, /* NTSC (M) */ + STD_PAL = 1, /* PAL (B, D, G, H, I) */ + STD_SECAM = 2, /* SECAM */ + STD_NTSC443 = 3, /* NTSC4.43 */ + STD_PAL_M = 4, /* PAL (M) */ + STD_PAL_CN = 5, /* PAL (CN) */ + STD_PAL_60 = 6, /* PAL 60 */ + STD_INVALID = 7, + STD_AUTO = 7, +}; + +struct tw5864_input { + int nr; /* input number */ + struct tw5864_dev *root; + struct mutex lock; /* used for vidq and vdev */ + spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + struct video_device vdev; + struct v4l2_ctrl_handler hdl; + struct vb2_queue vidq; + struct list_head active; + enum resolution resolution; + unsigned int width, height; + unsigned int frame_seqno; + unsigned int frame_gop_seqno; + unsigned int h264_idr_pic_id; + int enabled; + enum tw5864_vid_std std; + v4l2_std_id v4l2_std; + int tail_nb_bits; + u8 tail; + u8 *buf_cur_ptr; + int buf_cur_space_left; + + u32 reg_interlacing; + u32 reg_vlc; + u32 reg_dsp_codec; + u32 reg_dsp; + u32 reg_emu; + u32 reg_dsp_qp; + u32 reg_dsp_ref_mvp_lambda; + u32 reg_dsp_i4x4_weight; + u32 buf_id; + + struct tw5864_buf *vb; + + struct v4l2_ctrl *md_threshold_grid_ctrl; + u16 md_threshold_grid_values[12 * 16]; + int qp; + int gop; + + /* + * In (1/MAX_FPS) units. + * For max FPS (default), set to 1. + * For 1 FPS, set to e.g. 32. + */ + int frame_interval; + unsigned long new_frame_deadline; +}; + +struct tw5864_h264_frame { + struct tw5864_dma_buf vlc; + struct tw5864_dma_buf mv; + int vlc_len; + u32 checksum; + struct tw5864_input *input; + u64 timestamp; + unsigned int seqno; + unsigned int gop_seqno; +}; + +/* global device status */ +struct tw5864_dev { + spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + struct v4l2_device v4l2_dev; + struct tw5864_input inputs[TW5864_INPUTS]; +#define H264_BUF_CNT 4 + struct tw5864_h264_frame h264_buf[H264_BUF_CNT]; + int h264_buf_r_index; + int h264_buf_w_index; + + struct tasklet_struct tasklet; + + int encoder_busy; + /* Input number to check next for ready raw picture (in RR fashion) */ + int next_input; + + /* pci i/o */ + char name[64]; + struct pci_dev *pci; + void __iomem *mmio; + u32 irqmask; +}; + +#define tw_readl(reg) readl(dev->mmio + reg) +#define tw_mask_readl(reg, mask) \ + (tw_readl(reg) & (mask)) +#define tw_mask_shift_readl(reg, mask, shift) \ + (tw_mask_readl((reg), ((mask) << (shift))) >> (shift)) + +#define tw_writel(reg, value) writel((value), dev->mmio + reg) +#define tw_mask_writel(reg, mask, value) \ + tw_writel(reg, (tw_readl(reg) & ~(mask)) | ((value) & (mask))) +#define tw_mask_shift_writel(reg, mask, shift, value) \ + tw_mask_writel((reg), ((mask) << (shift)), ((value) << (shift))) + +#define tw_setl(reg, bit) tw_writel((reg), tw_readl(reg) | (bit)) +#define tw_clearl(reg, bit) tw_writel((reg), tw_readl(reg) & ~(bit)) + +u8 tw5864_indir_readb(struct tw5864_dev *dev, u16 addr); +#define tw_indir_readb(addr) tw5864_indir_readb(dev, addr) +void tw5864_indir_writeb(struct tw5864_dev *dev, u16 addr, u8 data); +#define tw_indir_writeb(addr, data) tw5864_indir_writeb(dev, addr, data) + +void tw5864_irqmask_apply(struct tw5864_dev *dev); +int tw5864_video_init(struct tw5864_dev *dev, int *video_nr); +void tw5864_video_fini(struct tw5864_dev *dev); +void tw5864_prepare_frame_headers(struct tw5864_input *input); +void tw5864_h264_put_stream_header(u8 **buf, size_t *space_left, int qp, + int width, int height); +void tw5864_h264_put_slice_header(u8 **buf, size_t *space_left, + unsigned int idr_pic_id, + unsigned int frame_gop_seqno, + int *tail_nb_bits, u8 *tail); +void tw5864_request_encoded_frame(struct tw5864_input *input); diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 5e8212845c87..a45e02367321 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -535,7 +535,7 @@ static void tw68_stop_streaming(struct vb2_queue *q) } } -static struct vb2_ops tw68_video_qops = { +static const struct vb2_ops tw68_video_qops = { .queue_setup = tw68_queue_setup, .buf_queue = tw68_buf_queue, .buf_prepare = tw68_buf_prepare, diff --git a/drivers/media/pci/tw686x/tw686x-audio.c b/drivers/media/pci/tw686x/tw686x-audio.c index 96e444c49173..77190768622a 100644 --- a/drivers/media/pci/tw686x/tw686x-audio.c +++ b/drivers/media/pci/tw686x/tw686x-audio.c @@ -269,7 +269,7 @@ static snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss) return bytes_to_frames(ss->runtime, ac->ptr); } -static struct snd_pcm_ops tw686x_pcm_ops = { +static const struct snd_pcm_ops tw686x_pcm_ops = { .open = tw686x_pcm_open, .close = tw686x_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index cdb16de770fe..c3fafa97b2d0 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -577,7 +577,7 @@ static int tw686x_buf_prepare(struct vb2_buffer *vb) return 0; } -static struct vb2_ops tw686x_video_qops = { +static const struct vb2_ops tw686x_video_qops = { .queue_setup = tw686x_queue_setup, .buf_queue = tw686x_buf_queue, .buf_prepare = tw686x_buf_prepare, @@ -672,30 +672,20 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int tw686x_set_format(struct tw686x_video_channel *vc, + unsigned int pixelformat, unsigned int width, + unsigned int height, bool realloc) { - struct tw686x_video_channel *vc = video_drvdata(file); struct tw686x_dev *dev = vc->dev; - u32 val, width, line_width, height; - unsigned long bitsperframe; + u32 val, dma_width, dma_height, dma_line_width; int err, pb; - if (vb2_is_busy(&vc->vidq)) - return -EBUSY; - - bitsperframe = vc->width * vc->height * vc->format->depth; - err = tw686x_try_fmt_vid_cap(file, priv, f); - if (err) - return err; - - vc->format = format_by_fourcc(f->fmt.pix.pixelformat); - vc->width = f->fmt.pix.width; - vc->height = f->fmt.pix.height; + vc->format = format_by_fourcc(pixelformat); + vc->width = width; + vc->height = height; /* We need new DMA buffers if the framesize has changed */ - if (dev->dma_ops->alloc && - bitsperframe != vc->width * vc->height * vc->format->depth) { + if (dev->dma_ops->alloc && realloc) { for (pb = 0; pb < 2; pb++) dev->dma_ops->free(vc, pb); @@ -739,14 +729,36 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); /* Program the DMA frame size */ - width = (vc->width * 2) & 0x7ff; - height = vc->height / 2; - line_width = (vc->width * 2) & 0x7ff; - val = (height << 22) | (line_width << 11) | width; + dma_width = (vc->width * 2) & 0x7ff; + dma_height = vc->height / 2; + dma_line_width = (vc->width * 2) & 0x7ff; + val = (dma_height << 22) | (dma_line_width << 11) | dma_width; reg_write(vc->dev, VDMA_WHP[vc->ch], val); return 0; } +static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + unsigned long area; + bool realloc; + int err; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + area = vc->width * vc->height; + err = tw686x_try_fmt_vid_cap(file, priv, f); + if (err) + return err; + + realloc = area != (f->fmt.pix.width * f->fmt.pix.height); + return tw686x_set_format(vc, f->fmt.pix.pixelformat, + f->fmt.pix.width, f->fmt.pix.height, + realloc); +} + static int tw686x_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -763,17 +775,9 @@ static int tw686x_querycap(struct file *file, void *priv, return 0; } -static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +static int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id) { - struct tw686x_video_channel *vc = video_drvdata(file); - struct v4l2_format f; - u32 val, ret; - - if (vc->video_standard == id) - return 0; - - if (vb2_is_busy(&vc->vidq)) - return -EBUSY; + u32 val; if (id & V4L2_STD_NTSC) val = 0; @@ -802,14 +806,31 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); reg_write(vc->dev, VIDEO_CONTROL1, val); + return 0; +} + +static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + struct v4l2_format f; + int ret; + + if (vc->video_standard == id) + return 0; + + if (vb2_is_busy(&vc->vidq)) + return -EBUSY; + + ret = tw686x_set_standard(vc, id); + if (ret) + return ret; /* * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change, * calling g_fmt and s_fmt will sanitize the height * according to the standard. */ - ret = tw686x_g_fmt_vid_cap(file, priv, &f); - if (!ret) - tw686x_s_fmt_vid_cap(file, priv, &f); + tw686x_g_fmt_vid_cap(file, priv, &f); + tw686x_s_fmt_vid_cap(file, priv, &f); /* * Frame decimation depends on the chosen standard, @@ -885,6 +906,42 @@ static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) return 0; } +static int tw686x_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + + if (fsize->index) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.max_width = TW686X_VIDEO_WIDTH; + fsize->stepwise.min_width = fsize->stepwise.max_width / 2; + fsize->stepwise.step_width = fsize->stepwise.min_width; + fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard); + fsize->stepwise.min_height = fsize->stepwise.max_height / 2; + fsize->stepwise.step_height = fsize->stepwise.min_height; + return 0; +} + +static int tw686x_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *ival) +{ + struct tw686x_video_channel *vc = video_drvdata(file); + int max_fps = TW686X_MAX_FPS(vc->video_standard); + int max_rates = DIV_ROUND_UP(max_fps, 2); + + if (ival->index >= max_rates) + return -EINVAL; + + ival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + ival->discrete.numerator = 1; + if (ival->index < (max_rates - 1)) + ival->discrete.denominator = (ival->index + 1) * 2; + else + ival->discrete.denominator = max_fps; + return 0; +} + static int tw686x_g_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { @@ -928,10 +985,21 @@ static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } +static void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i) +{ + u32 val; + + vc->input = i; + + val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); + val &= ~(0x3 << 30); + val |= i << 30; + reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); +} + static int tw686x_s_input(struct file *file, void *priv, unsigned int i) { struct tw686x_video_channel *vc = video_drvdata(file); - u32 val; if (i >= TW686X_INPUTS_PER_CH) return -EINVAL; @@ -943,12 +1011,7 @@ static int tw686x_s_input(struct file *file, void *priv, unsigned int i) if (vb2_is_busy(&vc->vidq)) return -EBUSY; - vc->input = i; - - val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); - val &= ~(0x3 << 30); - val |= i << 30; - reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); + tw686x_set_input(vc, i); return 0; } @@ -1007,6 +1070,8 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { .vidioc_g_parm = tw686x_g_parm, .vidioc_s_parm = tw686x_s_parm, + .vidioc_enum_framesizes = tw686x_enum_framesizes, + .vidioc_enum_frameintervals = tw686x_enum_frameintervals, .vidioc_enum_input = tw686x_enum_input, .vidioc_g_input = tw686x_g_input, @@ -1093,8 +1158,7 @@ void tw686x_video_free(struct tw686x_dev *dev) for (ch = 0; ch < max_channels(dev); ch++) { struct tw686x_video_channel *vc = &dev->video_channels[ch]; - if (vc->device) - video_unregister_device(vc->device); + video_unregister_device(vc->device); if (dev->dma_ops->free) for (pb = 0; pb < 2; pb++) @@ -1104,7 +1168,7 @@ void tw686x_video_free(struct tw686x_dev *dev) int tw686x_video_init(struct tw686x_dev *dev) { - unsigned int ch, val, pb; + unsigned int ch, val; int err; if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) @@ -1138,27 +1202,23 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->ch = ch; /* default settings */ - vc->format = &formats[0]; - vc->video_standard = V4L2_STD_NTSC; - vc->width = TW686X_VIDEO_WIDTH; - vc->height = TW686X_VIDEO_HEIGHT(vc->video_standard); - vc->input = 0; + err = tw686x_set_standard(vc, V4L2_STD_NTSC); + if (err) + goto error; - reg_write(vc->dev, SDT[ch], 0); - tw686x_set_framerate(vc, 30); + err = tw686x_set_format(vc, formats[0].fourcc, + TW686X_VIDEO_WIDTH, + TW686X_VIDEO_HEIGHT(vc->video_standard), + true); + if (err) + goto error; + tw686x_set_input(vc, 0); + tw686x_set_framerate(vc, 30); reg_write(dev, VDELAY_LO[ch], 0x14); reg_write(dev, HACTIVE_LO[ch], 0xd0); reg_write(dev, VIDEO_SIZE[ch], 0); - if (dev->dma_ops->alloc) { - for (pb = 0; pb < 2; pb++) { - err = dev->dma_ops->alloc(vc, pb); - if (err) - goto error; - } - } - vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vc->vidq.drv_priv = vc; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 80caa70c6360..d6b631add216 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -2365,94 +2365,80 @@ static int zoran_s_output(struct file *file, void *__fh, unsigned int output) } /* cropping (sub-frame capture) */ -static int zoran_cropcap(struct file *file, void *__fh, - struct v4l2_cropcap *cropcap) +static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - int type = cropcap->type, res = 0; - memset(cropcap, 0, sizeof(*cropcap)); - cropcap->type = type; + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { dprintk(1, KERN_ERR - "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", + "%s: VIDIOC_G_SELECTION - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); - res = -EINVAL; - return res; + return -EINVAL; } - cropcap->bounds.top = cropcap->bounds.left = 0; - cropcap->bounds.width = BUZ_MAX_WIDTH; - cropcap->bounds.height = BUZ_MAX_HEIGHT; - cropcap->defrect.top = cropcap->defrect.left = 0; - cropcap->defrect.width = BUZ_MIN_WIDTH; - cropcap->defrect.height = BUZ_MIN_HEIGHT; - return res; -} - -static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int type = crop->type, res = 0; - - memset(crop, 0, sizeof(*crop)); - crop->type = type; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - res = -EINVAL; - return res; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r.top = fh->jpg_settings.img_y; + sel->r.left = fh->jpg_settings.img_x; + sel->r.width = fh->jpg_settings.img_width; + sel->r.height = fh->jpg_settings.img_height; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = sel->r.left = 0; + sel->r.width = BUZ_MIN_WIDTH; + sel->r.height = BUZ_MIN_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = sel->r.left = 0; + sel->r.width = BUZ_MAX_WIDTH; + sel->r.height = BUZ_MAX_HEIGHT; + break; + default: + return -EINVAL; } - - crop->c.top = fh->jpg_settings.img_y; - crop->c.left = fh->jpg_settings.img_x; - crop->c.width = fh->jpg_settings.img_width; - crop->c.height = fh->jpg_settings.img_height; - return res; + return 0; } -static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop) +static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - int res = 0; struct zoran_jpg_settings settings; + int res; - settings = fh->jpg_settings; + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - if (fh->buffers.allocated) { + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { dprintk(1, KERN_ERR - "%s: VIDIOC_S_CROP - cannot change settings while active\n", + "%s: VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); - res = -EBUSY; - return res; + return -EINVAL; } - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { + settings = fh->jpg_settings; + + if (fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", + "%s: VIDIOC_S_SELECTION - cannot change settings while active\n", ZR_DEVNAME(zr)); - res = -EINVAL; - return res; + return -EBUSY; } /* move into a form that we understand */ - settings.img_x = crop->c.left; - settings.img_y = crop->c.top; - settings.img_width = crop->c.width; - settings.img_height = crop->c.height; + settings.img_x = sel->r.left; + settings.img_y = sel->r.top; + settings.img_width = sel->r.width; + settings.img_height = sel->r.height; /* check validity */ res = zoran_check_jpg_settings(zr, &settings, 0); @@ -2808,9 +2794,8 @@ zoran_mmap (struct file *file, static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_querycap = zoran_querycap, - .vidioc_cropcap = zoran_cropcap, - .vidioc_s_crop = zoran_s_crop, - .vidioc_g_crop = zoran_g_crop, + .vidioc_s_selection = zoran_s_selection, + .vidioc_g_selection = zoran_g_selection, .vidioc_enum_input = zoran_enum_input, .vidioc_g_input = zoran_g_input, .vidioc_s_input = zoran_s_input, diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 552b635cfce7..ce4a96fccc43 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -91,6 +91,15 @@ config VIDEO_OMAP3_DEBUG ---help--- Enable debug messages on OMAP 3 camera controller driver. +config VIDEO_PXA27x + tristate "PXA27x Quick Capture Interface driver" + depends on VIDEO_DEV && HAS_DMA + depends on PXA27x || COMPILE_TEST + select VIDEOBUF2_DMA_SG + select SG_SPLIT + ---help--- + This is a v4l2 driver for the PXA27x Quick Capture Interface + config VIDEO_S3C_CAMIF tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API @@ -107,10 +116,10 @@ config VIDEO_S3C_CAMIF source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" -source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" source "drivers/media/platform/rcar-vin/Kconfig" +source "drivers/media/platform/atmel/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" @@ -155,7 +164,7 @@ config VIDEO_CODA config VIDEO_MEDIATEK_VPU tristate "Mediatek Video Processor Unit" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA depends on ARCH_MEDIATEK || COMPILE_TEST ---help--- This driver provides downloading VPU firmware and @@ -257,6 +266,21 @@ config VIDEO_STI_BDISP help This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. +config VIDEO_STI_HVA + tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on HAS_DMA + depends on ARCH_STI || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format + video encoder of STMicroelectronics SoC, allowing hardware encoding of + raw uncompressed formats in various compressed video bitstreams format. + + To compile this driver as a module, choose M here: + the module will be called st-hva. + config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 21771c1a13fb..40b18d12726e 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o @@ -30,12 +31,12 @@ obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ +obj-$(CONFIG_VIDEO_STI_HVA) += sti/hva/ obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ obj-$(CONFIG_BLACKFIN) += blackfin/ @@ -58,6 +59,8 @@ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/ + ccflags-y += -I$(srctree)/drivers/media/i2c obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig new file mode 100644 index 000000000000..867dca22a473 --- /dev/null +++ b/drivers/media/platform/atmel/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_ATMEL_ISC + tristate "ATMEL Image Sensor Controller (ISC) support" + depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on ARCH_AT91 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + This module makes the ATMEL Image Sensor Controller available + as a v4l2 device.
\ No newline at end of file diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile new file mode 100644 index 000000000000..9d7c999d434d --- /dev/null +++ b/drivers/media/platform/atmel/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h new file mode 100644 index 000000000000..00c449717cde --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -0,0 +1,165 @@ +#ifndef __ATMEL_ISC_REGS_H +#define __ATMEL_ISC_REGS_H + +#include <linux/bitops.h> + +/* ISC Control Enable Register 0 */ +#define ISC_CTRLEN 0x00000000 + +/* ISC Control Disable Register 0 */ +#define ISC_CTRLDIS 0x00000004 + +/* ISC Control Status Register 0 */ +#define ISC_CTRLSR 0x00000008 + +#define ISC_CTRL_CAPTURE BIT(0) +#define ISC_CTRL_UPPRO BIT(1) +#define ISC_CTRL_HISREQ BIT(2) +#define ISC_CTRL_HISCLR BIT(3) + +/* ISC Parallel Front End Configuration 0 Register */ +#define ISC_PFE_CFG0 0x0000000c + +#define ISC_PFE_CFG0_HPOL_LOW BIT(0) +#define ISC_PFE_CFG0_VPOL_LOW BIT(1) +#define ISC_PFE_CFG0_PPOL_LOW BIT(2) + +#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) +#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) + +#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) +#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) +#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) +#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) +#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) +#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) + +/* ISC Clock Enable Register */ +#define ISC_CLKEN 0x00000018 + +/* ISC Clock Disable Register */ +#define ISC_CLKDIS 0x0000001c + +/* ISC Clock Status Register */ +#define ISC_CLKSR 0x00000020 + +#define ISC_CLK(n) BIT(n) + +/* ISC Clock Configuration Register */ +#define ISC_CLKCFG 0x00000024 +#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) +#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) +#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) +#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) + +/* ISC Interrupt Enable Register */ +#define ISC_INTEN 0x00000028 + +/* ISC Interrupt Disable Register */ +#define ISC_INTDIS 0x0000002c + +/* ISC Interrupt Mask Register */ +#define ISC_INTMASK 0x00000030 + +/* ISC Interrupt Status Register */ +#define ISC_INTSR 0x00000034 + +#define ISC_INT_DDONE BIT(8) + +/* ISC White Balance Control Register */ +#define ISC_WB_CTRL 0x00000058 + +/* ISC White Balance Configuration Register */ +#define ISC_WB_CFG 0x0000005c + +/* ISC Color Filter Array Control Register */ +#define ISC_CFA_CTRL 0x00000070 + +/* ISC Color Filter Array Configuration Register */ +#define ISC_CFA_CFG 0x00000074 + +#define ISC_BAY_CFG_GRGR 0x0 +#define ISC_BAY_CFG_RGRG 0x1 +#define ISC_BAY_CFG_GBGB 0x2 +#define ISC_BAY_CFG_BGBG 0x3 +#define ISC_BAY_CFG_MASK GENMASK(1, 0) + +/* ISC Color Correction Control Register */ +#define ISC_CC_CTRL 0x00000078 + +/* ISC Gamma Correction Control Register */ +#define ISC_GAM_CTRL 0x00000094 + +/* Color Space Conversion Control Register */ +#define ISC_CSC_CTRL 0x00000398 + +/* Contrast And Brightness Control Register */ +#define ISC_CBC_CTRL 0x000003b4 + +/* Subsampling 4:4:4 to 4:2:2 Control Register */ +#define ISC_SUB422_CTRL 0x000003c4 + +/* Subsampling 4:2:2 to 4:2:0 Control Register */ +#define ISC_SUB420_CTRL 0x000003cc + +/* Rounding, Limiting and Packing Configuration Register */ +#define ISC_RLP_CFG 0x000003d0 + +#define ISC_RLP_CFG_MODE_DAT8 0x0 +#define ISC_RLP_CFG_MODE_DAT9 0x1 +#define ISC_RLP_CFG_MODE_DAT10 0x2 +#define ISC_RLP_CFG_MODE_DAT11 0x3 +#define ISC_RLP_CFG_MODE_DAT12 0x4 +#define ISC_RLP_CFG_MODE_DATY8 0x5 +#define ISC_RLP_CFG_MODE_DATY10 0x6 +#define ISC_RLP_CFG_MODE_ARGB444 0x7 +#define ISC_RLP_CFG_MODE_ARGB555 0x8 +#define ISC_RLP_CFG_MODE_RGB565 0x9 +#define ISC_RLP_CFG_MODE_ARGB32 0xa +#define ISC_RLP_CFG_MODE_YYCC 0xb +#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc +#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) + +/* DMA Configuration Register */ +#define ISC_DCFG 0x000003e0 +#define ISC_DCFG_IMODE_PACKED8 0x0 +#define ISC_DCFG_IMODE_PACKED16 0x1 +#define ISC_DCFG_IMODE_PACKED32 0x2 +#define ISC_DCFG_IMODE_YC422SP 0x3 +#define ISC_DCFG_IMODE_YC422P 0x4 +#define ISC_DCFG_IMODE_YC420SP 0x5 +#define ISC_DCFG_IMODE_YC420P 0x6 +#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) + +#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) +#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) +#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) +#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) +#define ISC_DCFG_YMBSIZE_MASK GENMASK(5, 4) + +#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) +#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) +#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) +#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) +#define ISC_DCFG_CMBSIZE_MASK GENMASK(9, 8) + +/* DMA Control Register */ +#define ISC_DCTRL 0x000003e4 + +#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) +#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) +#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) +#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) + +#define ISC_DCTRL_IE_IS (0x0 << 4) + +/* DMA Descriptor Address Register */ +#define ISC_DNDA 0x000003e8 + +/* DMA Address 0 Register */ +#define ISC_DAD0 0x000003ec + +/* DMA Stride 0 Register */ +#define ISC_DST0 0x000003f0 + +#endif diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c new file mode 100644 index 000000000000..ccfe13b7d3f8 --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -0,0 +1,1520 @@ +/* + * Atmel Image Sensor Controller (ISC) driver + * + * Copyright (C) 2016 Atmel + * + * Author: Songjun Wu <songjun.wu@microchip.com> + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA + * + * ISC video pipeline integrates the following submodules: + * PFE: Parallel Front End to sample the camera sensor input stream + * WB: Programmable white balance in the Bayer domain + * CFA: Color filter array interpolation module + * CC: Programmable color correction + * GAM: Gamma correction + * CSC: Programmable color space conversion + * CBC: Contrast and Brightness control + * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling + * RLP: This module performs rounding, range limiting + * and packing of the incoming data + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-of.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "atmel-isc-regs.h" + +#define ATMEL_ISC_NAME "atmel_isc" + +#define ISC_MAX_SUPPORT_WIDTH 2592 +#define ISC_MAX_SUPPORT_HEIGHT 1944 + +#define ISC_CLK_MAX_DIV 255 + +enum isc_clk_id { + ISC_ISPCK = 0, + ISC_MCK = 1, +}; + +struct isc_clk { + struct clk_hw hw; + struct clk *clk; + struct regmap *regmap; + u8 id; + u8 parent_id; + u32 div; + struct device *dev; +}; + +#define to_isc_clk(hw) container_of(hw, struct isc_clk, hw) + +struct isc_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct isc_subdev_entity { + struct v4l2_subdev *sd; + struct v4l2_async_subdev *asd; + struct v4l2_async_notifier notifier; + struct v4l2_subdev_pad_config *config; + + u32 pfe_cfg0; + + struct list_head list; +}; + +/* + * struct isc_format - ISC media bus format information + * @fourcc: Fourcc code for this format + * @mbus_code: V4L2 media bus format code. + * @bpp: Bytes per pixel (when stored in memory) + * @reg_bps: reg value for bits per sample + * (when transferred over a bus) + * @support: Indicates format supported by subdev + */ +struct isc_format { + u32 fourcc; + u32 mbus_code; + u8 bpp; + + u32 reg_bps; + u32 reg_rlp_mode; + u32 reg_dcfg_imode; + u32 reg_dctrl_dview; + + bool support; +}; + +#define ISC_PIPE_LINE_NODE_NUM 11 + +struct isc_device { + struct regmap *regmap; + struct clk *hclock; + struct clk *ispck; + struct isc_clk isc_clks[2]; + + struct device *dev; + struct v4l2_device v4l2_dev; + struct video_device video_dev; + + struct vb2_queue vb2_vidq; + spinlock_t dma_queue_lock; + struct list_head dma_queue; + struct isc_buffer *cur_frm; + unsigned int sequence; + bool stop; + struct completion comp; + + struct v4l2_format fmt; + struct isc_format **user_formats; + unsigned int num_user_formats; + const struct isc_format *current_fmt; + + struct mutex lock; + + struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; + + struct isc_subdev_entity *current_subdev; + struct list_head subdev_entities; +}; + +static struct isc_format isc_formats[] = { + { V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, + 2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, +}; + +static int isc_clk_enable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + struct regmap *regmap = isc_clk->regmap; + + dev_dbg(isc_clk->dev, "ISC CLK: %s, div = %d, parent id = %d\n", + __func__, isc_clk->div, isc_clk->parent_id); + + regmap_update_bits(regmap, ISC_CLKCFG, + ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), + (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | + (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); + + regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); + + return 0; +} + +static void isc_clk_disable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + + regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); +} + +static int isc_clk_is_enabled(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 status; + + regmap_read(isc_clk->regmap, ISC_CLKSR, &status); + + return status & ISC_CLK(isc_clk->id) ? 1 : 0; +} + +static unsigned long +isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); +} + +static int isc_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + long best_rate = -EINVAL; + int best_diff = -1; + unsigned int i, div; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + if (!parent_rate) + continue; + + for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { + unsigned long rate; + int diff; + + rate = DIV_ROUND_CLOSEST(parent_rate, div); + diff = abs(req->rate - rate); + + if (best_diff < 0 || best_diff > diff) { + best_rate = rate; + best_diff = diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + dev_dbg(isc_clk->dev, + "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + + return 0; +} + +static int isc_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + isc_clk->parent_id = index; + + return 0; +} + +static u8 isc_clk_get_parent(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return isc_clk->parent_id; +} + +static int isc_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > (ISC_CLK_MAX_DIV + 1) || !div) + return -EINVAL; + + isc_clk->div = div - 1; + + return 0; +} + +static const struct clk_ops isc_clk_ops = { + .enable = isc_clk_enable, + .disable = isc_clk_disable, + .is_enabled = isc_clk_is_enabled, + .recalc_rate = isc_clk_recalc_rate, + .determine_rate = isc_clk_determine_rate, + .set_parent = isc_clk_set_parent, + .get_parent = isc_clk_get_parent, + .set_rate = isc_clk_set_rate, +}; + +static int isc_clk_register(struct isc_device *isc, unsigned int id) +{ + struct regmap *regmap = isc->regmap; + struct device_node *np = isc->dev->of_node; + struct isc_clk *isc_clk; + struct clk_init_data init; + const char *clk_name = np->name; + const char *parent_names[3]; + int num_parents; + + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1 || num_parents > 3) + return -EINVAL; + + if (num_parents > 2 && id == ISC_ISPCK) + num_parents = 2; + + of_clk_parent_fill(np, parent_names, num_parents); + + if (id == ISC_MCK) + of_property_read_string(np, "clock-output-names", &clk_name); + else + clk_name = "isc-ispck"; + + init.parent_names = parent_names; + init.num_parents = num_parents; + init.name = clk_name; + init.ops = &isc_clk_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + isc_clk = &isc->isc_clks[id]; + isc_clk->hw.init = &init; + isc_clk->regmap = regmap; + isc_clk->id = id; + isc_clk->dev = isc->dev; + + isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); + if (IS_ERR(isc_clk->clk)) { + dev_err(isc->dev, "%s: clock register fail\n", clk_name); + return PTR_ERR(isc_clk->clk); + } else if (id == ISC_MCK) + of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); + + return 0; +} + +static int isc_clk_init(struct isc_device *isc) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) + isc->isc_clks[i].clk = ERR_PTR(-EINVAL); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + ret = isc_clk_register(isc, i); + if (ret) + return ret; + } + + return 0; +} + +static void isc_clk_cleanup(struct isc_device *isc) +{ + unsigned int i; + + of_clk_del_provider(isc->dev->of_node); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + struct isc_clk *isc_clk = &isc->isc_clks[i]; + + if (!IS_ERR(isc_clk->clk)) + clk_unregister(isc_clk->clk); + } +} + +static int isc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned int size = isc->fmt.fmt.pix.sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int isc_buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = isc->fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + vbuf->field = isc->fmt.fmt.pix.field; + + return 0; +} + +static inline void isc_start_dma(struct regmap *regmap, + struct isc_buffer *frm, u32 dview) +{ + dma_addr_t addr; + + addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); + + regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS); + regmap_write(regmap, ISC_DAD0, addr); + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); +} + +static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) +{ + u32 val; + unsigned int i; + + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + val = pipeline & BIT(i) ? 1 : 0; + regmap_field_write(isc->pipeline[i], val); + } +} + +static int isc_configure(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + const struct isc_format *current_fmt = isc->current_fmt; + struct isc_subdev_entity *subdev = isc->current_subdev; + u32 val, mask; + int counter = 10; + + val = current_fmt->reg_bps | subdev->pfe_cfg0 | + ISC_PFE_CFG0_MODE_PROGRESSIVE; + mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | + ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | + ISC_PFE_CFG0_MODE_MASK; + + regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val); + + regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK, + current_fmt->reg_rlp_mode); + + regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, + current_fmt->reg_dcfg_imode); + + /* Disable the pipeline */ + isc_set_pipeline(isc, 0x0); + + /* Update profile */ + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); + + regmap_read(regmap, ISC_CTRLSR, &val); + while ((val & ISC_CTRL_UPPRO) && counter--) { + usleep_range(1000, 2000); + regmap_read(regmap, ISC_CTRLSR, &val); + } + + if (counter < 0) + return -ETIMEDOUT; + + return 0; +} + +static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + struct regmap *regmap = isc->regmap; + struct isc_buffer *buf; + unsigned long flags; + int ret; + u32 val; + + /* Enable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&isc->v4l2_dev, "stream on failed in subdev\n"); + goto err_start_stream; + } + + pm_runtime_get_sync(isc->dev); + + /* Disable all the interrupts */ + regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL); + + /* Clean the interrupt status register */ + regmap_read(regmap, ISC_INTSR, &val); + + ret = isc_configure(isc); + if (unlikely(ret)) + goto err_configure; + + /* Enable DMA interrupt */ + regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + + isc->sequence = 0; + isc->stop = false; + reinit_completion(&isc->comp); + + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview); + + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + return 0; + +err_configure: + pm_runtime_put_sync(isc->dev); + + v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + +err_start_stream: + spin_lock_irqsave(&isc->dma_queue_lock, flags); + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + return ret; +} + +static void isc_stop_streaming(struct vb2_queue *vq) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned long flags; + struct isc_buffer *buf; + int ret; + + isc->stop = true; + + /* Wait until the end of the current frame */ + if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) + v4l2_err(&isc->v4l2_dev, + "Timeout waiting for end of the capture\n"); + + /* Disable DMA interrupt */ + regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); + + pm_runtime_put_sync(isc->dev); + + /* Disable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); + + /* Release all active buffers */ + spin_lock_irqsave(&isc->dma_queue_lock, flags); + if (unlikely(isc->cur_frm)) { + vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + isc->cur_frm = NULL; + } + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static void isc_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + list_add_tail(&buf->list, &isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static struct vb2_ops isc_vb2_ops = { + .queue_setup = isc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = isc_buffer_prepare, + .start_streaming = isc_start_streaming, + .stop_streaming = isc_stop_streaming, + .buf_queue = isc_buffer_queue, +}; + +static int isc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct isc_device *isc = video_drvdata(file); + + strcpy(cap->driver, ATMEL_ISC_NAME); + strcpy(cap->card, "Atmel Image Sensor Controller"); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", isc->v4l2_dev.name); + + return 0; +} + +static int isc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isc_device *isc = video_drvdata(file); + u32 index = f->index; + + if (index >= isc->num_user_formats) + return -EINVAL; + + f->pixelformat = isc->user_formats[index]->fourcc; + + return 0; +} + +static int isc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct isc_device *isc = video_drvdata(file); + + *fmt = isc->fmt; + + return 0; +} + +static struct isc_format *find_format_by_fourcc(struct isc_device *isc, + unsigned int fourcc) +{ + unsigned int num_formats = isc->num_user_formats; + struct isc_format *fmt; + unsigned int i; + + for (i = 0; i < num_formats; i++) { + fmt = isc->user_formats[i]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, + struct isc_format **current_fmt) +{ + struct isc_format *isc_fmt; + struct v4l2_pix_format *pixfmt = &f->fmt.pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + isc_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); + if (!isc_fmt) { + v4l2_warn(&isc->v4l2_dev, "Format 0x%x not found\n", + pixfmt->pixelformat); + isc_fmt = isc->user_formats[isc->num_user_formats - 1]; + pixfmt->pixelformat = isc_fmt->fourcc; + } + + /* Limit to Atmel ISC hardware capabilities */ + if (pixfmt->width > ISC_MAX_SUPPORT_WIDTH) + pixfmt->width = ISC_MAX_SUPPORT_WIDTH; + if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT) + pixfmt->height = ISC_MAX_SUPPORT_HEIGHT; + + v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, + isc->current_subdev->config, &format); + if (ret < 0) + return ret; + + v4l2_fill_pix_format(pixfmt, &format.format); + + pixfmt->field = V4L2_FIELD_NONE; + pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (current_fmt) + *current_fmt = isc_fmt; + + return 0; +} + +static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) +{ + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct isc_format *current_fmt; + int ret; + + ret = isc_try_fmt(isc, f, ¤t_fmt); + if (ret) + return ret; + + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, + current_fmt->mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, + set_fmt, NULL, &format); + if (ret < 0) + return ret; + + isc->fmt = *f; + isc->current_fmt = current_fmt; + + return 0; +} + +static int isc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + if (vb2_is_streaming(&isc->vb2_vidq)) + return -EBUSY; + + return isc_set_fmt(isc, f); +} + +static int isc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + return isc_try_fmt(isc, f, NULL); +} + +static int isc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = 0; + strcpy(inp->name, "Camera"); + + return 0; +} + +static int isc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int isc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(isc->current_subdev->sd, video, g_parm, a); +} + +static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(isc->current_subdev->sd, video, s_parm, a); +} + +static int isc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isc_device *isc = video_drvdata(file); + const struct isc_format *isc_fmt; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + isc_fmt = find_format_by_fourcc(isc, fsize->pixel_format); + if (!isc_fmt) + return -EINVAL; + + fse.code = isc_fmt->mbus_code; + + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, + NULL, &fse); + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int isc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct isc_device *isc = video_drvdata(file); + const struct isc_format *isc_fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + isc_fmt = find_format_by_fourcc(isc, fival->pixel_format); + if (!isc_fmt) + return -EINVAL; + + fie.code = isc_fmt->mbus_code; + + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, + enum_frame_interval, NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_ioctl_ops isc_ioctl_ops = { + .vidioc_querycap = isc_querycap, + .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, + + .vidioc_enum_input = isc_enum_input, + .vidioc_g_input = isc_g_input, + .vidioc_s_input = isc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_parm = isc_g_parm, + .vidioc_s_parm = isc_s_parm, + .vidioc_enum_framesizes = isc_enum_framesizes, + .vidioc_enum_frameintervals = isc_enum_frameintervals, +}; + +static int isc_open(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + int ret; + + if (mutex_lock_interruptible(&isc->lock)) + return -ERESTARTSYS; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); + goto unlock; + } + + ret = isc_set_fmt(isc, &isc->fmt); + if (ret) { + v4l2_subdev_call(sd, core, s_power, 0); + v4l2_fh_release(file); + } + +unlock: + mutex_unlock(&isc->lock); + return ret; +} + +static int isc_release(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + bool fh_singular; + int ret; + + mutex_lock(&isc->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + mutex_unlock(&isc->lock); + + return ret; +} + +static const struct v4l2_file_operations isc_fops = { + .owner = THIS_MODULE, + .open = isc_open, + .release = isc_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static irqreturn_t isc_interrupt(int irq, void *dev_id) +{ + struct isc_device *isc = (struct isc_device *)dev_id; + struct regmap *regmap = isc->regmap; + u32 isc_intsr, isc_intmask, pending; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&isc->dma_queue_lock); + + regmap_read(regmap, ISC_INTSR, &isc_intsr); + regmap_read(regmap, ISC_INTMASK, &isc_intmask); + + pending = isc_intsr & isc_intmask; + + if (likely(pending & ISC_INT_DDONE)) { + if (isc->cur_frm) { + struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; + struct vb2_buffer *vb = &vbuf->vb2_buf; + + vb->timestamp = ktime_get_ns(); + vbuf->sequence = isc->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + isc->cur_frm = NULL; + } + + if (!list_empty(&isc->dma_queue) && !isc->stop) { + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_start_dma(regmap, isc->cur_frm, + isc->current_fmt->reg_dctrl_dview); + } + + if (isc->stop) + complete(&isc->comp); + + ret = IRQ_HANDLED; + } + + spin_unlock(&isc->dma_queue_lock); + + return ret; +} + +static int isc_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct isc_subdev_entity *subdev_entity = + container_of(notifier, struct isc_subdev_entity, notifier); + + if (video_is_registered(&isc->video_dev)) { + v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); + return -EBUSY; + } + + subdev_entity->sd = subdev; + + return 0; +} + +static void isc_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + + video_unregister_device(&isc->video_dev); + if (isc->current_subdev->config) + v4l2_subdev_free_pad_config(isc->current_subdev->config); +} + +static struct isc_format *find_format_by_code(unsigned int code, int *index) +{ + struct isc_format *fmt = &isc_formats[0]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isc_formats); i++) { + if (fmt->mbus_code == code) { + *index = i; + return fmt; + } + + fmt++; + } + + return NULL; +} + +static int isc_formats_init(struct isc_device *isc) +{ + struct isc_format *fmt; + struct v4l2_subdev *subdev = isc->current_subdev->sd; + int num_fmts = 0, i, j; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + fmt = &isc_formats[0]; + for (i = 0; i < ARRAY_SIZE(isc_formats); i++) { + fmt->support = false; + fmt++; + } + + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code)) { + mbus_code.index++; + fmt = find_format_by_code(mbus_code.code, &i); + if (!fmt) + continue; + + fmt->support = true; + num_fmts++; + } + + if (!num_fmts) + return -ENXIO; + + isc->num_user_formats = num_fmts; + isc->user_formats = devm_kcalloc(isc->dev, + num_fmts, sizeof(struct isc_format *), + GFP_KERNEL); + if (!isc->user_formats) { + v4l2_err(&isc->v4l2_dev, "could not allocate memory\n"); + return -ENOMEM; + } + + fmt = &isc_formats[0]; + for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) { + if (fmt->support) + isc->user_formats[j++] = fmt; + + fmt++; + } + + return 0; +} + +static int isc_set_default_fmt(struct isc_device *isc) +{ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .field = V4L2_FIELD_NONE, + .pixelformat = isc->user_formats[0]->fourcc, + }, + }; + int ret; + + ret = isc_try_fmt(isc, &f, NULL); + if (ret) + return ret; + + isc->current_fmt = isc->user_formats[0]; + isc->fmt = f; + + return 0; +} + +static int isc_async_complete(struct v4l2_async_notifier *notifier) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct isc_subdev_entity *sd_entity; + struct video_device *vdev = &isc->video_dev; + struct vb2_queue *q = &isc->vb2_vidq; + int ret; + + isc->current_subdev = container_of(notifier, + struct isc_subdev_entity, notifier); + sd_entity = isc->current_subdev; + + mutex_init(&isc->lock); + init_completion(&isc->comp); + + /* Initialize videobuf2 queue */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = isc; + q->buf_struct_size = sizeof(struct isc_buffer); + q->ops = &isc_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &isc->lock; + q->min_buffers_needed = 1; + q->dev = isc->dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "vb2_queue_init() failed: %d\n", ret); + return ret; + } + + /* Init video dma queues */ + INIT_LIST_HEAD(&isc->dma_queue); + spin_lock_init(&isc->dma_queue_lock); + + sd_entity->config = v4l2_subdev_alloc_pad_config(sd_entity->sd); + if (sd_entity->config == NULL) + return -ENOMEM; + + ret = isc_formats_init(isc); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "Init format failed: %d\n", ret); + return ret; + } + + ret = isc_set_default_fmt(isc); + if (ret) { + v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); + return ret; + } + + /* Register video device */ + strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &isc_fops; + vdev->ioctl_ops = &isc_ioctl_ops; + vdev->v4l2_dev = &isc->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &isc->lock; + vdev->ctrl_handler = isc->current_subdev->sd->ctrl_handler; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + video_set_drvdata(vdev, isc); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "video_register_device failed: %d\n", ret); + return ret; + } + + return 0; +} + +static void isc_subdev_cleanup(struct isc_device *isc) +{ + struct isc_subdev_entity *subdev_entity; + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) + v4l2_async_notifier_unregister(&subdev_entity->notifier); + + INIT_LIST_HEAD(&isc->subdev_entities); +} + +static int isc_pipeline_init(struct isc_device *isc) +{ + struct device *dev = isc->dev; + struct regmap *regmap = isc->regmap; + struct regmap_field *regs; + unsigned int i; + + /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ + const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { + REG_FIELD(ISC_WB_CTRL, 0, 0), + REG_FIELD(ISC_CFA_CTRL, 0, 0), + REG_FIELD(ISC_CC_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 1, 1), + REG_FIELD(ISC_GAM_CTRL, 2, 2), + REG_FIELD(ISC_GAM_CTRL, 3, 3), + REG_FIELD(ISC_CSC_CTRL, 0, 0), + REG_FIELD(ISC_CBC_CTRL, 0, 0), + REG_FIELD(ISC_SUB422_CTRL, 0, 0), + REG_FIELD(ISC_SUB420_CTRL, 0, 0), + }; + + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + isc->pipeline[i] = regs; + } + + return 0; +} + +static int isc_parse_dt(struct device *dev, struct isc_device *isc) +{ + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *rem; + struct v4l2_of_endpoint v4l2_epn; + struct isc_subdev_entity *subdev_entity; + unsigned int flags; + int ret; + + INIT_LIST_HEAD(&isc->subdev_entities); + + for (; ;) { + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + break; + + rem = of_graph_get_remote_port_parent(epn); + if (!rem) { + dev_notice(dev, "Remote device at %s not found\n", + of_node_full_name(epn)); + continue; + } + + ret = v4l2_of_parse_endpoint(epn, &v4l2_epn); + if (ret) { + of_node_put(rem); + ret = -EINVAL; + dev_err(dev, "Could not parse the endpoint\n"); + break; + } + + subdev_entity = devm_kzalloc(dev, + sizeof(*subdev_entity), GFP_KERNEL); + if (subdev_entity == NULL) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + subdev_entity->asd = devm_kzalloc(dev, + sizeof(*subdev_entity->asd), GFP_KERNEL); + if (subdev_entity->asd == NULL) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + flags = v4l2_epn.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; + + subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_OF; + subdev_entity->asd->match.of.node = rem; + list_add_tail(&subdev_entity->list, &isc->subdev_entities); + } + + of_node_put(epn); + return ret; +} + +/* regmap configuration */ +#define ATMEL_ISC_REG_MAX 0xbfc +static const struct regmap_config isc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_ISC_REG_MAX, +}; + +static int atmel_isc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isc_device *isc; + struct resource *res; + void __iomem *io_base; + struct isc_subdev_entity *subdev_entity; + int irq; + int ret; + + isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); + if (!isc) + return -ENOMEM; + + platform_set_drvdata(pdev, isc); + isc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config); + if (IS_ERR(isc->regmap)) { + ret = PTR_ERR(isc->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + dev_err(dev, "failed to get irq: %d\n", ret); + return ret; + } + + ret = devm_request_irq(dev, irq, isc_interrupt, 0, + ATMEL_ISC_NAME, isc); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + ret = isc_pipeline_init(isc); + if (ret) + return ret; + + isc->hclock = devm_clk_get(dev, "hclock"); + if (IS_ERR(isc->hclock)) { + ret = PTR_ERR(isc->hclock); + dev_err(dev, "failed to get hclock: %d\n", ret); + return ret; + } + + ret = isc_clk_init(isc); + if (ret) { + dev_err(dev, "failed to init isc clock: %d\n", ret); + goto clean_isc_clk; + } + + isc->ispck = isc->isc_clks[ISC_ISPCK].clk; + + /* ispck should be greater or equal to hclock */ + ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); + if (ret) { + dev_err(dev, "failed to set ispck rate: %d\n", ret); + goto clean_isc_clk; + } + + ret = v4l2_device_register(dev, &isc->v4l2_dev); + if (ret) { + dev_err(dev, "unable to register v4l2 device.\n"); + goto clean_isc_clk; + } + + ret = isc_parse_dt(dev, isc); + if (ret) { + dev_err(dev, "fail to parse device tree\n"); + goto unregister_v4l2_device; + } + + if (list_empty(&isc->subdev_entities)) { + dev_err(dev, "no subdev found\n"); + goto unregister_v4l2_device; + } + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + subdev_entity->notifier.subdevs = &subdev_entity->asd; + subdev_entity->notifier.num_subdevs = 1; + subdev_entity->notifier.bound = isc_async_bound; + subdev_entity->notifier.unbind = isc_async_unbind; + subdev_entity->notifier.complete = isc_async_complete; + + ret = v4l2_async_notifier_register(&isc->v4l2_dev, + &subdev_entity->notifier); + if (ret) { + dev_err(dev, "fail to register async notifier\n"); + goto cleanup_subdev; + } + + if (video_is_registered(&isc->video_dev)) + break; + } + + pm_runtime_enable(dev); + + return 0; + +cleanup_subdev: + isc_subdev_cleanup(isc); + +unregister_v4l2_device: + v4l2_device_unregister(&isc->v4l2_dev); + +clean_isc_clk: + isc_clk_cleanup(isc); + + return ret; +} + +static int atmel_isc_remove(struct platform_device *pdev) +{ + struct isc_device *isc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + isc_subdev_cleanup(isc); + + v4l2_device_unregister(&isc->v4l2_dev); + + isc_clk_cleanup(isc); + + return 0; +} + +static int __maybe_unused isc_runtime_suspend(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + + clk_disable_unprepare(isc->ispck); + clk_disable_unprepare(isc->hclock); + + return 0; +} + +static int __maybe_unused isc_runtime_resume(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(isc->hclock); + if (ret) + return ret; + + return clk_prepare_enable(isc->ispck); +} + +static const struct dev_pm_ops atmel_isc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) +}; + +static const struct of_device_id atmel_isc_of_match[] = { + { .compatible = "atmel,sama5d2-isc" }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_isc_of_match); + +static struct platform_driver atmel_isc_driver = { + .probe = atmel_isc_probe, + .remove = atmel_isc_remove, + .driver = { + .name = ATMEL_ISC_NAME, + .pm = &atmel_isc_dev_pm_ops, + .of_match_table = of_match_ptr(atmel_isc_of_match), + }, +}; + +module_platform_driver(atmel_isc_driver); + +MODULE_AUTHOR("Songjun Wu <songjun.wu@microchip.com>"); +MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0b1709e96673..a9bc0175e4d3 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -440,7 +440,7 @@ vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, /* * Application initially set the image format. Current display * size is obtained from the vpbe display controller. expected_xsize - * and expected_ysize are set through S_CROP ioctl. Based on this, + * and expected_ysize are set through S_SELECTION ioctl. Based on this, * driver will calculate the scale factors for vertical and * horizontal direction so that the image is displayed scaled * and expanded. Application uses expansion to display the image @@ -649,24 +649,23 @@ static int vpbe_display_querycap(struct file *file, void *priv, return 0; } -static int vpbe_display_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop) +static int vpbe_display_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpbe_layer *layer = video_drvdata(file); struct vpbe_display *disp_dev = layer->disp_dev; struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; struct osd_layer_config *cfg = &layer->layer_info.config; struct osd_state *osd_device = disp_dev->osd_device; - struct v4l2_rect rect = crop->c; + struct v4l2_rect rect = sel->r; int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, - "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); + "VIDIOC_S_SELECTION, layer id = %d\n", layer->device_id); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - } if (rect.top < 0) rect.top = 0; @@ -714,32 +713,45 @@ static int vpbe_display_s_crop(struct file *file, void *priv, else osd_device->ops.set_interpolation_filter(osd_device, 0); + sel->r = rect; return 0; } -static int vpbe_display_g_crop(struct file *file, void *priv, - struct v4l2_crop *crop) +static int vpbe_display_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpbe_layer *layer = video_drvdata(file); struct osd_layer_config *cfg = &layer->layer_info.config; struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; struct osd_state *osd_device = layer->disp_dev->osd_device; - struct v4l2_rect *rect = &crop->c; + struct v4l2_rect *rect = &sel->r; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, - "VIDIOC_G_CROP, layer id = %d\n", + "VIDIOC_G_SELECTION, layer id = %d\n", layer->device_id); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + rect->top = cfg->ypos; + rect->left = cfg->xpos; + rect->width = cfg->xsize; + rect->height = cfg->ysize; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rect->left = 0; + rect->top = 0; + rect->width = vpbe_dev->current_timings.xres; + rect->height = vpbe_dev->current_timings.yres; + break; + default: return -EINVAL; } - osd_device->ops.get_layer_config(osd_device, - layer->layer_info.id, cfg); - rect->top = cfg->ypos; - rect->left = cfg->xpos; - rect->width = cfg->xsize; - rect->height = cfg->ysize; return 0; } @@ -752,13 +764,10 @@ static int vpbe_display_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); - cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - cropcap->bounds.left = 0; - cropcap->bounds.top = 0; - cropcap->bounds.width = vpbe_dev->current_timings.xres; - cropcap->bounds.height = vpbe_dev->current_timings.yres; + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + cropcap->pixelaspect = vpbe_dev->current_timings.aspect; - cropcap->defrect = cropcap->bounds; return 0; } @@ -1251,8 +1260,8 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_cropcap = vpbe_display_cropcap, - .vidioc_g_crop = vpbe_display_g_crop, - .vidioc_s_crop = vpbe_display_s_crop, + .vidioc_g_selection = vpbe_display_g_selection, + .vidioc_s_selection = vpbe_display_s_selection, .vidioc_s_std = vpbe_display_s_std, .vidioc_g_std = vpbe_display_g_std, diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 7767e072d623..6efb2f1631c4 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1610,38 +1610,53 @@ static int vpfe_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); - if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + /* If std_index is invalid, then just return (== 1:1 aspect) */ + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + return 0; - memset(crop, 0, sizeof(struct v4l2_cropcap)); - crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop->bounds.width = crop->defrect.width = - vpfe_standards[vpfe_dev->std_index].width; - crop->bounds.height = crop->defrect.height = - vpfe_standards[vpfe_dev->std_index].height; crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; return 0; } -static int vpfe_g_crop(struct file *file, void *priv, - struct v4l2_crop *crop) +static int vpfe_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpfe_device *vpfe_dev = video_drvdata(file); - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_selection\n"); - crop->c = vpfe_dev->crop; + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vpfe_dev->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.width = vpfe_standards[vpfe_dev->std_index].width; + sel->r.height = vpfe_standards[vpfe_dev->std_index].height; + break; + default: + return -EINVAL; + } return 0; } -static int vpfe_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop) +static int vpfe_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpfe_device *vpfe_dev = video_drvdata(file); - struct v4l2_rect rect = crop->c; + struct v4l2_rect rect = sel->r; int ret = 0; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; if (vpfe_dev->started) { /* make sure streaming is not started */ @@ -1669,7 +1684,7 @@ static int vpfe_s_crop(struct file *file, void *priv, vpfe_dev->std_info.active_pixels) || (rect.top + rect.height > vpfe_dev->std_info.active_lines)) { - v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_SELECTION params\n"); ret = -EINVAL; goto unlock_out; } @@ -1682,6 +1697,7 @@ static int vpfe_s_crop(struct file *file, void *priv, vpfe_dev->fmt.fmt.pix.bytesperline * vpfe_dev->fmt.fmt.pix.height; vpfe_dev->crop = rect; + sel->r = rect; unlock_out: mutex_unlock(&vpfe_dev->lock); return ret; @@ -1760,8 +1776,8 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_streamon = vpfe_streamon, .vidioc_streamoff = vpfe_streamoff, .vidioc_cropcap = vpfe_cropcap, - .vidioc_g_crop = vpfe_g_crop, - .vidioc_s_crop = vpfe_s_crop, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, .vidioc_default = vpfe_param_handler, }; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index ec6494cbdd45..9f03b791b711 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -261,7 +261,7 @@ static void gsc_m2m_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops gsc_m2m_qops = { +static const struct vb2_ops gsc_m2m_qops = { .queue_setup = gsc_m2m_queue_setup, .buf_prepare = gsc_m2m_buf_prepare, .buf_queue = gsc_m2m_buf_queue, @@ -277,9 +277,10 @@ static int gsc_m2m_querycap(struct file *file, void *fh, struct gsc_ctx *ctx = fh_to_ctx(fh); struct gsc_dev *gsc = ctx->gsc_dev; - strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver)); - strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card)); - strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&gsc->pdev->dev)); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index fdec499fbbda..964f4a681934 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -452,7 +452,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); } -static struct vb2_ops fimc_capture_qops = { +static const struct vb2_ops fimc_capture_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -1796,6 +1796,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vid_cap->wb_fmt.code = fmt->mbus_code; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; + vfd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad); if (ret) goto err_free_ctx; diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c index 7521aa59b064..6bba4ca022be 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -55,26 +55,33 @@ static int fimc_is_i2c_probe(struct platform_device *pdev) i2c_adap->algo = &fimc_is_i2c_algorithm; i2c_adap->class = I2C_CLASS_SPD; - ret = i2c_add_adapter(i2c_adap); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add I2C bus %s\n", - node->full_name); - return ret; - } - platform_set_drvdata(pdev, isp_i2c); - pm_runtime_enable(&pdev->dev); - pm_runtime_enable(&i2c_adap->dev); + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) + goto err_pm_dis; + /* + * Client drivers of this adapter don't do any I2C transfers as that + * is handled by the ISP firmware. But we rely on the runtime PM + * state propagation from the clients up to the adapter driver so + * clear the ignore_children flags here. PM rutnime calls are not + * used in probe() handler of clients of this adapter so there is + * no issues with clearing the flag right after registering the I2C + * adapter. + */ + pm_suspend_ignore_children(&i2c_adap->dev, false); return 0; + +err_pm_dis: + pm_runtime_disable(&pdev->dev); + return ret; } static int fimc_is_i2c_remove(struct platform_device *pdev) { struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev); - pm_runtime_disable(&isp_i2c->adapter.dev); pm_runtime_disable(&pdev->dev); i2c_del_adapter(&isp_i2c->adapter); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 32ca55f16677..518ad34f80d7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -52,6 +52,9 @@ static char *fimc_is_clocks[ISS_CLKS_MAX] = { [ISS_CLK_DRC] = "drc", [ISS_CLK_FD] = "fd", [ISS_CLK_MCUISP] = "mcuisp", + [ISS_CLK_GICISP] = "gicisp", + [ISS_CLK_PWM_ISP] = "pwm_isp", + [ISS_CLK_MCUCTL_ISP] = "mcuctl_isp", [ISS_CLK_UART] = "uart", [ISS_CLK_ISP_DIV0] = "ispdiv0", [ISS_CLK_ISP_DIV1] = "ispdiv1", @@ -165,6 +168,7 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, struct device_node *node) { struct fimc_is_sensor *sensor = &is->sensor[index]; + struct device_node *ep, *port; u32 tmp = 0; int ret; @@ -175,22 +179,25 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, return -EINVAL; } - node = of_graph_get_next_endpoint(node, NULL); - if (!node) + ep = of_graph_get_next_endpoint(node, NULL); + if (!ep) return -ENXIO; - node = of_graph_get_remote_port(node); - if (!node) + port = of_graph_get_remote_port(ep); + of_node_put(ep); + if (!port) return -ENXIO; /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ - ret = of_property_read_u32(node, "reg", &tmp); + ret = of_property_read_u32(port, "reg", &tmp); if (ret < 0) { dev_err(&is->pdev->dev, "reg property not found at: %s\n", - node->full_name); + port->full_name); + of_node_put(port); return ret; } + of_node_put(port); sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; return 0; } @@ -845,13 +852,18 @@ static int fimc_is_probe(struct platform_device *pdev) goto err_pm; vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret < 0) + goto err_pm; + /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes * will be created within the subdev's registered() callback. */ ret = fimc_is_register_subdevs(is); if (ret < 0) - goto err_pm; + goto err_of_dep; ret = fimc_is_debugfs_create(is); if (ret < 0) @@ -870,6 +882,8 @@ err_dfs: fimc_is_debugfs_remove(is); err_sd: fimc_is_unregister_subdevs(is); +err_of_dep: + of_platform_depopulate(dev); err_pm: if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); @@ -929,6 +943,7 @@ static int fimc_is_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(dev)) fimc_is_runtime_suspend(dev); free_irq(is->irq, is); + of_platform_depopulate(dev); fimc_is_unregister_subdevs(is); vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 3a82c6a214c7..ee05da034aa1 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -77,6 +77,9 @@ enum { ISS_CLK_DRC, ISS_CLK_FD, ISS_CLK_MCUISP, + ISS_CLK_GICISP, + ISS_CLK_PWM_ISP, + ISS_CLK_MCUCTL_ISP, ISS_CLK_UART, ISS_GATE_CLKS_MAX, ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX, diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 293b807020c4..8efe9160ab34 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -705,6 +705,7 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index a0f149fb88e1..b91abf1c4d43 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1432,6 +1432,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->ctrl_handler = handler; sd->internal_ops = &fimc_lite_subdev_internal_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->entity.ops = &fimc_lite_subdev_media_ops; sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, fimc); @@ -1454,25 +1455,17 @@ static void fimc_lite_clk_put(struct fimc_lite *fimc) if (IS_ERR(fimc->clock)) return; - clk_unprepare(fimc->clock); clk_put(fimc->clock); fimc->clock = ERR_PTR(-EINVAL); } static int fimc_lite_clk_get(struct fimc_lite *fimc) { - int ret; - fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); if (IS_ERR(fimc->clock)) return PTR_ERR(fimc->clock); - ret = clk_prepare(fimc->clock); - if (ret < 0) { - clk_put(fimc->clock); - fimc->clock = ERR_PTR(-EINVAL); - } - return ret; + return 0; } static const struct of_device_id flite_of_match[]; @@ -1543,7 +1536,7 @@ static int fimc_lite_probe(struct platform_device *pdev) pm_runtime_enable(dev); if (!pm_runtime_enabled(dev)) { - ret = clk_enable(fimc->clock); + ret = clk_prepare_enable(fimc->clock); if (ret < 0) goto err_sd; } @@ -1568,7 +1561,7 @@ static int fimc_lite_runtime_resume(struct device *dev) { struct fimc_lite *fimc = dev_get_drvdata(dev); - clk_enable(fimc->clock); + clk_prepare_enable(fimc->clock); return 0; } @@ -1576,7 +1569,7 @@ static int fimc_lite_runtime_suspend(struct device *dev) { struct fimc_lite *fimc = dev_get_drvdata(dev); - clk_disable(fimc->clock); + clk_disable_unprepare(fimc->clock); return 0; } #endif diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index b1309e114edb..6028e4fbaed3 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -219,7 +219,7 @@ static void fimc_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } -static struct vb2_ops fimc_qops = { +static const struct vb2_ops fimc_qops = { .queue_setup = fimc_queue_setup, .buf_prepare = fimc_buf_prepare, .buf_queue = fimc_buf_queue, diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 891625e77ef5..1a1154a9dfa4 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1190,6 +1190,10 @@ static int fimc_md_link_notify(struct media_link *link, unsigned int flags, return ret ? -EPIPE : 0; } +static const struct media_device_ops fimc_md_ops = { + .link_notify = fimc_md_link_notify, +}; + static ssize_t fimc_md_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1416,7 +1420,7 @@ static int fimc_md_probe(struct platform_device *pdev) strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); - fmd->media_dev.link_notify = fimc_md_link_notify; + fmd->media_dev.ops = &fimc_md_ops; fmd->media_dev.dev = dev; v4l2_dev = &fmd->v4l2_dev; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 86e681daa89d..befd9fc0adc4 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -853,6 +853,7 @@ static int s5pcsis_probe(struct platform_device *pdev) state->format.width = S5PCSIS_DEF_PIX_WIDTH; state->format.height = S5PCSIS_DEF_PIX_HEIGHT; + state->sd.entity.function = MEDIA_ENT_F_IO_V4L; state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&state->sd.entity, diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 0fcb5c78031d..bedc7cc4c7d6 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -852,7 +852,7 @@ static void deinterlace_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops deinterlace_qops = { +static const struct vb2_ops deinterlace_qops = { .queue_setup = deinterlace_queue_setup, .buf_prepare = deinterlace_buf_prepare, .buf_queue = deinterlace_buf_queue, @@ -1016,7 +1016,7 @@ static int deinterlace_probe(struct platform_device *pdev) return -ENODEV; if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) { - v4l2_err(&pcdev->v4l2_dev, "DMA does not support INTERLEAVE\n"); + dev_err(&pdev->dev, "DMA does not support INTERLEAVE\n"); goto rel_dma; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 3a8e6958adae..c8eaa41c00e6 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -239,7 +239,7 @@ struct mtk_vcodec_ctx { enum mtk_encode_param param_change; struct mtk_enc_params enc_params; - struct venc_common_if *enc_if; + const struct venc_common_if *enc_if; unsigned long drv_handle; int int_cond; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 2c5719ac23b2..1b1a28abbf1f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -243,6 +243,8 @@ static int vidioc_venc_s_parm(struct file *file, void *priv, a->parm.output.timeperframe.numerator; ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + return 0; } @@ -254,6 +256,7 @@ static int vidioc_venc_g_parm(struct file *file, void *priv, if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) return -EINVAL; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; a->parm.output.timeperframe.denominator = ctx->enc_params.framerate_num; a->parm.output.timeperframe.numerator = @@ -621,6 +624,69 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, return vidioc_try_fmt(f, fmt); } +static int vidioc_venc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->coded_width; + s->r.height = q_data->coded_height; + break; + case V4L2_SEL_TGT_CROP: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_venc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Only support crop from (0,0) */ + s->r.top = 0; + s->r.left = 0; + s->r.width = min(s->r.width, q_data->coded_width); + s->r.height = min(s->r.height, q_data->coded_height); + q_data->visible_width = s->r.width; + q_data->visible_height = s->r.height; + break; + default: + return -EINVAL; + } + return 0; +} + static int vidioc_venc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -679,6 +745,9 @@ const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_g_selection = vidioc_venc_g_selection, + .vidioc_s_selection = vidioc_venc_s_selection, }; static int vb2ops_venc_queue_setup(struct vb2_queue *vq, @@ -854,7 +923,7 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) ctx->state = MTK_STATE_FREE; } -static struct vb2_ops mtk_venc_vb2_ops = { +static const struct vb2_ops mtk_venc_vb2_ops = { .queue_setup = vb2ops_venc_queue_setup, .buf_prepare = vb2ops_venc_buf_prepare, .buf_queue = vb2ops_venc_buf_queue, diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c index 63d4be4ff327..b76c80bdf30b 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -664,16 +664,16 @@ static int h264_enc_deinit(unsigned long handle) return ret; } -static struct venc_common_if venc_h264_if = { +static const struct venc_common_if venc_h264_if = { h264_enc_init, h264_enc_encode, h264_enc_set_param, h264_enc_deinit, }; -struct venc_common_if *get_h264_enc_comm_if(void); +const struct venc_common_if *get_h264_enc_comm_if(void); -struct venc_common_if *get_h264_enc_comm_if(void) +const struct venc_common_if *get_h264_enc_comm_if(void) { return &venc_h264_if; } diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c index 6d9758479f9a..544f57186243 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -469,16 +469,16 @@ static int vp8_enc_deinit(unsigned long handle) return ret; } -static struct venc_common_if venc_vp8_if = { +static const struct venc_common_if venc_vp8_if = { vp8_enc_init, vp8_enc_encode, vp8_enc_set_param, vp8_enc_deinit, }; -struct venc_common_if *get_vp8_enc_comm_if(void); +const struct venc_common_if *get_vp8_enc_comm_if(void); -struct venc_common_if *get_vp8_enc_comm_if(void) +const struct venc_common_if *get_vp8_enc_comm_if(void) { return &venc_vp8_if; } diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c index c4c83e7189c3..d02d5f1df279 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -26,8 +26,8 @@ #include "mtk_vcodec_enc_pm.h" #include "mtk_vpu.h" -struct venc_common_if *get_h264_enc_comm_if(void); -struct venc_common_if *get_vp8_enc_comm_if(void); +const struct venc_common_if *get_h264_enc_comm_if(void); +const struct venc_common_if *get_vp8_enc_comm_if(void); int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index c639406fe72e..e68d271b10af 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -743,7 +743,7 @@ static void emmaprp_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops emmaprp_qops = { +static const struct vb2_ops emmaprp_qops = { .queue_setup = emmaprp_queue_setup, .buf_prepare = emmaprp_buf_prepare, .buf_queue = emmaprp_buf_queue, diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 6b01e126fe73..e668dde6d857 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1247,36 +1247,33 @@ static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, return 0; } -static int vidioc_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cropcap) +static int vidioc_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct omap_vout_device *vout = fh; struct v4l2_pix_format *pix = &vout->pix; - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; - /* Width and height are always even */ - cropcap->bounds.width = pix->width & ~1; - cropcap->bounds.height = pix->height & ~1; - - omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect); - cropcap->pixelaspect.numerator = 1; - cropcap->pixelaspect.denominator = 1; - return 0; -} - -static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) -{ - struct omap_vout_device *vout = fh; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vout->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + omap_vout_default_crop(&vout->pix, &vout->fbuf, &sel->r); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + /* Width and height are always even */ + sel->r.width = pix->width & ~1; + sel->r.height = pix->height & ~1; + break; + default: return -EINVAL; - crop->c = vout->crop; + } return 0; } -static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) +static int vidioc_s_selection(struct file *file, void *fh, struct v4l2_selection *sel) { int ret = -EINVAL; struct omap_vout_device *vout = fh; @@ -1285,6 +1282,12 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr struct omap_video_timings *timing; struct omap_dss_device *dssdev; + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + if (vout->streaming) return -EBUSY; @@ -1309,9 +1312,8 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr vout->fbuf.fmt.width = timing->x_res; } - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, - &vout->fbuf, &crop->c); + ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, + &vout->fbuf, &sel->r); s_crop_err: mutex_unlock(&vout->lock); @@ -1780,9 +1782,8 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 5d54e2c6c16b..0321d84addc7 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -657,6 +657,10 @@ static irqreturn_t isp_isr(int irq, void *_isp) return IRQ_HANDLED; } +static const struct media_device_ops isp_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + /* ----------------------------------------------------------------------------- * Pipeline stream management */ @@ -1680,7 +1684,7 @@ static int isp_register_entities(struct isp_device *isp) strlcpy(isp->media_dev.model, "TI OMAP3 ISP", sizeof(isp->media_dev.model)); isp->media_dev.hw_revision = isp->revision; - isp->media_dev.link_notify = v4l2_pipeline_link_notify; + isp->media_dev.ops = &isp_media_ops; media_device_init(&isp->media_dev); isp->v4l2_dev.mdev = &isp->media_dev; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 7d9f35976d18..7354469670b7 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -772,40 +772,45 @@ isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) } static int -isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) -{ - struct isp_video *video = video_drvdata(file); - struct v4l2_subdev *subdev; - int ret; - - subdev = isp_video_remote_subdev(video, NULL); - if (subdev == NULL) - return -EINVAL; - - mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); - mutex_unlock(&video->mutex); - - return ret == -ENOIOCTLCMD ? -ENOTTY : ret; -} - -static int -isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) +isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); struct v4l2_subdev_format format; struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; u32 pad; int ret; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } subdev = isp_video_remote_subdev(video, &pad); if (subdev == NULL) return -EINVAL; - /* Try the get crop operation first and fallback to get format if not + /* Try the get selection operation first and fallback to get format if not * implemented. */ - ret = v4l2_subdev_call(subdev, video, g_crop, crop); + sdsel.pad = pad; + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; if (ret != -ENOIOCTLCMD) return ret; @@ -815,28 +820,50 @@ isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) if (ret < 0) return ret == -ENOIOCTLCMD ? -ENOTTY : ret; - crop->c.left = 0; - crop->c.top = 0; - crop->c.width = format.format.width; - crop->c.height = format.format.height; + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format.format.width; + sel->r.height = format.format.height; return 0; } static int -isp_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) +isp_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + u32 pad; int ret; - subdev = isp_video_remote_subdev(video, NULL); + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = isp_video_remote_subdev(video, &pad); if (subdev == NULL) return -EINVAL; + sdsel.pad = pad; mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, s_crop, crop); + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); mutex_unlock(&video->mutex); + if (!ret) + sel->r = sdsel.r; return ret == -ENOIOCTLCMD ? -ENOTTY : ret; } @@ -1252,9 +1279,8 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_g_fmt_vid_out = isp_video_get_format, .vidioc_s_fmt_vid_out = isp_video_set_format, .vidioc_try_fmt_vid_out = isp_video_try_format, - .vidioc_cropcap = isp_video_cropcap, - .vidioc_g_crop = isp_video_get_crop, - .vidioc_s_crop = isp_video_set_crop, + .vidioc_g_selection = isp_video_get_selection, + .vidioc_s_selection = isp_video_set_selection, .vidioc_g_parm = isp_video_get_param, .vidioc_s_parm = isp_video_set_param, .vidioc_reqbufs = isp_video_reqbufs, diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 2aaf4a8f71a0..c12209c701d3 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -3,6 +3,7 @@ * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * Copyright (C) 2016, Robert Jarzmik <robert.jarzmik@free.fr> * * 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 @@ -14,6 +15,7 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/errno.h> @@ -22,8 +24,8 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/time.h> -#include <linux/device.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/sched.h> @@ -32,13 +34,15 @@ #include <linux/dma-mapping.h> #include <linux/dma/pxa-dma.h> +#include <media/v4l2-async.h> +#include <media/v4l2-clk.h> #include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf-dma-sg.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-of.h> +#include <media/videobuf2-dma-sg.h> + #include <linux/videodev2.h> #include <linux/platform_data/media/camera-pxa.h> @@ -46,6 +50,9 @@ #define PXA_CAM_VERSION "0.0.6" #define PXA_CAM_DRV_NAME "pxa27x-camera" +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + /* Camera Interface */ #define CICR0 0x0000 #define CICR1 0x0004 @@ -168,6 +175,462 @@ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ CICR0_EOFM | CICR0_FOM) +#define sensor_call(cam, o, f, args...) \ + v4l2_subdev_call(cam->sensor, o, f, ##args) + +/* + * Format handling + */ + +/** + * enum pxa_mbus_packing - data packing types on the media-bus + * @PXA_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one + * sample represents one pixel + * @PXA_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the + * possibly incomplete byte high bits are padding + * @PXA_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended + * to 16 bits + */ +enum pxa_mbus_packing { + PXA_MBUS_PACKING_NONE, + PXA_MBUS_PACKING_2X8_PADHI, + PXA_MBUS_PACKING_EXTEND16, +}; + +/** + * enum pxa_mbus_order - sample order on the media bus + * @PXA_MBUS_ORDER_LE: least significant sample first + * @PXA_MBUS_ORDER_BE: most significant sample first + */ +enum pxa_mbus_order { + PXA_MBUS_ORDER_LE, + PXA_MBUS_ORDER_BE, +}; + +/** + * enum pxa_mbus_layout - planes layout in memory + * @PXA_MBUS_LAYOUT_PACKED: color components packed + * @PXA_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) + * @PXA_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a + * chroma plane (C plane is half the size + * of Y plane) + * @PXA_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a + * chroma plane (C plane is the same size + * as Y plane) + */ +enum pxa_mbus_layout { + PXA_MBUS_LAYOUT_PACKED = 0, + PXA_MBUS_LAYOUT_PLANAR_2Y_U_V, + PXA_MBUS_LAYOUT_PLANAR_2Y_C, + PXA_MBUS_LAYOUT_PLANAR_Y_C, +}; + +/** + * struct pxa_mbus_pixelfmt - Data format on the media bus + * @name: Name of the format + * @fourcc: Fourcc code, that will be obtained if the data is + * stored in memory in the following way: + * @packing: Type of sample-packing, that has to be used + * @order: Sample order when storing in memory + * @bits_per_sample: How many bits the bridge has to sample + */ +struct pxa_mbus_pixelfmt { + const char *name; + u32 fourcc; + enum pxa_mbus_packing packing; + enum pxa_mbus_order order; + enum pxa_mbus_layout layout; + u8 bits_per_sample; +}; + +/** + * struct pxa_mbus_lookup - Lookup FOURCC IDs by mediabus codes for pass-through + * @code: mediabus pixel-code + * @fmt: pixel format description + */ +struct pxa_mbus_lookup { + u32 code; + struct pxa_mbus_pixelfmt fmt; +}; + +static const struct pxa_mbus_lookup mbus_fmt[] = { +{ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555, + .name = "RGB555", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "RGB555X", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565X, + .name = "RGB565X", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .name = "Bayer 8 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_GREY, + .name = "Grey", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_Y10, + .name = "Grey 10bit", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB444, + .name = "RGB444", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, + .name = "Bayer 10 BGGR DPCM 8", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .name = "Bayer 10 GBRG", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .name = "Bayer 10 GRBG", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .name = "Bayer 10 RGGB", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .name = "Bayer 12 BGGR", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .name = "Bayer 12 GBRG", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .name = "Bayer 12 GRBG", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .name = "Bayer 12 RGGB", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, +}; + +static s32 pxa_mbus_bytes_per_line(u32 width, const struct pxa_mbus_pixelfmt *mf) +{ + if (mf->layout != PXA_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + + switch (mf->packing) { + case PXA_MBUS_PACKING_NONE: + return width * mf->bits_per_sample / 8; + case PXA_MBUS_PACKING_2X8_PADHI: + case PXA_MBUS_PACKING_EXTEND16: + return width * 2; + } + return -EINVAL; +} + +static s32 pxa_mbus_image_size(const struct pxa_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + switch (mf->packing) { + case PXA_MBUS_PACKING_2X8_PADHI: + return bytes_per_line * height * 2; + default: + return -EINVAL; + } +} + +static const struct pxa_mbus_pixelfmt *pxa_mbus_find_fmtdesc( + u32 code, + const struct pxa_mbus_lookup *lookup, + int n) +{ + int i; + + for (i = 0; i < n; i++) + if (lookup[i].code == code) + return &lookup[i].fmt; + + return NULL; +} + +static const struct pxa_mbus_pixelfmt *pxa_mbus_get_fmtdesc( + u32 code) +{ + return pxa_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); +} + +static unsigned int pxa_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags) +{ + unsigned long common_flags; + bool hsync = true, vsync = true, pclk, data, mode; + bool mipi_lanes, mipi_clock; + + common_flags = cfg->flags & flags; + + switch (cfg->type) { + case V4L2_MBUS_PARALLEL: + hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW); + vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW); + /* fall through */ + case V4L2_MBUS_BT656: + pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING); + data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); + mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); + return (!hsync || !vsync || !pclk || !data || !mode) ? + 0 : common_flags; + case V4L2_MBUS_CSI2: + mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; + mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); + return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; + } + return 0; +} + +/** + * struct soc_camera_format_xlate - match between host and sensor formats + * @code: code of a sensor provided format + * @host_fmt: host format after host translation from code + * + * Host and sensor translation structure. Used in table of host and sensor + * formats matchings in soc_camera_device. A host can override the generic list + * generation by implementing get_formats(), and use it for format checks and + * format setup. + */ +struct soc_camera_format_xlate { + u32 code; + const struct pxa_mbus_pixelfmt *host_fmt; +}; + /* * Structures */ @@ -180,19 +643,33 @@ enum pxa_camera_active_dma { /* buffer for one video frame */ struct pxa_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_v4l2_buffer vbuf; + struct list_head queue; u32 code; + int nb_planes; /* our descriptor lists for Y, U and V channels */ struct dma_async_tx_descriptor *descs[3]; dma_cookie_t cookie[3]; struct scatterlist *sg[3]; int sg_len[3]; + size_t plane_sizes[3]; int inwork; enum pxa_camera_active_dma active_dma; }; struct pxa_camera_dev { - struct soc_camera_host soc_host; + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_async_notifier notifier; + struct vb2_queue vb2_vq; + struct v4l2_subdev *sensor; + struct soc_camera_format_xlate *user_formats; + const struct soc_camera_format_xlate *current_fmt; + struct v4l2_pix_format current_pix; + + struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asds[1]; + /* * PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than @@ -212,11 +689,14 @@ struct pxa_camera_dev { unsigned long ciclk; unsigned long mclk; u32 mclk_divisor; + struct v4l2_clk *mclk_clk; u16 width_flags; /* max 10 bits */ struct list_head capture; spinlock_t lock; + struct mutex mlock; + unsigned int buf_sequence; struct pxa_buffer *active; struct tasklet_struct task_eof; @@ -230,59 +710,90 @@ struct pxa_cam { static const char *pxa_cam_driver_description = "PXA_Camera"; -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ - /* - * Videobuf operations + * Format translation functions */ -static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static const struct soc_camera_format_xlate +*pxa_mbus_xlate_by_fourcc(struct soc_camera_format_xlate *user_formats, + unsigned int fourcc) { - struct soc_camera_device *icd = vq->priv_data; + unsigned int i; - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); - - *size = icd->sizeimage; - - if (0 == *count) - *count = 32; - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; - - return 0; + for (i = 0; user_formats[i].code; i++) + if (user_formats[i].host_fmt->fourcc == fourcc) + return user_formats + i; + return NULL; } -static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) +static struct soc_camera_format_xlate *pxa_mbus_build_fmts_xlate( + struct v4l2_device *v4l2_dev, struct v4l2_subdev *subdev, + int (*get_formats)(struct v4l2_device *, unsigned int, + struct soc_camera_format_xlate *xlate)) { - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - int i; - - BUG_ON(in_interrupt()); + unsigned int i, fmts = 0, raw_fmts = 0; + int ret; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct soc_camera_format_xlate *user_formats; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - &buf->vb, buf->vb.baddr, buf->vb.bsize); + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { + raw_fmts++; + code.index++; + } /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in STATE_QUEUED or STATE_ACTIVE + * First pass - only count formats this host-sensor + * configuration can provide */ - videobuf_waiton(vq, &buf->vb, 0, 0); + for (i = 0; i < raw_fmts; i++) { + ret = get_formats(v4l2_dev, i, NULL); + if (ret < 0) + return ERR_PTR(ret); + fmts += ret; + } - for (i = 0; i < 3 && buf->descs[i]; i++) { - dmaengine_desc_free(buf->descs[i]); - kfree(buf->sg[i]); - buf->descs[i] = NULL; - buf->sg[i] = NULL; - buf->sg_len[i] = 0; + if (!fmts) + return ERR_PTR(-ENXIO); + + user_formats = kcalloc(fmts + 1, sizeof(*user_formats), GFP_KERNEL); + if (!user_formats) + return ERR_PTR(-ENOMEM); + + /* Second pass - actually fill data formats */ + fmts = 0; + for (i = 0; i < raw_fmts; i++) { + ret = get_formats(v4l2_dev, i, user_formats + fmts); + if (ret < 0) + goto egfmt; + fmts += ret; } - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); + user_formats[fmts].code = 0; - buf->vb.state = VIDEOBUF_NEEDS_INIT; + return user_formats; +egfmt: + kfree(user_formats); + return ERR_PTR(ret); +} - dev_dbg(icd->parent, "%s end (vb=0x%p) 0x%08lx %d\n", __func__, - &buf->vb, buf->vb.baddr, buf->vb.bsize); +/* + * Videobuf operations + */ +static struct pxa_buffer *vb2_to_pxa_buffer(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + return container_of(vbuf, struct pxa_buffer, vbuf); +} + +static struct device *pcdev_to_dev(struct pxa_camera_dev *pcdev) +{ + return pcdev->v4l2_dev.dev; +} + +static struct pxa_camera_dev *v4l2_dev_to_pcdev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct pxa_camera_dev, v4l2_dev); } static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, @@ -312,31 +823,26 @@ static void pxa_camera_dma_irq_v(void *data) /** * pxa_init_dma_channel - init dma descriptors * @pcdev: pxa camera device - * @buf: pxa buffer to find pxa dma channel + * @vb: videobuffer2 buffer * @dma: dma video buffer * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') * @cibr: camera Receive Buffer Register - * @size: bytes to transfer - * @offset: offset in videobuffer of the first byte to transfer * * Prepares the pxa dma descriptors to transfer one camera channel. * * Returns 0 if success or -ENOMEM if no memory is available */ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, - struct pxa_buffer *buf, - struct videobuf_dmabuf *dma, int channel, - int cibr, int size, int offset) + struct pxa_buffer *buf, int channel, + struct scatterlist *sg, int sglen) { struct dma_chan *dma_chan = pcdev->dma_chans[channel]; - struct scatterlist *sg = buf->sg[channel]; - int sglen = buf->sg_len[channel]; struct dma_async_tx_descriptor *tx; tx = dmaengine_prep_slave_sg(dma_chan, sg, sglen, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_REUSE); if (!tx) { - dev_err(pcdev->soc_host.v4l2_dev.dev, + dev_err(pcdev_to_dev(pcdev), "dmaengine_prep_slave_sg failed\n"); goto fail; } @@ -357,11 +863,9 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, buf->descs[channel] = tx; return 0; fail: - kfree(sg); - - dev_dbg(pcdev->soc_host.v4l2_dev.dev, - "%s (vb=0x%p) dma_tx=%p\n", - __func__, &buf->vb, tx); + dev_dbg(pcdev_to_dev(pcdev), + "%s (vb=%p) dma_tx=%p\n", + __func__, buf, tx); return -ENOMEM; } @@ -370,133 +874,10 @@ static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf) { buf->active_dma = DMA_Y; - if (pcdev->channels == 3) + if (buf->nb_planes == 3) buf->active_dma |= DMA_U | DMA_V; } -/* - * Please check the DMA prepared buffer structure in : - * Documentation/video4linux/pxa_camera.txt - * Please check also in pxa_camera_check_link_miss() to understand why DMA chain - * modification while DMA chain is running will work anyway. - */ -static int pxa_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct device *dev = pcdev->soc_host.v4l2_dev.dev; - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - int ret; - int size_y, size_u = 0, size_v = 0; - size_t sizes[3]; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* Added list head initialization on alloc */ - WARN_ON(!list_empty(&vb->queue)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - memset((void *)vb->baddr, 0xaa, vb->bsize); -#endif - - BUG_ON(NULL == icd->current_fmt); - - /* - * I think, in buf_prepare you only have to protect global data, - * the actual buffer is yours - */ - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = icd->sizeimage; - if (0 != vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; - } - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int size = vb->size; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto out; - - if (pcdev->channels == 3) { - size_y = size / 2; - size_u = size_v = size / 4; - } else { - size_y = size; - } - - sizes[0] = size_y; - sizes[1] = size_u; - sizes[2] = size_v; - ret = sg_split(dma->sglist, dma->sglen, 0, pcdev->channels, - sizes, buf->sg, buf->sg_len, GFP_KERNEL); - if (ret < 0) { - dev_err(dev, "sg_split failed: %d\n", ret); - goto fail; - } - - /* init DMA for Y channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, - size_y, 0); - if (ret) { - dev_err(dev, "DMA initialization for Y/RGB failed\n"); - goto fail; - } - - /* init DMA for U channel */ - if (size_u) - ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, - size_u, size_y); - if (ret) { - dev_err(dev, "DMA initialization for U failed\n"); - goto fail; - } - - /* init DMA for V channel */ - if (size_v) - ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, - size_v, size_y + size_u); - if (ret) { - dev_err(dev, "DMA initialization for V failed\n"); - goto fail; - } - - vb->state = VIDEOBUF_PREPARED; - } - - buf->inwork = 0; - pxa_videobuf_set_actdma(pcdev, buf); - - return 0; - -fail: - free_buffer(vq, buf); -out: - buf->inwork = 0; - return ret; -} - /** * pxa_dma_start_channels - start DMA channel for active buffer * @pcdev: pxa camera device @@ -507,12 +888,9 @@ out: static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) { int i; - struct pxa_buffer *active; - - active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d)\n", __func__, i); dma_async_issue_pending(pcdev->dma_chans[i]); } @@ -523,7 +901,7 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d)\n", __func__, i); dmaengine_terminate_all(pcdev->dma_chans[i]); } @@ -536,7 +914,7 @@ static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev, for (i = 0; i < pcdev->channels; i++) { buf->cookie[i] = dmaengine_submit(buf->descs[i]); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d) : submit vb=%p cookie=%d\n", __func__, i, buf, buf->descs[i]->cookie); } @@ -554,7 +932,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) { unsigned long cicr0; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); + dev_dbg(pcdev_to_dev(pcdev), "%s\n", __func__); __raw_writel(__raw_readl(pcdev->base + CISR), pcdev->base + CISR); /* Enable End-Of-Frame Interrupt */ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; @@ -572,72 +950,24 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) __raw_writel(cicr0, pcdev->base + CICR0); pcdev->active = NULL; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); -} - -/* Called under spinlock_irqsave(&pcdev->lock, ...) */ -static void pxa_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", - __func__, vb, vb->baddr, vb->bsize, pcdev->active); - - list_add_tail(&vb->queue, &pcdev->capture); - - vb->state = VIDEOBUF_ACTIVE; - pxa_dma_add_tail_buf(pcdev, buf); - - if (!pcdev->active) - pxa_camera_start_capture(pcdev); -} - -static void pxa_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); -#ifdef DEBUG - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - switch (vb->state) { - case VIDEOBUF_ACTIVE: - dev_dbg(dev, "%s (active)\n", __func__); - break; - case VIDEOBUF_QUEUED: - dev_dbg(dev, "%s (queued)\n", __func__); - break; - case VIDEOBUF_PREPARED: - dev_dbg(dev, "%s (prepared)\n", __func__); - break; - default: - dev_dbg(dev, "%s (unknown)\n", __func__); - break; - } -#endif - - free_buffer(vq, buf); + dev_dbg(pcdev_to_dev(pcdev), "%s\n", __func__); } static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, - struct videobuf_buffer *vb, - struct pxa_buffer *buf) + struct pxa_buffer *buf, + enum vb2_buffer_state state) { + struct vb2_buffer *vb = &buf->vbuf.vb2_buf; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - v4l2_get_timestamp(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", - __func__, vb); + list_del_init(&buf->queue); + vb->timestamp = ktime_get_ns(); + vbuf->sequence = pcdev->buf_sequence++; + vbuf->field = V4L2_FIELD_NONE; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + dev_dbg(pcdev_to_dev(pcdev), "%s dequeued buffer (buf=0x%p)\n", + __func__, buf); if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); @@ -645,7 +975,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, } pcdev->active = list_entry(pcdev->capture.next, - struct pxa_buffer, vb.queue); + struct pxa_buffer, queue); } /** @@ -670,7 +1000,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev, { bool is_dma_stopped = last_submitted != last_issued; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s : top queued buffer=%p, is_dma_stopped=%d\n", __func__, pcdev->active, is_dma_stopped); @@ -681,19 +1011,17 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev, static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { - struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf, *last_buf; unsigned long flags; u32 camera_status, overrun; int chan; - struct videobuf_buffer *vb; enum dma_status last_status; dma_cookie_t last_issued; spin_lock_irqsave(&pcdev->lock, flags); camera_status = __raw_readl(pcdev->base + CISR); - dev_dbg(dev, "camera dma irq, cisr=0x%x dma=%d\n", + dev_dbg(pcdev_to_dev(pcdev), "camera dma irq, cisr=0x%x dma=%d\n", camera_status, act_dma); overrun = CISR_IFO_0; if (pcdev->channels == 3) @@ -714,9 +1042,8 @@ static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, if (!pcdev->active) goto out; - vb = &pcdev->active->vb; - buf = container_of(vb, struct pxa_buffer, vb); - WARN_ON(buf->inwork || list_empty(&vb->queue)); + buf = pcdev->active; + WARN_ON(buf->inwork || list_empty(&buf->queue)); /* * It's normal if the last frame creates an overrun, as there @@ -734,23 +1061,23 @@ static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, break; } last_buf = list_entry(pcdev->capture.prev, - struct pxa_buffer, vb.queue); + struct pxa_buffer, queue); last_status = dma_async_is_tx_complete(pcdev->dma_chans[chan], last_buf->cookie[chan], NULL, &last_issued); if (camera_status & overrun && last_status != DMA_COMPLETE) { - dev_dbg(dev, "FIFO overrun! CISR: %x\n", + dev_dbg(pcdev_to_dev(pcdev), "FIFO overrun! CISR: %x\n", camera_status); pxa_camera_stop_capture(pcdev); - list_for_each_entry(buf, &pcdev->capture, vb.queue) + list_for_each_entry(buf, &pcdev->capture, queue) pxa_dma_add_tail_buf(pcdev, buf); pxa_camera_start_capture(pcdev); goto out; } buf->active_dma &= ~act_dma; if (!buf->active_dma) { - pxa_camera_wakeup(pcdev, vb, buf); + pxa_camera_wakeup(pcdev, buf, VB2_BUF_STATE_DONE); pxa_camera_check_link_miss(pcdev, last_buf->cookie[chan], last_issued); } @@ -759,33 +1086,10 @@ out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static struct videobuf_queue_ops pxa_videobuf_ops = { - .buf_setup = pxa_videobuf_setup, - .buf_prepare = pxa_videobuf_prepare, - .buf_queue = pxa_videobuf_queue, - .buf_release = pxa_videobuf_release, -}; - -static void pxa_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - - /* - * We must pass NULL as dev pointer, then all pci_* dma operations - * transform to normal dma_* ones. - */ - videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &ici->host_lock); -} - static u32 mclk_get_divisor(struct platform_device *pdev, struct pxa_camera_dev *pcdev) { unsigned long mclk = pcdev->mclk; - struct device *dev = &pdev->dev; u32 div; unsigned long lcdclk; @@ -795,7 +1099,8 @@ static u32 mclk_get_divisor(struct platform_device *pdev, /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(dev, "Limiting master clock to %lu\n", mclk); + dev_warn(pcdev_to_dev(pcdev), + "Limiting master clock to %lu\n", mclk); } /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ @@ -805,7 +1110,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev, if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n", + dev_dbg(pcdev_to_dev(pcdev), "LCD clock %luHz, target freq %luHz, divisor %u\n", lcdclk, mclk, div); return div; @@ -860,9 +1165,8 @@ static void pxa_camera_eof(unsigned long arg) struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg; unsigned long cifr; struct pxa_buffer *buf; - struct videobuf_buffer *vb; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "Camera interrupt status 0x%x\n", __raw_readl(pcdev->base + CISR)); @@ -871,9 +1175,8 @@ static void pxa_camera_eof(unsigned long arg) __raw_writel(cifr, pcdev->base + CIFR); pcdev->active = list_first_entry(&pcdev->capture, - struct pxa_buffer, vb.queue); - vb = &pcdev->active->vb; - buf = container_of(vb, struct pxa_buffer, vb); + struct pxa_buffer, queue); + buf = pcdev->active; pxa_videobuf_set_actdma(pcdev, buf); pxa_dma_start_channels(pcdev); @@ -885,7 +1188,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) unsigned long status, cicr0; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "Camera interrupt status 0x%lx\n", status); if (!status) @@ -902,47 +1205,6 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) return IRQ_HANDLED; } -static int pxa_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "PXA Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void pxa_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "PXA Camera driver detached from camera %d\n", - icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on PXA quick capture interface - * Called with .host_lock held - */ -static int pxa_camera_clock_start(struct soc_camera_host *ici) -{ - struct pxa_camera_dev *pcdev = ici->priv; - - pxa_camera_activate(pcdev); - - return 0; -} - -/* Called with .host_lock held */ -static void pxa_camera_clock_stop(struct soc_camera_host *ici) -{ - struct pxa_camera_dev *pcdev = ici->priv; - - /* disable capture, disable interrupts */ - __raw_writel(0x3ff, pcdev->base + CICR0); - - /* Stop DMA engine */ - pxa_dma_stop_channels(pcdev); - pxa_camera_deactivate(pcdev); -} - static int test_platform_param(struct pxa_camera_dev *pcdev, unsigned char buswidth, unsigned long *flags) { @@ -968,15 +1230,12 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, return -EINVAL; } -static void pxa_camera_setup_cicr(struct soc_camera_device *icd, +static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev, unsigned long flags, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); unsigned long dw, bpp; u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0, y_skip_top; - int ret = v4l2_subdev_call(sd, sensor, g_skip_top_lines, &y_skip_top); + int ret = sensor_call(pcdev, sensor, g_skip_top_lines, &y_skip_top); if (ret < 0) y_skip_top = 0; @@ -985,7 +1244,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, * Datawidth is now guaranteed to be equal to one of the three values. * We fix bit-per-pixel equal to data-width... */ - switch (icd->current_fmt->host_fmt->bits_per_sample) { + switch (pcdev->current_fmt->host_fmt->bits_per_sample) { case 10: dw = 4; bpp = 0x40; @@ -1019,7 +1278,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, if (cicr0 & CICR0_ENB) __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0); - cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw; + cicr1 = CICR1_PPL_VAL(pcdev->current_pix.width - 1) | bpp | dw; switch (pixfmt) { case V4L2_PIX_FMT_YUV422P: @@ -1048,7 +1307,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, } cicr2 = 0; - cicr3 = CICR3_LPF_VAL(icd->user_height - 1) | + cicr3 = CICR3_LPF_VAL(pcdev->current_pix.height - 1) | CICR3_BFW_VAL(min((u32)255, y_skip_top)); cicr4 |= pcdev->mclk_divisor; @@ -1064,28 +1323,271 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, __raw_writel(cicr0, pcdev->base + CICR0); } -static int pxa_camera_set_bus_param(struct soc_camera_device *icd) +/* + * Videobuf2 section + */ +static void pxa_buffer_cleanup(struct pxa_buffer *buf) +{ + int i; + + for (i = 0; i < 3 && buf->descs[i]; i++) { + dmaengine_desc_free(buf->descs[i]); + kfree(buf->sg[i]); + buf->descs[i] = NULL; + buf->sg[i] = NULL; + buf->sg_len[i] = 0; + buf->plane_sizes[i] = 0; + } + buf->nb_planes = 0; +} + +static int pxa_buffer_init(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf) +{ + struct vb2_buffer *vb = &buf->vbuf.vb2_buf; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + int nb_channels = pcdev->channels; + int i, ret = 0; + unsigned long size = vb2_plane_size(vb, 0); + + switch (nb_channels) { + case 1: + buf->plane_sizes[0] = size; + break; + case 3: + buf->plane_sizes[0] = size / 2; + buf->plane_sizes[1] = size / 4; + buf->plane_sizes[2] = size / 4; + break; + default: + return -EINVAL; + }; + buf->nb_planes = nb_channels; + + ret = sg_split(sgt->sgl, sgt->nents, 0, nb_channels, + buf->plane_sizes, buf->sg, buf->sg_len, GFP_KERNEL); + if (ret < 0) { + dev_err(pcdev_to_dev(pcdev), + "sg_split failed: %d\n", ret); + return ret; + } + for (i = 0; i < nb_channels; i++) { + ret = pxa_init_dma_channel(pcdev, buf, i, + buf->sg[i], buf->sg_len[i]); + if (ret) { + pxa_buffer_cleanup(buf); + return ret; + } + } + INIT_LIST_HEAD(&buf->queue); + + return ret; +} + +static void pxac_vb2_cleanup(struct vb2_buffer *vb) +{ + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vb=%p)\n", __func__, vb); + pxa_buffer_cleanup(buf); +} + +static void pxac_vb2_queue(struct vb2_buffer *vb) +{ + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vb=%p) nb_channels=%d size=%lu active=%p\n", + __func__, vb, pcdev->channels, vb2_get_plane_payload(vb, 0), + pcdev->active); + + list_add_tail(&buf->queue, &pcdev->capture); + + pxa_dma_add_tail_buf(pcdev, buf); +} + +/* + * Please check the DMA prepared buffer structure in : + * Documentation/video4linux/pxa_camera.txt + * Please check also in pxa_camera_check_link_miss() to understand why DMA chain + * modification while DMA chain is running will work anyway. + */ +static int pxac_vb2_prepare(struct vb2_buffer *vb) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + int ret = 0; + + switch (pcdev->channels) { + case 1: + case 3: + vb2_set_plane_payload(vb, 0, pcdev->current_pix.sizeimage); + break; + default: + return -EINVAL; + } + + dev_dbg(pcdev_to_dev(pcdev), + "%s (vb=%p) nb_channels=%d size=%lu\n", + __func__, vb, pcdev->channels, vb2_get_plane_payload(vb, 0)); + + WARN_ON(!pcdev->current_fmt); + +#ifdef DEBUG + /* + * This can be useful if you want to see if we actually fill + * the buffer with something + */ + for (i = 0; i < vb->num_planes; i++) + memset((void *)vb2_plane_vaddr(vb, i), + 0xaa, vb2_get_plane_payload(vb, i)); +#endif + + /* + * I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours + */ + buf->inwork = 0; + pxa_videobuf_set_actdma(pcdev, buf); + + return ret; +} + +static int pxac_vb2_init(struct vb2_buffer *vb) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(nb_channels=%d)\n", + __func__, pcdev->channels); + + return pxa_buffer_init(pcdev, buf); +} + +static int pxac_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *nbufs, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + int size = pcdev->current_pix.sizeimage; + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vq=%p nbufs=%d num_planes=%d size=%d)\n", + __func__, vq, *nbufs, *num_planes, size); + /* + * Called from VIDIOC_REQBUFS or in compatibility mode For YUV422P + * format, even if there are 3 planes Y, U and V, we reply there is only + * one plane, containing Y, U and V data, one after the other. + */ + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + + *num_planes = 1; + switch (pcdev->channels) { + case 1: + case 3: + sizes[0] = size; + break; + default: + return -EINVAL; + } + + if (!*nbufs) + *nbufs = 1; + + return 0; +} + +static int pxac_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + + dev_dbg(pcdev_to_dev(pcdev), "%s(count=%d) active=%p\n", + __func__, count, pcdev->active); + + pcdev->buf_sequence = 0; + if (!pcdev->active) + pxa_camera_start_capture(pcdev); + + return 0; +} + +static void pxac_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + struct pxa_buffer *buf, *tmp; + + dev_dbg(pcdev_to_dev(pcdev), "%s active=%p\n", + __func__, pcdev->active); + pxa_camera_stop_capture(pcdev); + + list_for_each_entry_safe(buf, tmp, &pcdev->capture, queue) + pxa_camera_wakeup(pcdev, buf, VB2_BUF_STATE_ERROR); +} + +static struct vb2_ops pxac_vb2_ops = { + .queue_setup = pxac_vb2_queue_setup, + .buf_init = pxac_vb2_init, + .buf_prepare = pxac_vb2_prepare, + .buf_queue = pxac_vb2_queue, + .buf_cleanup = pxac_vb2_cleanup, + .start_streaming = pxac_vb2_start_streaming, + .stop_streaming = pxac_vb2_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int pxa_camera_init_videobuf2(struct pxa_camera_dev *pcdev) +{ + int ret; + struct vb2_queue *vq = &pcdev->vb2_vq; + + memset(vq, 0, sizeof(*vq)); + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vq->drv_priv = pcdev; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->buf_struct_size = sizeof(struct pxa_buffer); + vq->dev = pcdev->v4l2_dev.dev; + + vq->ops = &pxac_vb2_ops; + vq->mem_ops = &vb2_dma_sg_memops; + vq->lock = &pcdev->mlock; + + ret = vb2_queue_init(vq); + dev_dbg(pcdev_to_dev(pcdev), + "vb2_queue_init(vq=%p): %d\n", vq, ret); + + return ret; +} + +/* + * Video ioctls section + */ +static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; + u32 pixfmt = pcdev->current_fmt->host_fmt->fourcc; unsigned long bus_flags, common_flags; int ret; - struct pxa_cam *cam = icd->host_priv; - ret = test_platform_param(pcdev, icd->current_fmt->host_fmt->bits_per_sample, + ret = test_platform_param(pcdev, + pcdev->current_fmt->host_fmt->bits_per_sample, &bus_flags); if (ret < 0) return ret; - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + ret = sensor_call(pcdev, video, g_mbus_config, &cfg); if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, + common_flags = pxa_mbus_config_compatible(&cfg, bus_flags); if (!common_flags) { - dev_warn(icd->parent, + dev_warn(pcdev_to_dev(pcdev), "Flags incompatible: camera 0x%x, host 0x%lx\n", cfg.flags, bus_flags); return -EINVAL; @@ -1124,26 +1626,22 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd) } cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + ret = sensor_call(pcdev, video, s_mbus_config, &cfg); if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + dev_dbg(pcdev_to_dev(pcdev), + "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } - cam->flags = common_flags; - - pxa_camera_setup_cicr(icd, common_flags, pixfmt); + pxa_camera_setup_cicr(pcdev, common_flags, pixfmt); return 0; } -static int pxa_camera_try_bus_param(struct soc_camera_device *icd, +static int pxa_camera_try_bus_param(struct pxa_camera_dev *pcdev, unsigned char buswidth) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long bus_flags, common_flags; int ret = test_platform_param(pcdev, buswidth, &bus_flags); @@ -1151,12 +1649,12 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, if (ret < 0) return ret; - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + ret = sensor_call(pcdev, video, g_mbus_config, &cfg); if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, + common_flags = pxa_mbus_config_compatible(&cfg, bus_flags); if (!common_flags) { - dev_warn(icd->parent, + dev_warn(pcdev_to_dev(pcdev), "Flags incompatible: camera 0x%x, host 0x%lx\n", cfg.flags, bus_flags); return -EINVAL; @@ -1168,66 +1666,56 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, return ret; } -static const struct soc_mbus_pixelfmt pxa_camera_formats[] = { +static const struct pxa_mbus_pixelfmt pxa_camera_formats[] = { { .fourcc = V4L2_PIX_FMT_YUV422P, .name = "Planar YUV422 16 bit", .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PLANAR_2Y_U_V, }, }; /* This will be corrected as we get more formats */ -static bool pxa_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) +static bool pxa_camera_packing_supported(const struct pxa_mbus_pixelfmt *fmt) { - return fmt->packing == SOC_MBUS_PACKING_NONE || + return fmt->packing == PXA_MBUS_PACKING_NONE || (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || + fmt->packing == PXA_MBUS_PACKING_2X8_PADHI) || (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); + fmt->packing == PXA_MBUS_PACKING_EXTEND16); } -static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, +static int pxa_camera_get_formats(struct v4l2_device *v4l2_dev, + unsigned int idx, struct soc_camera_format_xlate *xlate) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(v4l2_dev); int formats = 0, ret; - struct pxa_cam *cam; struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .index = idx, }; - const struct soc_mbus_pixelfmt *fmt; + const struct pxa_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); + ret = sensor_call(pcdev, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code.code); + fmt = pxa_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); + dev_err(pcdev_to_dev(pcdev), + "Invalid format code #%u: %d\n", idx, code.code); return 0; } /* This also checks support for the requested bits-per-sample */ - ret = pxa_camera_try_bus_param(icd, fmt->bits_per_sample); + ret = pxa_camera_try_bus_param(pcdev, fmt->bits_per_sample); if (ret < 0) return 0; - if (!icd->host_priv) { - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: formats++; @@ -1235,25 +1723,29 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id xlate->host_fmt = &pxa_camera_formats[0]; xlate->code = code.code; xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", + dev_dbg(pcdev_to_dev(pcdev), + "Providing format %s using code %d\n", pxa_camera_formats[0].name, code.code); } + /* fall through */ case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: if (xlate) - dev_dbg(dev, "Providing format %s packed\n", + dev_dbg(pcdev_to_dev(pcdev), + "Providing format %s packed\n", fmt->name); break; default: if (!pxa_camera_packing_supported(fmt)) return 0; if (xlate) - dev_dbg(dev, + dev_dbg(pcdev_to_dev(pcdev), "Providing format %s in pass-through mode\n", fmt->name); + break; } /* Generic pass-through */ @@ -1267,10 +1759,22 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id return formats; } -static void pxa_camera_put_formats(struct soc_camera_device *icd) +static int pxa_camera_build_formats(struct pxa_camera_dev *pcdev) { - kfree(icd->host_priv); - icd->host_priv = NULL; + struct soc_camera_format_xlate *xlate; + + xlate = pxa_mbus_build_fmts_xlate(&pcdev->v4l2_dev, pcdev->sensor, + pxa_camera_get_formats); + if (IS_ERR(xlate)) + return PTR_ERR(xlate); + + pcdev->user_formats = xlate; + return 0; +} + +static void pxa_camera_destroy_formats(struct pxa_camera_dev *pcdev) +{ + kfree(pcdev->user_formats); } static int pxa_camera_check_frame(u32 width, u32 height) @@ -1280,158 +1784,72 @@ static int pxa_camera_check_frame(u32 width, u32 height) (width & 0x01); } -static int pxa_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int pxac_vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) { - const struct v4l2_rect *rect = &a->c; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_sense sense = { - .master_clock = pcdev->mclk, - .pixel_clock_max = pcdev->ciclk / 4, - }; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct pxa_cam *cam = icd->host_priv; - u32 fourcc = icd->current_fmt->host_fmt->fourcc; - int ret; - - /* If PCLK is used to latch data from the sensor, check sense */ - if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) - icd->sense = &sense; - - ret = v4l2_subdev_call(sd, video, s_crop, a); - - icd->sense = NULL; - - if (ret < 0) { - dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n", - rect->width, rect->height, rect->left, rect->top); - return ret; - } + struct pxa_camera_dev *pcdev = video_drvdata(file); - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (pxa_camera_check_frame(mf->width, mf->height)) { - /* - * Camera cropping produced a frame beyond our capabilities. - * FIXME: just extract a subframe, that we can process. - */ - v4l_bound_align_image(&mf->width, 48, 2048, 1, - &mf->height, 32, 2048, 0, - fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); - if (ret < 0) - return ret; + if (reg->reg > CIBR2) + return -ERANGE; - if (pxa_camera_check_frame(mf->width, mf->height)) { - dev_warn(icd->parent, - "Inconsistent state. Use S_FMT to repair\n"); - return -EINVAL; - } - } - - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { - if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(dev, - "pixel clock %lu set by the camera too high!", - sense.pixel_clock); - return -EIO; - } - recalculate_fifo_timeout(pcdev, sense.pixel_clock); - } - - icd->user_width = mf->width; - icd->user_height = mf->height; - - pxa_camera_setup_cicr(icd, cam->flags, fourcc); - - return ret; + reg->val = __raw_readl(pcdev->base + reg->reg); + reg->size = sizeof(__u32); + return 0; } -static int pxa_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxac_vidioc_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) { - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate = NULL; - struct soc_camera_sense sense = { - .master_clock = pcdev->mclk, - .pixel_clock_max = pcdev->ciclk / 4, - }; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; + struct pxa_camera_dev *pcdev = video_drvdata(file); - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pix->pixelformat); + if (reg->reg > CIBR2) + return -ERANGE; + if (reg->size != sizeof(__u32)) return -EINVAL; - } - - /* If PCLK is used to latch data from the sensor, check sense */ - if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) - /* The caller holds a mutex. */ - icd->sense = &sense; - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; + __raw_writel(reg->val, pcdev->base + reg->reg); + return 0; +} +#endif - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); +static int pxac_vidioc_enum_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_fmtdesc *f) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + const struct pxa_mbus_pixelfmt *format; + unsigned int idx; - if (mf->code != xlate->code) + for (idx = 0; pcdev->user_formats[idx].code; idx++); + if (f->index >= idx) return -EINVAL; - icd->sense = NULL; - - if (ret < 0) { - dev_warn(dev, "Failed to configure for format %x\n", - pix->pixelformat); - } else if (pxa_camera_check_frame(mf->width, mf->height)) { - dev_warn(dev, - "Camera driver produced an unsupported frame %dx%d\n", - mf->width, mf->height); - ret = -EINVAL; - } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { - if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(dev, - "pixel clock %lu set by the camera too high!", - sense.pixel_clock); - return -EIO; - } - recalculate_fifo_timeout(pcdev, sense.pixel_clock); - } - - if (ret < 0) - return ret; + format = pcdev->user_formats[f->index].host_fmt; + f->pixelformat = format->fourcc; + return 0; +} - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; +static int pxac_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + struct v4l2_pix_format *pix = &f->fmt.pix; - return ret; + pix->width = pcdev->current_pix.width; + pix->height = pcdev->current_pix.height; + pix->bytesperline = pcdev->current_pix.bytesperline; + pix->sizeimage = pcdev->current_pix.sizeimage; + pix->field = pcdev->current_pix.field; + pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc; + pix->colorspace = pcdev->current_pix.colorspace; + dev_dbg(pcdev_to_dev(pcdev), "current_fmt->fourcc: 0x%08x\n", + pcdev->current_fmt->host_fmt->fourcc); + return 0; } -static int pxa_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxac_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct pxa_camera_dev *pcdev = video_drvdata(filp); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; @@ -1442,9 +1860,9 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, __u32 pixfmt = pix->pixelformat; int ret; - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + xlate = pxa_mbus_xlate_by_fourcc(pcdev->user_formats, pixfmt); if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); + dev_warn(pcdev_to_dev(pcdev), "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1458,90 +1876,311 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, &pix->height, 32, 2048, 0, pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - /* Only progressive video supported so far */ - mf->field = V4L2_FIELD_NONE; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); + v4l2_fill_mbus_format(mf, pix, xlate->code); + ret = sensor_call(pcdev, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf->width; - pix->height = mf->height; - pix->colorspace = mf->colorspace; + v4l2_fill_pix_format(pix, mf); + /* Only progressive video supported so far */ switch (mf->field) { case V4L2_FIELD_ANY: case V4L2_FIELD_NONE: - pix->field = V4L2_FIELD_NONE; + pix->field = V4L2_FIELD_NONE; break; default: /* TODO: support interlaced at least in pass-through mode */ - dev_err(icd->parent, "Field type %d unsupported.\n", + dev_err(pcdev_to_dev(pcdev), "Field type %d unsupported.\n", mf->field); return -EINVAL; } - return ret; + ret = pxa_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; + + pix->bytesperline = ret; + ret = pxa_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = ret; + return 0; } -static int pxa_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) +static int pxac_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) { - int i; + struct pxa_camera_dev *pcdev = video_drvdata(filp); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + unsigned long flags; + int ret, is_busy; - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct pxa_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct pxa_buffer, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); + dev_dbg(pcdev_to_dev(pcdev), + "s_fmt_vid_cap(pix=%dx%d:%x)\n", + pix->width, pix->height, pix->pixelformat); + + spin_lock_irqsave(&pcdev->lock, flags); + is_busy = pcdev->active || vb2_is_busy(&pcdev->vb2_vq); + spin_unlock_irqrestore(&pcdev->lock, flags); + + if (is_busy) + return -EBUSY; + + ret = pxac_vidioc_try_fmt_vid_cap(filp, priv, f); + if (ret) + return ret; + + xlate = pxa_mbus_xlate_by_fourcc(pcdev->user_formats, + pix->pixelformat); + v4l2_fill_mbus_format(&format.format, pix, xlate->code); + ret = sensor_call(pcdev, pad, set_fmt, NULL, &format); + if (ret < 0) { + dev_warn(pcdev_to_dev(pcdev), + "Failed to configure for format %x\n", + pix->pixelformat); + } else if (pxa_camera_check_frame(pix->width, pix->height)) { + dev_warn(pcdev_to_dev(pcdev), + "Camera driver produced an unsupported frame %dx%d\n", + pix->width, pix->height); + return -EINVAL; } + pcdev->current_fmt = xlate; + pcdev->current_pix = *pix; + + ret = pxa_camera_set_bus_param(pcdev); + return ret; +} + +static int pxac_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->bus_info, "platform:pxa-camera", sizeof(cap->bus_info)); + strlcpy(cap->driver, PXA_CAM_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } -static unsigned int pxa_camera_poll(struct file *file, poll_table *pt) +static int pxac_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) { - struct soc_camera_device *icd = file->private_data; - struct pxa_buffer *buf; + if (i->index > 0) + return -EINVAL; - buf = list_entry(icd->vb_vidq.stream.next, struct pxa_buffer, - vb.stream); + i->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(i->name, "Camera", sizeof(i->name)); - poll_wait(file, &buf->vb.done, pt); + return 0; +} - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; +static int pxac_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; return 0; } -static int pxa_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) +static int pxac_vidioc_s_input(struct file *file, void *priv, unsigned int i) { - /* cap->name is set by the firendly caller:-> */ - strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + if (i > 0) + return -EINVAL; return 0; } +static int pxac_fops_camera_open(struct file *filp) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + int ret; + + mutex_lock(&pcdev->mlock); + ret = v4l2_fh_open(filp); + if (ret < 0) + goto out; + + ret = sensor_call(pcdev, core, s_power, 1); + if (ret) + v4l2_fh_release(filp); +out: + mutex_unlock(&pcdev->mlock); + return ret; +} + +static int pxac_fops_camera_release(struct file *filp) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + int ret; + + ret = vb2_fop_release(filp); + if (ret < 0) + return ret; + + mutex_lock(&pcdev->mlock); + ret = sensor_call(pcdev, core, s_power, 0); + mutex_unlock(&pcdev->mlock); + + return ret; +} + +static const struct v4l2_file_operations pxa_camera_fops = { + .owner = THIS_MODULE, + .open = pxac_fops_camera_open, + .release = pxac_fops_camera_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = { + .vidioc_querycap = pxac_vidioc_querycap, + + .vidioc_enum_input = pxac_vidioc_enum_input, + .vidioc_g_input = pxac_vidioc_g_input, + .vidioc_s_input = pxac_vidioc_s_input, + + .vidioc_enum_fmt_vid_cap = pxac_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pxac_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pxac_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pxac_vidioc_try_fmt_vid_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pxac_vidioc_g_register, + .vidioc_s_register = pxac_vidioc_s_register, +#endif +}; + +static struct v4l2_clk_ops pxa_camera_mclk_ops = { +}; + +static const struct video_device pxa_camera_videodev_template = { + .name = "pxa-camera", + .minor = -1, + .fops = &pxa_camera_fops, + .ioctl_ops = &pxa_camera_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, +}; + +static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int err; + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(v4l2_dev); + struct video_device *vdev = &pcdev->vdev; + struct v4l2_pix_format *pix = &pcdev->current_pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; + + dev_info(pcdev_to_dev(pcdev), "%s(): trying to bind a device\n", + __func__); + mutex_lock(&pcdev->mlock); + *vdev = pxa_camera_videodev_template; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &pcdev->mlock; + pcdev->sensor = subdev; + pcdev->vdev.queue = &pcdev->vb2_vq; + pcdev->vdev.v4l2_dev = &pcdev->v4l2_dev; + pcdev->vdev.ctrl_handler = subdev->ctrl_handler; + video_set_drvdata(&pcdev->vdev, pcdev); + + err = pxa_camera_build_formats(pcdev); + if (err) { + dev_err(pcdev_to_dev(pcdev), "building formats failed: %d\n", + err); + goto out; + } + + pcdev->current_fmt = pcdev->user_formats; + pix->field = V4L2_FIELD_NONE; + pix->width = DEFAULT_WIDTH; + pix->height = DEFAULT_HEIGHT; + pix->bytesperline = + pxa_mbus_bytes_per_line(pix->width, + pcdev->current_fmt->host_fmt); + pix->sizeimage = + pxa_mbus_image_size(pcdev->current_fmt->host_fmt, + pix->bytesperline, pix->height); + pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc; + v4l2_fill_mbus_format(mf, pix, pcdev->current_fmt->code); + err = sensor_call(pcdev, pad, set_fmt, NULL, &format); + if (err) + goto out; + + v4l2_fill_pix_format(pix, mf); + pr_info("%s(): colorspace=0x%x pixfmt=0x%x\n", + __func__, pix->colorspace, pix->pixelformat); + + err = pxa_camera_init_videobuf2(pcdev); + if (err) + goto out; + + err = video_register_device(&pcdev->vdev, VFL_TYPE_GRABBER, -1); + if (err) { + v4l2_err(v4l2_dev, "register video device failed: %d\n", err); + pcdev->sensor = NULL; + } else { + dev_info(pcdev_to_dev(pcdev), + "PXA Camera driver attached to camera %s\n", + subdev->name); + } +out: + mutex_unlock(&pcdev->mlock); + return err; +} + +static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(notifier->v4l2_dev); + + mutex_lock(&pcdev->mlock); + dev_info(pcdev_to_dev(pcdev), + "PXA Camera driver detached from camera %s\n", + subdev->name); + + /* disable capture, disable interrupts */ + __raw_writel(0x3ff, pcdev->base + CICR0); + + /* Stop DMA engine */ + pxa_dma_stop_channels(pcdev); + + pxa_camera_destroy_formats(pcdev); + video_unregister_device(&pcdev->vdev); + pcdev->sensor = NULL; + + mutex_unlock(&pcdev->mlock); +} + +/* + * Driver probe, remove, suspend and resume operations + */ static int pxa_camera_suspend(struct device *dev) { - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_camera_dev *pcdev = dev_get_drvdata(dev); int i = 0, ret = 0; pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0); @@ -1550,9 +2189,8 @@ static int pxa_camera_suspend(struct device *dev) pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3); pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4); - if (pcdev->soc_host.icd) { - struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd); - ret = v4l2_subdev_call(sd, core, s_power, 0); + if (pcdev->sensor) { + ret = sensor_call(pcdev, core, s_power, 0); if (ret == -ENOIOCTLCMD) ret = 0; } @@ -1562,8 +2200,7 @@ static int pxa_camera_suspend(struct device *dev) static int pxa_camera_resume(struct device *dev) { - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_camera_dev *pcdev = dev_get_drvdata(dev); int i = 0, ret = 0; __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0); @@ -1572,9 +2209,8 @@ static int pxa_camera_resume(struct device *dev) __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3); __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4); - if (pcdev->soc_host.icd) { - struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd); - ret = v4l2_subdev_call(sd, core, s_power, 1); + if (pcdev->sensor) { + ret = sensor_call(pcdev, core, s_power, 1); if (ret == -ENOIOCTLCMD) ret = 0; } @@ -1586,29 +2222,12 @@ static int pxa_camera_resume(struct device *dev) return ret; } -static struct soc_camera_host_ops pxa_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = pxa_camera_add_device, - .remove = pxa_camera_remove_device, - .clock_start = pxa_camera_clock_start, - .clock_stop = pxa_camera_clock_stop, - .set_crop = pxa_camera_set_crop, - .get_formats = pxa_camera_get_formats, - .put_formats = pxa_camera_put_formats, - .set_fmt = pxa_camera_set_fmt, - .try_fmt = pxa_camera_try_fmt, - .init_videobuf = pxa_camera_init_videobuf, - .reqbufs = pxa_camera_reqbufs, - .poll = pxa_camera_poll, - .querycap = pxa_camera_querycap, - .set_bus_param = pxa_camera_set_bus_param, -}; - static int pxa_camera_pdata_from_dt(struct device *dev, - struct pxa_camera_dev *pcdev) + struct pxa_camera_dev *pcdev, + struct v4l2_async_subdev *asd) { u32 mclk_rate; - struct device_node *np = dev->of_node; + struct device_node *remote, *np = dev->of_node; struct v4l2_of_endpoint ep; int err = of_property_read_u32(np, "clock-frequency", &mclk_rate); @@ -1660,6 +2279,15 @@ static int pxa_camera_pdata_from_dt(struct device *dev, if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; + asd->match_type = V4L2_ASYNC_MATCH_OF; + remote = of_graph_get_remote_port(np); + if (remote) { + asd->match.of.node = remote; + of_node_put(remote); + } else { + dev_notice(dev, "no remote for %s\n", of_node_full_name(np)); + } + out: of_node_put(np); @@ -1678,6 +2306,7 @@ static int pxa_camera_probe(struct platform_device *pdev) }; dma_cap_mask_t mask; struct pxad_param params; + char clk_name[V4L2_CLK_NAME_SIZE]; int irq; int err = 0, i; @@ -1700,10 +2329,14 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (&pdev->dev.of_node && !pcdev->pdata) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); } else { pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; + pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; + pcdev->asd.match.i2c.adapter_id = + pcdev->pdata->sensor_i2c_adapter_id; + pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; } if (err < 0) return err; @@ -1735,6 +2368,7 @@ static int pxa_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); + mutex_init(&pcdev->mlock); /* * Request the regions. @@ -1767,6 +2401,7 @@ static int pxa_camera_probe(struct platform_device *pdev) ¶ms, &pdev->dev, "CI_U"); if (!pcdev->dma_chans[1]) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); + err = -ENODEV; goto exit_free_dma_y; } @@ -1776,6 +2411,7 @@ static int pxa_camera_probe(struct platform_device *pdev) ¶ms, &pdev->dev, "CI_V"); if (!pcdev->dma_chans[2]) { dev_err(&pdev->dev, "Can't request DMA for V\n"); + err = -ENODEV; goto exit_free_dma_u; } @@ -1797,19 +2433,50 @@ static int pxa_camera_probe(struct platform_device *pdev) goto exit_free_dma; } - pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; - pcdev->soc_host.ops = &pxa_soc_camera_host_ops; - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev); - err = soc_camera_host_register(&pcdev->soc_host); + pxa_camera_activate(pcdev); + + dev_set_drvdata(&pdev->dev, pcdev); + err = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); if (err) goto exit_free_dma; - return 0; + pcdev->asds[0] = &pcdev->asd; + pcdev->notifier.subdevs = pcdev->asds; + pcdev->notifier.num_subdevs = 1; + pcdev->notifier.bound = pxa_camera_sensor_bound; + pcdev->notifier.unbind = pxa_camera_sensor_unbind; + if (!of_have_populated_dt()) + pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; + + err = pxa_camera_init_videobuf2(pcdev); + if (err) + goto exit_free_v4l2dev; + + if (pcdev->mclk) { + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + pcdev->asd.match.i2c.adapter_id, + pcdev->asd.match.i2c.address); + + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, + clk_name, NULL); + if (IS_ERR(pcdev->mclk_clk)) { + err = PTR_ERR(pcdev->mclk_clk); + goto exit_free_v4l2dev; + } + } + + err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); + if (err) + goto exit_free_clk; + + return 0; +exit_free_clk: + v4l2_clk_unregister(pcdev->mclk_clk); +exit_free_v4l2dev: + v4l2_device_unregister(&pcdev->v4l2_dev); exit_free_dma: dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: @@ -1821,15 +2488,15 @@ exit_free_dma_y: static int pxa_camera_remove(struct platform_device *pdev) { - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct pxa_camera_dev *pcdev = container_of(soc_host, - struct pxa_camera_dev, soc_host); + struct pxa_camera_dev *pcdev = dev_get_drvdata(&pdev->dev); + pxa_camera_deactivate(pcdev); dma_release_channel(pcdev->dma_chans[0]); dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); - soc_camera_host_unregister(soc_host); + v4l2_clk_unregister(pcdev->mclk_clk); + v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index bc50c69ee0c5..f3a3f31cdfa9 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -99,14 +99,14 @@ EXPORT_SYMBOL_GPL(rcar_fcp_put); */ int rcar_fcp_enable(struct rcar_fcp_device *fcp) { - int error; + int ret; if (!fcp) return 0; - error = pm_runtime_get_sync(fcp->dev); - if (error < 0) - return error; + ret = pm_runtime_get_sync(fcp->dev); + if (ret < 0) + return ret; return 0; } @@ -165,6 +165,7 @@ static int rcar_fcp_remove(struct platform_device *pdev) } static const struct of_device_id rcar_fcp_of_match[] = { + { .compatible = "renesas,fcpf" }, { .compatible = "renesas,fcpv" }, { }, }; diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index b2ff2d4e8bb1..111d2a151f6a 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,6 +1,6 @@ config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 4b2007b73463..098a0b1cc10a 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -31,26 +31,22 @@ #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) -static int rvin_mbus_supported(struct rvin_dev *vin) +static bool rvin_mbus_supported(struct rvin_graph_entity *entity) { - struct v4l2_subdev *sd; + struct v4l2_subdev *sd = entity->subdev; struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - sd = vin_to_source(vin); - code.index = 0; while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { code.index++; switch (code.code) { case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - vin->source.code = code.code; - vin_dbg(vin, "Found supported media bus format: %d\n", - vin->source.code); + entity->code = code.code; return true; default: break; @@ -60,142 +56,168 @@ static int rvin_mbus_supported(struct rvin_dev *vin) return false; } -static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) +static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) { struct rvin_dev *vin = notifier_to_vin(notifier); int ret; + /* Verify subdevices mbus format */ + if (!rvin_mbus_supported(&vin->digital)) { + vin_err(vin, "Unsupported media bus format for %s\n", + vin->digital.subdev->name); + return -EINVAL; + } + + vin_dbg(vin, "Found media bus format for %s: %d\n", + vin->digital.subdev->name, vin->digital.code); + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); if (ret < 0) { vin_err(vin, "Failed to register subdev nodes\n"); return ret; } - if (!rvin_mbus_supported(vin)) { - vin_err(vin, "No supported mediabus format found\n"); - return -EINVAL; + return rvin_v4l2_probe(vin); +} + +static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + if (vin->digital.subdev == subdev) { + vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + rvin_v4l2_remove(vin); + vin->digital.subdev = NULL; + return; } - return rvin_v4l2_probe(vin); + vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name); } -static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, +static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { struct rvin_dev *vin = notifier_to_vin(notifier); - rvin_v4l2_remove(vin); + v4l2_set_subdev_hostdata(subdev, vin); + + if (vin->digital.asd.match.of.node == subdev->dev->of_node) { + vin_dbg(vin, "bound digital subdev %s\n", subdev->name); + vin->digital.subdev = subdev; + return 0; + } + + vin_err(vin, "no entity for subdev %s to bind\n", subdev->name); + return -EINVAL; } -static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int rvin_digitial_parse_v4l2(struct rvin_dev *vin, + struct device_node *ep, + struct v4l2_mbus_config *mbus_cfg) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct v4l2_of_endpoint v4l2_ep; + int ret; - vin_dbg(vin, "subdev %s bound\n", subdev->name); + ret = v4l2_of_parse_endpoint(ep, &v4l2_ep); + if (ret) { + vin_err(vin, "Could not parse v4l2 endpoint\n"); + return -EINVAL; + } - vin->entity.entity = &subdev->entity; - vin->entity.subdev = subdev; + mbus_cfg->type = v4l2_ep.bus_type; + + switch (mbus_cfg->type) { + case V4L2_MBUS_PARALLEL: + vin_dbg(vin, "Found PARALLEL media bus\n"); + mbus_cfg->flags = v4l2_ep.bus.parallel.flags; + break; + case V4L2_MBUS_BT656: + vin_dbg(vin, "Found BT656 media bus\n"); + mbus_cfg->flags = 0; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } return 0; } -static int rvin_graph_parse(struct rvin_dev *vin, - struct device_node *node) +static int rvin_digital_graph_parse(struct rvin_dev *vin) { - struct device_node *remote; - struct device_node *ep = NULL; - struct device_node *next; - int ret = 0; - - while (1) { - next = of_graph_get_next_endpoint(node, ep); - if (!next) - break; - - of_node_put(ep); - ep = next; + struct device_node *ep, *np; + int ret; - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - ret = -EINVAL; - break; - } + vin->digital.asd.match.of.node = NULL; + vin->digital.subdev = NULL; - /* Skip entities that we have already processed. */ - if (remote == vin->dev->of_node) { - of_node_put(remote); - continue; - } + /* + * Port 0 id 0 is local digital input, try to get it. + * Not all instances can or will have this, that is OK + */ + ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0); + if (!ep) + return 0; - /* Remote node to connect */ - if (!vin->entity.node) { - vin->entity.node = remote; - vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; - vin->entity.asd.match.of.node = remote; - ret++; - } + np = of_graph_get_remote_port_parent(ep); + if (!np) { + vin_err(vin, "No remote parent for digital input\n"); + of_node_put(ep); + return -EINVAL; } + of_node_put(np); + ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg); of_node_put(ep); + if (ret) + return ret; - return ret; + vin->digital.asd.match.of.node = np; + vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF; + + return 0; } -static int rvin_graph_init(struct rvin_dev *vin) +static int rvin_digital_graph_init(struct rvin_dev *vin) { struct v4l2_async_subdev **subdevs = NULL; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = rvin_graph_parse(vin, vin->dev->of_node); - if (ret < 0) { - vin_err(vin, "Graph parsing failed\n"); - goto done; - } - - if (!ret) { - vin_err(vin, "No subdev found in graph\n"); - goto done; - } + ret = rvin_digital_graph_parse(vin); + if (ret) + return ret; - if (ret != 1) { - vin_err(vin, "More then one subdev found in graph\n"); - goto done; + if (!vin->digital.asd.match.of.node) { + vin_dbg(vin, "No digital subdevice found\n"); + return -ENODEV; } /* Register the subdevices notifier. */ subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); - if (subdevs == NULL) { - ret = -ENOMEM; - goto done; - } + if (subdevs == NULL) + return -ENOMEM; - subdevs[0] = &vin->entity.asd; + subdevs[0] = &vin->digital.asd; + + vin_dbg(vin, "Found digital subdevice %s\n", + of_node_full_name(subdevs[0]->match.of.node)); - vin->notifier.subdevs = subdevs; vin->notifier.num_subdevs = 1; - vin->notifier.bound = rvin_graph_notify_bound; - vin->notifier.unbind = rvin_graph_notify_unbind; - vin->notifier.complete = rvin_graph_notify_complete; + vin->notifier.subdevs = subdevs; + vin->notifier.bound = rvin_digital_notify_bound; + vin->notifier.unbind = rvin_digital_notify_unbind; + vin->notifier.complete = rvin_digital_notify_complete; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); - goto done; - } - - ret = 0; - -done: - if (ret < 0) { - v4l2_async_notifier_unregister(&vin->notifier); - of_node_put(vin->entity.node); + return ret; } - return ret; + return 0; } /* ----------------------------------------------------------------------------- @@ -209,56 +231,14 @@ static const struct of_device_id rvin_of_id_table[] = { { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, { }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); -static int rvin_parse_dt(struct rvin_dev *vin) -{ - const struct of_device_id *match; - struct v4l2_of_endpoint ep; - struct device_node *np; - int ret; - - match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev); - if (!match) - return -ENODEV; - - vin->chip = (enum chip_id)match->data; - - np = of_graph_get_next_endpoint(vin->dev->of_node, NULL); - if (!np) { - vin_err(vin, "Could not find endpoint\n"); - return -EINVAL; - } - - ret = v4l2_of_parse_endpoint(np, &ep); - if (ret) { - vin_err(vin, "Could not parse endpoint\n"); - return ret; - } - - of_node_put(np); - - vin->mbus_cfg.type = ep.bus_type; - - switch (vin->mbus_cfg.type) { - case V4L2_MBUS_PARALLEL: - vin->mbus_cfg.flags = ep.bus.parallel.flags; - break; - case V4L2_MBUS_BT656: - vin->mbus_cfg.flags = 0; - break; - default: - vin_err(vin, "Unknown media bus type\n"); - return -EINVAL; - } - - return 0; -} - static int rcar_vin_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct rvin_dev *vin; struct resource *mem; int irq, ret; @@ -267,11 +247,12 @@ static int rcar_vin_probe(struct platform_device *pdev) if (!vin) return -ENOMEM; - vin->dev = &pdev->dev; + match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev); + if (!match) + return -ENODEV; - ret = rvin_parse_dt(vin); - if (ret) - return ret; + vin->dev = &pdev->dev; + vin->chip = (enum chip_id)match->data; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem == NULL) @@ -282,14 +263,14 @@ static int rcar_vin_probe(struct platform_device *pdev) return PTR_ERR(vin->base); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return ret; + if (irq < 0) + return irq; ret = rvin_dma_probe(vin, irq); if (ret) return ret; - ret = rvin_graph_init(vin); + ret = rvin_digital_graph_init(vin); if (ret < 0) goto error; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 496aa97b6400..9ccd5ff55e19 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -95,6 +95,7 @@ /* Video n Module Status Register bits */ #define VNMS_FBS_MASK (3 << 3) #define VNMS_FBS_SHIFT 3 +#define VNMS_FS (1 << 2) #define VNMS_AV (1 << 1) #define VNMS_CA (1 << 0) @@ -131,6 +132,7 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset) static int rvin_setup(struct rvin_dev *vin) { u32 vnmc, dmr, dmr2, interrupts; + v4l2_std_id std; bool progressive = false, output_is_yuv = false, input_is_yuv = false; switch (vin->format.field) { @@ -141,12 +143,21 @@ static int rvin_setup(struct rvin_dev *vin) vnmc = VNMC_IM_EVEN; break; case V4L2_FIELD_INTERLACED: + /* Default to TB */ + vnmc = VNMC_IM_FULL; + /* Use BT if video standard can be read and is 60 Hz format */ + if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) { + if (std & V4L2_STD_525_60) + vnmc = VNMC_IM_FULL | VNMC_FOC; + } + break; case V4L2_FIELD_INTERLACED_TB: vnmc = VNMC_IM_FULL; break; case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; + case V4L2_FIELD_ALTERNATE: case V4L2_FIELD_NONE: if (vin->continuous) { vnmc = VNMC_IM_ODD_EVEN; @@ -163,24 +174,24 @@ static int rvin_setup(struct rvin_dev *vin) /* * Input interface */ - switch (vin->source.code) { + switch (vin->digital.code) { case MEDIA_BUS_FMT_YUYV8_1X16: /* BT.601/BT.1358 16bit YCbCr422 */ vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; - case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ? VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; input_is_yuv = true; break; case MEDIA_BUS_FMT_RGB888_1X24: vnmc |= VNMC_INF_RGB888; break; - case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ? VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; input_is_yuv = true; break; @@ -192,11 +203,11 @@ static int rvin_setup(struct rvin_dev *vin) dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); /* Hsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_HPS; /* Vsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_VPS; /* @@ -225,11 +236,9 @@ static int rvin_setup(struct rvin_dev *vin) dmr = 0; break; case V4L2_PIX_FMT_XBGR32: - if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) { - dmr = VNDMR_EXRGB; - break; - } - /* fall through */ + /* Note: not supported on M1 */ + dmr = VNDMR_EXRGB; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); @@ -322,15 +331,26 @@ static bool rvin_capture_active(struct rvin_dev *vin) return rvin_read(vin, VNMS_REG) & VNMS_CA; } -static int rvin_get_active_slot(struct rvin_dev *vin) +static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms) { if (vin->continuous) - return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK) - >> VNMS_FBS_SHIFT; + return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; return 0; } +static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) +{ + if (vin->format.field == V4L2_FIELD_ALTERNATE) { + /* If FS is set it's a Even field */ + if (vnms & VNMS_FS) + return V4L2_FIELD_BOTTOM; + return V4L2_FIELD_TOP; + } + + return vin->format.field; +} + static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) { const struct rvin_video_format *fmt; @@ -871,7 +891,7 @@ static bool rvin_fill_hw(struct rvin_dev *vin) static irqreturn_t rvin_irq(int irq, void *data) { struct rvin_dev *vin = data; - u32 int_status; + u32 int_status, vnms; int slot; unsigned int sequence, handled = 0; unsigned long flags; @@ -898,7 +918,8 @@ static irqreturn_t rvin_irq(int irq, void *data) } /* Prepare for capture and update state */ - slot = rvin_get_active_slot(vin); + vnms = rvin_read(vin, VNMS_REG); + slot = rvin_get_active_slot(vin, vnms); sequence = vin->sequence++; vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", @@ -913,7 +934,7 @@ static irqreturn_t rvin_irq(int irq, void *data) goto done; /* Capture frame */ - vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); vin->queue_buf[slot]->sequence = sequence; vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); @@ -1116,7 +1137,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq) rvin_disable_interrupts(vin); } -static struct vb2_ops rvin_qops = { +static const struct vb2_ops rvin_qops = { .queue_setup = rvin_queue_setup, .buf_prepare = rvin_buffer_prepare, .buf_queue = rvin_buffer_queue, diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 10a5c107e8b9..2bbe6d495fa6 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -92,21 +92,84 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) * V4L2 */ +static void rvin_reset_crop_compose(struct rvin_dev *vin) +{ + vin->crop.top = vin->crop.left = 0; + vin->crop.width = vin->source.width; + vin->crop.height = vin->source.height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = vin->format.width; + vin->compose.height = vin->format.height; +} + +static int rvin_reset_format(struct rvin_dev *vin) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + fmt.pad = vin->src_pad_idx; + + ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); + if (ret) + return ret; + + vin->format.width = mf->width; + vin->format.height = mf->height; + vin->format.colorspace = mf->colorspace; + vin->format.field = mf->field; + + /* + * If the subdevice uses ALTERNATE field mode and G_STD is + * implemented use the VIN HW to combine the two fields to + * one INTERLACED frame. The ALTERNATE field mode can still + * be requested in S_FMT and be respected, this is just the + * default which is applied at probing or when S_STD is called. + */ + if (vin->format.field == V4L2_FIELD_ALTERNATE && + v4l2_subdev_has_op(vin_to_source(vin), video, g_std)) + vin->format.field = V4L2_FIELD_INTERLACED; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + vin->format.height /= 2; + break; + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + default: + vin->format.field = V4L2_FIELD_NONE; + break; + } + + rvin_reset_crop_compose(vin); + + return 0; +} + static int __rvin_try_format_source(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) { struct v4l2_subdev *sd; struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, }; + enum v4l2_field field; int ret; sd = vin_to_source(vin); - v4l2_fill_mbus_format(&format.format, pix, vin->source.code); + v4l2_fill_mbus_format(&format.format, pix, vin->digital.code); pad_cfg = v4l2_subdev_alloc_pad_config(sd); if (pad_cfg == NULL) @@ -114,28 +177,31 @@ static int __rvin_try_format_source(struct rvin_dev *vin, format.pad = vin->src_pad_idx; - ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt, - pad_cfg, &format); - if (ret < 0) - goto cleanup; + field = pix->field; + + ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto done; v4l2_fill_pix_format(pix, &format.format); + pix->field = field; + source->width = pix->width; source->height = pix->height; vin_dbg(vin, "Source resolution: %ux%u\n", source->width, source->height); -cleanup: +done: v4l2_subdev_free_pad_config(pad_cfg); - return 0; + return ret; } static int __rvin_try_format(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) { const struct rvin_video_format *info; u32 rwidth, rheight, walign; @@ -144,6 +210,10 @@ static int __rvin_try_format(struct rvin_dev *vin, rwidth = pix->width; rheight = pix->height; + /* Keep current field if no specific one is asked for */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = vin->format.field; + /* * Retrieve format information and select the current format if the * requested format isn't supported. @@ -164,21 +234,14 @@ static int __rvin_try_format(struct rvin_dev *vin, /* Limit to source capabilities */ __rvin_try_format_source(vin, which, pix, source); - /* If source can't match format try if VIN can scale */ - if (source->width != rwidth || source->height != rheight) - rvin_scale_try(vin, pix, rwidth, rheight); - - /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ - walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; - - /* Limit to VIN capabilities */ - v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, - &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); - switch (pix->field) { - case V4L2_FIELD_NONE: case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + pix->height /= 2; + source->height /= 2; + break; + case V4L2_FIELD_NONE: case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: @@ -188,11 +251,27 @@ static int __rvin_try_format(struct rvin_dev *vin, break; } + /* If source can't match format try if VIN can scale */ + if (source->width != rwidth || source->height != rheight) + rvin_scale_try(vin, pix, rwidth, rheight); + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, + &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); + pix->bytesperline = max_t(u32, pix->bytesperline, rvin_format_bytesperline(pix)); pix->sizeimage = max_t(u32, pix->sizeimage, rvin_format_sizeimage(pix)); + if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) { + vin_err(vin, "pixel format XBGR32 not supported on M1\n"); + return -EINVAL; + } + vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", rwidth, rheight, pix->width, pix->height, pix->bytesperline, pix->sizeimage); @@ -219,7 +298,7 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv, struct rvin_source_fmt source; return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, - &source); + &source); } static int rvin_s_fmt_vid_cap(struct file *file, void *priv, @@ -233,7 +312,7 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, return -EBUSY; ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &source); + &source); if (ret) return ret; @@ -242,6 +321,8 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, vin->format = f->fmt.pix; + rvin_reset_crop_compose(vin); + return 0; } @@ -334,8 +415,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->crop = s->r = r; vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", - r.width, r.height, r.left, r.top, - vin->source.width, vin->source.height); + r.width, r.height, r.left, r.top, + vin->source.width, vin->source.height); break; case V4L2_SEL_TGT_COMPOSE: /* Make sure compose rect fits inside output format */ @@ -359,8 +440,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->compose = s->r = r; vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", - r.width, r.height, r.left, r.top, - vin->format.width, vin->format.height); + r.width, r.height, r.left, r.top, + vin->format.width, vin->format.height); break; default: return -EINVAL; @@ -381,7 +462,7 @@ static int rvin_cropcap(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return v4l2_subdev_call(sd, video, cropcap, crop); + return v4l2_subdev_call(sd, video, g_pixelaspect, &crop->pixelaspect); } static int rvin_enum_input(struct file *file, void *priv, @@ -433,35 +514,14 @@ static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) { struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret = v4l2_subdev_call(sd, video, s_std, a); + int ret; + ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a); if (ret < 0) return ret; /* Changing the standard will change the width/height */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret) { - vin_err(vin, "Failed to get initial format\n"); - return ret; - } - - vin->format.width = mf->width; - vin->format.height = mf->height; - - vin->crop.top = vin->crop.left = 0; - vin->crop.width = mf->width; - vin->crop.height = mf->height; - - vin->compose.top = vin->compose.left = 0; - vin->compose.width = mf->width; - vin->compose.height = mf->height; - - return 0; + return rvin_reset_format(vin); } static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -483,14 +543,14 @@ static int rvin_subscribe_event(struct v4l2_fh *fh, } static int rvin_enum_dv_timings(struct file *file, void *priv_fh, - struct v4l2_enum_dv_timings *timings) + struct v4l2_enum_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); int pad, ret; pad = timings->pad; - timings->pad = vin->src_pad_idx; + timings->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -500,52 +560,51 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, } static int rvin_s_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int err; - - err = v4l2_subdev_call(sd, - video, s_dv_timings, timings); - if (!err) { - vin->source.width = timings->bt.width; - vin->source.height = timings->bt.height; - vin->format.width = timings->bt.width; - vin->format.height = timings->bt.height; - } - return err; + int ret; + + ret = v4l2_subdev_call(sd, video, s_dv_timings, timings); + if (ret) + return ret; + + vin->source.width = timings->bt.width; + vin->source.height = timings->bt.height; + vin->format.width = timings->bt.width; + vin->format.height = timings->bt.height; + + return 0; } static int rvin_g_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, - video, g_dv_timings, timings); + return v4l2_subdev_call(sd, video, g_dv_timings, timings); } static int rvin_query_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, - video, query_dv_timings, timings); + return v4l2_subdev_call(sd, video, query_dv_timings, timings); } static int rvin_dv_timings_cap(struct file *file, void *priv_fh, - struct v4l2_dv_timings_cap *cap) + struct v4l2_dv_timings_cap *cap) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); int pad, ret; pad = cap->pad; - cap->pad = vin->src_pad_idx; + cap->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -554,6 +613,44 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, return ret; } +static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int input, ret; + + if (edid->pad) + return -EINVAL; + + input = edid->pad; + edid->pad = vin->sink_pad_idx; + + ret = v4l2_subdev_call(sd, pad, get_edid, edid); + + edid->pad = input; + + return ret; +} + +static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int input, ret; + + if (edid->pad) + return -EINVAL; + + input = edid->pad; + edid->pad = vin->sink_pad_idx; + + ret = v4l2_subdev_call(sd, pad, set_edid, edid); + + edid->pad = input; + + return ret; +} + static const struct v4l2_ioctl_ops rvin_ioctl_ops = { .vidioc_querycap = rvin_querycap, .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, @@ -576,6 +673,9 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = { .vidioc_s_dv_timings = rvin_s_dv_timings, .vidioc_query_dv_timings = rvin_query_dv_timings, + .vidioc_g_edid = rvin_g_edid, + .vidioc_s_edid = rvin_s_edid, + .vidioc_querystd = rvin_querystd, .vidioc_g_std = rvin_g_std, .vidioc_s_std = rvin_s_std, @@ -767,16 +867,9 @@ static void rvin_notify(struct v4l2_subdev *sd, int rvin_v4l2_probe(struct rvin_dev *vin) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; struct video_device *vdev = &vin->vdev; struct v4l2_subdev *sd = vin_to_source(vin); -#if defined(CONFIG_MEDIA_CONTROLLER) - int pad_idx; -#endif - int ret; + int pad_idx, ret; v4l2_set_subdev_hostdata(sd, vin); @@ -823,41 +916,23 @@ int rvin_v4l2_probe(struct rvin_dev *vin) V4L2_CAP_READWRITE; vin->src_pad_idx = 0; -#if defined(CONFIG_MEDIA_CONTROLLER) for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) - if (sd->entity.pads[pad_idx].flags - == MEDIA_PAD_FL_SOURCE) + if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE) break; if (pad_idx >= sd->entity.num_pads) return -EINVAL; vin->src_pad_idx = pad_idx; -#endif - fmt.pad = vin->src_pad_idx; - /* Try to improve our guess of a reasonable window format */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret) { - vin_err(vin, "Failed to get initial format\n"); - return ret; - } + vin->sink_pad_idx = 0; + for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) + if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) { + vin->sink_pad_idx = pad_idx; + break; + } - /* Set default format */ - vin->format.width = mf->width; - vin->format.height = mf->height; - vin->format.colorspace = mf->colorspace; - vin->format.field = mf->field; vin->format.pixelformat = RVIN_DEFAULT_FORMAT; - - - /* Set initial crop and compose */ - vin->crop.top = vin->crop.left = 0; - vin->crop.width = mf->width; - vin->crop.height = mf->height; - - vin->compose.top = vin->compose.left = 0; - vin->compose.width = mf->width; - vin->compose.height = mf->height; + rvin_reset_format(vin); ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 31ad39a39937..727e215c0871 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -30,9 +30,9 @@ #define HW_BUFFER_MASK 0x7f enum chip_id { - RCAR_GEN2, RCAR_H1, RCAR_M1, + RCAR_GEN2, }; /** @@ -50,12 +50,10 @@ enum rvin_dma_state { /** * struct rvin_source_fmt - Source information - * @code: Media bus format from source * @width: Width from source * @height: Height from source */ struct rvin_source_fmt { - u32 code; u32 width; u32 height; }; @@ -70,12 +68,19 @@ struct rvin_video_format { u8 bpp; }; +/** + * struct rvin_graph_entity - Video endpoint from async framework + * @asd: sub-device descriptor for async framework + * @subdev: subdevice matched using async framework + * @code: Media bus format from source + * @mbus_cfg: Media bus format from DT + */ struct rvin_graph_entity { - struct device_node *node; - struct media_entity *entity; - struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; + + u32 code; + struct v4l2_mbus_config mbus_cfg; }; /** @@ -83,14 +88,14 @@ struct rvin_graph_entity { * @dev: (OF) device * @base: device I/O register space remapped to virtual memory * @chip: type of VIN chip - * @mbus_cfg media bus configuration * * @vdev: V4L2 video device associated with VIN * @v4l2_dev: V4L2 device * @src_pad_idx: source pad index for media controller drivers + * @sink_pad_idx: sink pad index for media controller drivers * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @entity: entity in the DT for subdevice + * @digital: entity in the DT for local digital subdevice * * @lock: protects @queue * @queue: vb2 buffers queue @@ -113,14 +118,14 @@ struct rvin_dev { struct device *dev; void __iomem *base; enum chip_id chip; - struct v4l2_mbus_config mbus_cfg; struct video_device vdev; struct v4l2_device v4l2_dev; int src_pad_idx; + int sink_pad_idx; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity entity; + struct rvin_graph_entity digital; struct mutex lock; struct vb2_queue queue; @@ -139,7 +144,7 @@ struct rvin_dev { struct v4l2_rect compose; }; -#define vin_to_source(vin) vin->entity.subdev +#define vin_to_source(vin) vin->digital.subdev /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 16782ceb29c3..d1746ecc645d 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1183,7 +1183,7 @@ static void jpu_stop_streaming(struct vb2_queue *vq) } } -static struct vb2_ops jpu_qops = { +static const struct vb2_ops jpu_qops = { .queue_setup = jpu_queue_setup, .buf_prepare = jpu_buf_prepare, .buf_queue = jpu_buf_queue, diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 391dd7a7b362..62c0dec30b59 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -138,7 +138,7 @@ static void g2d_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } -static struct vb2_ops g2d_qops = { +static const struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, .buf_queue = g2d_buf_queue, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 785e6936c881..52dc7941db65 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -537,6 +537,7 @@ static const u32 fourcc_to_dwngrd_schema_id[] = { static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc) { int i; + for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) { if (fourcc_to_dwngrd_schema_id[i] == fourcc) return i; @@ -1246,17 +1247,18 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) { - strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder", + strlcpy(cap->driver, S5P_JPEG_M2M_NAME, sizeof(cap->driver)); strlcpy(cap->card, S5P_JPEG_M2M_NAME " encoder", sizeof(cap->card)); } else { - strlcpy(cap->driver, S5P_JPEG_M2M_NAME " decoder", + strlcpy(cap->driver, S5P_JPEG_M2M_NAME, sizeof(cap->driver)); strlcpy(cap->card, S5P_JPEG_M2M_NAME " decoder", sizeof(cap->card)); } - cap->bus_info[0] = 0; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->jpeg->dev)); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; @@ -1273,7 +1275,8 @@ static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, if (num == f->index) break; /* Correct type but haven't reached our index yet, - * just increment per-type index */ + * just increment per-type index + */ ++num; } } @@ -1349,6 +1352,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix->bytesperline = 0; if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { u32 bpl = q_data->w; + if (q_data->fmt->colplanes == 1) bpl = (bpl * q_data->fmt->depth) >> 3; pix->bytesperline = bpl; @@ -1374,6 +1378,7 @@ static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; + if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag && fmt->flags & ctx->jpeg->variant->fmt_ver_flag) { @@ -1431,7 +1436,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, return -EINVAL; /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported */ + * if any of the dimensions is unsupported + */ if (q_type == FMT_TYPE_OUTPUT) jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, 0, @@ -2489,6 +2495,7 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) if (ctx->mode == S5P_JPEG_DECODE && vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { struct s5p_jpeg_q_data tmp, *q_data; + ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp, (unsigned long)vb2_plane_vaddr(vb, 0), min((unsigned long)ctx->out_q.size, @@ -2538,7 +2545,7 @@ static void s5p_jpeg_stop_streaming(struct vb2_queue *q) pm_runtime_put(ctx->jpeg->dev); } -static struct vb2_ops s5p_jpeg_qops = { +static const struct vb2_ops s5p_jpeg_qops = { .queue_setup = s5p_jpeg_queue_setup, .buf_prepare = s5p_jpeg_buf_prepare, .buf_queue = s5p_jpeg_buf_queue, @@ -2996,27 +3003,11 @@ static int s5p_jpeg_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP -static int s5p_jpeg_suspend(struct device *dev) -{ - if (pm_runtime_suspended(dev)) - return 0; - - return s5p_jpeg_runtime_suspend(dev); -} - -static int s5p_jpeg_resume(struct device *dev) -{ - if (pm_runtime_suspended(dev)) - return 0; - - return s5p_jpeg_runtime_resume(dev); -} -#endif - static const struct dev_pm_ops s5p_jpeg_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume) - SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, + NULL) }; static struct s5p_jpeg_variant s5p_jpeg_drvdata = { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e3f104fafd0a..0a5b8f5e011e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -153,7 +153,7 @@ static void s5p_mfc_watchdog(unsigned long arg) * error. Now it is time to kill all instances and * reset the MFC. */ mfc_err("Time out during waiting for HW\n"); - queue_work(dev->watchdog_workqueue, &dev->watchdog_work); + schedule_work(&dev->watchdog_work); } dev->watchdog_timer.expires = jiffies + msecs_to_jiffies(MFC_WATCHDOG_INTERVAL); @@ -494,7 +494,6 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); s5p_mfc_clock_off(); wake_up_dev(dev, reason, err); - return; } /* Header parsing interrupt handling */ @@ -759,7 +758,6 @@ static int s5p_mfc_open(struct file *file) /* Allocate memory for context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { - mfc_err("Not enough memory\n"); ret = -ENOMEM; goto err_alloc; } @@ -776,7 +774,7 @@ static int s5p_mfc_open(struct file *file) while (dev->ctx[ctx->num]) { ctx->num++; if (ctx->num >= MFC_NUM_CONTEXTS) { - mfc_err("Too many open contexts\n"); + mfc_debug(2, "Too many open contexts\n"); ret = -EBUSY; goto err_no_ctx; } @@ -924,39 +922,50 @@ static int s5p_mfc_release(struct file *file) struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data); struct s5p_mfc_dev *dev = ctx->dev; + /* if dev is null, do cleanup that doesn't need dev */ mfc_debug_enter(); - mutex_lock(&dev->mfc_mutex); + if (dev) + mutex_lock(&dev->mfc_mutex); s5p_mfc_clock_on(); vb2_queue_release(&ctx->vq_src); vb2_queue_release(&ctx->vq_dst); - /* Mark context as idle */ - clear_work_bit_irqsave(ctx); - /* If instance was initialised and not yet freed, - * return instance and free resources */ - if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) { - mfc_debug(2, "Has to free instance\n"); - s5p_mfc_close_mfc_inst(dev, ctx); - } - /* hardware locking scheme */ - if (dev->curr_ctx == ctx->num) - clear_bit(0, &dev->hw_lock); - dev->num_inst--; - if (dev->num_inst == 0) { - mfc_debug(2, "Last instance\n"); - s5p_mfc_deinit_hw(dev); - del_timer_sync(&dev->watchdog_timer); - if (s5p_mfc_power_off() < 0) - mfc_err("Power off failed\n"); + if (dev) { + /* Mark context as idle */ + clear_work_bit_irqsave(ctx); + /* + * If instance was initialised and not yet freed, + * return instance and free resources + */ + if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) { + mfc_debug(2, "Has to free instance\n"); + s5p_mfc_close_mfc_inst(dev, ctx); + } + /* hardware locking scheme */ + if (dev->curr_ctx == ctx->num) + clear_bit(0, &dev->hw_lock); + dev->num_inst--; + if (dev->num_inst == 0) { + mfc_debug(2, "Last instance\n"); + s5p_mfc_deinit_hw(dev); + del_timer_sync(&dev->watchdog_timer); + if (s5p_mfc_power_off() < 0) + mfc_err("Power off failed\n"); + } } mfc_debug(2, "Shutting down clock\n"); s5p_mfc_clock_off(); - dev->ctx[ctx->num] = NULL; + if (dev) + dev->ctx[ctx->num] = NULL; s5p_mfc_dec_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); + /* vdev is gone if dev is null */ + if (dev) + v4l2_fh_exit(&ctx->fh); kfree(ctx); mfc_debug_leave(); - mutex_unlock(&dev->mfc_mutex); + if (dev) + mutex_unlock(&dev->mfc_mutex); + return 0; } @@ -1158,10 +1167,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->variant = mfc_get_drv_data(pdev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get io resource\n"); - return -ENOENT; - } dev->regs_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); @@ -1241,7 +1246,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); dev->hw_lock = 0; - dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME); INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker); atomic_set(&dev->watchdog_cnt, 0); init_timer(&dev->watchdog_timer); @@ -1298,12 +1302,28 @@ err_dma: static int s5p_mfc_remove(struct platform_device *pdev) { struct s5p_mfc_dev *dev = platform_get_drvdata(pdev); + struct s5p_mfc_ctx *ctx; + int i; v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name); + /* + * Clear ctx dev pointer to avoid races between s5p_mfc_remove() + * and s5p_mfc_release() and s5p_mfc_release() accessing ctx->dev + * after s5p_mfc_remove() is run during unbind. + */ + mutex_lock(&dev->mfc_mutex); + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + ctx = dev->ctx[i]; + if (!ctx) + continue; + /* clear ctx->dev */ + ctx->dev = NULL; + } + mutex_unlock(&dev->mfc_mutex); + del_timer_sync(&dev->watchdog_timer); - flush_workqueue(dev->watchdog_workqueue); - destroy_workqueue(dev->watchdog_workqueue); + flush_work(&dev->watchdog_work); video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 373e346fce3e..46b99f28cbd7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -292,7 +292,9 @@ struct s5p_mfc_priv_buf { * @warn_start: hardware error code from which warnings start * @mfc_ops: ops structure holding HW operation function pointers * @mfc_cmds: cmd structure holding HW commands function pointers + * @mfc_regs: structure holding MFC registers * @fw_ver: loaded firmware sub-version + * risc_on: flag indicates RISC is on or off * */ struct s5p_mfc_dev { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 47c997d9e8cb..52081ddc9bf2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -776,11 +776,12 @@ static int vidioc_g_crop(struct file *file, void *priv, u32 left, right, top, bottom; if (ctx->state != MFCINST_HEAD_PARSED && - ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING - && ctx->state != MFCINST_FINISHED) { - mfc_err("Cannont set crop\n"); - return -EINVAL; - } + ctx->state != MFCINST_RUNNING && + ctx->state != MFCINST_FINISHING && + ctx->state != MFCINST_FINISHED) { + mfc_err("Can not get crop information\n"); + return -EINVAL; + } if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) { left = s5p_mfc_hw_call(dev->mfc_ops, get_crop_info_h, ctx); right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT; diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig deleted file mode 100644 index 697aaed42486..000000000000 --- a/drivers/media/platform/s5p-tv/Kconfig +++ /dev/null @@ -1,88 +0,0 @@ -# drivers/media/platform/s5p-tv/Kconfig -# -# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. -# http://www.samsung.com/ -# Tomasz Stanislawski <t.stanislaws@samsung.com> -# -# Licensed under GPL - -config VIDEO_SAMSUNG_S5P_TV - bool "Samsung TV driver for S5P platform" - depends on PM - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - default n - ---help--- - Say Y here to enable selecting the TV output devices for - Samsung S5P platform. - -if VIDEO_SAMSUNG_S5P_TV - -config VIDEO_SAMSUNG_S5P_HDMI - tristate "Samsung HDMI Driver" - depends on VIDEO_V4L2 - depends on I2C - depends on VIDEO_SAMSUNG_S5P_TV - select VIDEO_SAMSUNG_S5P_HDMIPHY - help - Say Y here if you want support for the HDMI output - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an auxiliary driver, that exposes a V4L2 - subdev for use by other drivers. This driver requires - hdmiphy driver to work correctly. - -config VIDEO_SAMSUNG_S5P_HDMI_DEBUG - bool "Enable debug for HDMI Driver" - depends on VIDEO_SAMSUNG_S5P_HDMI - default n - help - Enables debugging for HDMI driver. - -config VIDEO_SAMSUNG_S5P_HDMIPHY - tristate "Samsung HDMIPHY Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && I2C - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the physical HDMI - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an I2C driver, that exposes a V4L2 - subdev for use by other drivers. - -config VIDEO_SAMSUNG_S5P_SII9234 - tristate "Samsung SII9234 Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && I2C - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the MHL interface - in S5P Samsung SoC. The driver can be compiled - as module. It is an I2C driver, that exposes a V4L2 - subdev for use by other drivers. - -config VIDEO_SAMSUNG_S5P_SDO - tristate "Samsung Analog TV Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the analog TV output - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an auxiliary driver, that exposes a V4L2 - subdev for use by other drivers. This driver requires - hdmiphy driver to work correctly. - -config VIDEO_SAMSUNG_S5P_MIXER - tristate "Samsung Mixer and Video Processor Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on VIDEO_SAMSUNG_S5P_TV - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - help - Say Y here if you want support for the Mixer in Samsung S5P SoCs. - This device produce image data to one of output interfaces. - -config VIDEO_SAMSUNG_S5P_MIXER_DEBUG - bool "Enable debug for Mixer Driver" - depends on VIDEO_SAMSUNG_S5P_MIXER - default n - help - Enables debugging for Mixer driver. - -endif # VIDEO_SAMSUNG_S5P_TV diff --git a/drivers/media/platform/s5p-tv/Makefile b/drivers/media/platform/s5p-tv/Makefile deleted file mode 100644 index 7cd47902e269..000000000000 --- a/drivers/media/platform/s5p-tv/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# drivers/media/platform/samsung/tvout/Makefile -# -# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. -# http://www.samsung.com/ -# Tomasz Stanislawski <t.stanislaws@samsung.com> -# -# Licensed under GPL - -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o -s5p-hdmiphy-y += hdmiphy_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o -s5p-sii9234-y += sii9234_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o -s5p-hdmi-y += hdmi_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o -s5p-sdo-y += sdo_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o -s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o - diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c deleted file mode 100644 index e71b13e40f59..000000000000 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * Samsung HDMI interface driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#define pr_fmt(fmt) "s5p-tv (hdmi_drv): " fmt - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG -#define DEBUG -#endif - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/i2c.h> -#include <linux/platform_device.h> -#include <media/v4l2-subdev.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <linux/bug.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> -#include <linux/v4l2-dv-timings.h> - -#include <linux/platform_data/media/s5p_hdmi.h> -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-dv-timings.h> - -#include "regs-hdmi.h" - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung HDMI"); -MODULE_LICENSE("GPL"); - -struct hdmi_pulse { - u32 beg; - u32 end; -}; - -struct hdmi_timings { - struct hdmi_pulse hact; - u32 hsyn_pol; /* 0 - high, 1 - low */ - struct hdmi_pulse hsyn; - u32 interlaced; - struct hdmi_pulse vact[2]; - u32 vsyn_pol; /* 0 - high, 1 - low */ - u32 vsyn_off; - struct hdmi_pulse vsyn[2]; -}; - -struct hdmi_resources { - struct clk *hdmi; - struct clk *sclk_hdmi; - struct clk *sclk_pixel; - struct clk *sclk_hdmiphy; - struct clk *hdmiphy; - struct regulator_bulk_data *regul_bulk; - int regul_count; -}; - -struct hdmi_device { - /** base address of HDMI registers */ - void __iomem *regs; - /** HDMI interrupt */ - unsigned int irq; - /** pointer to device parent */ - struct device *dev; - /** subdev generated by HDMI device */ - struct v4l2_subdev sd; - /** V4L2 device structure */ - struct v4l2_device v4l2_dev; - /** subdev of HDMIPHY interface */ - struct v4l2_subdev *phy_sd; - /** subdev of MHL interface */ - struct v4l2_subdev *mhl_sd; - /** configuration of current graphic mode */ - const struct hdmi_timings *cur_conf; - /** flag indicating that timings are dirty */ - int cur_conf_dirty; - /** current timings */ - struct v4l2_dv_timings cur_timings; - /** other resources */ - struct hdmi_resources res; -}; - -static const struct platform_device_id hdmi_driver_types[] = { - { - .name = "s5pv210-hdmi", - }, { - .name = "exynos4-hdmi", - }, { - /* end node */ - } -}; - -static const struct v4l2_subdev_ops hdmi_sd_ops; - -static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct hdmi_device, sd); -} - -static inline -void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value) -{ - writel(value, hdev->regs + reg_id); -} - -static inline -void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask) -{ - u32 old = readl(hdev->regs + reg_id); - value = (value & mask) | (old & ~mask); - writel(value, hdev->regs + reg_id); -} - -static inline -void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) -{ - writeb(value, hdev->regs + reg_id); -} - -static inline -void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value) -{ - switch (n) { - default: - writeb(value >> 24, hdev->regs + reg_id + 12); - case 3: - writeb(value >> 16, hdev->regs + reg_id + 8); - case 2: - writeb(value >> 8, hdev->regs + reg_id + 4); - case 1: - writeb(value >> 0, hdev->regs + reg_id + 0); - } -} - -static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) -{ - return readl(hdev->regs + reg_id); -} - -static irqreturn_t hdmi_irq_handler(int irq, void *dev_data) -{ - struct hdmi_device *hdev = dev_data; - u32 intc_flag; - - (void)irq; - intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG); - /* clearing flags for HPD plug/unplug */ - if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { - pr_info("unplugged\n"); - hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_UNPLUG); - } - if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { - pr_info("plugged\n"); - hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_PLUG); - } - - return IRQ_HANDLED; -} - -static void hdmi_reg_init(struct hdmi_device *hdev) -{ - /* enable HPD interrupts */ - hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | - HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - /* choose DVI mode */ - hdmi_write_mask(hdev, HDMI_MODE_SEL, - HDMI_MODE_DVI_EN, HDMI_MODE_MASK); - hdmi_write_mask(hdev, HDMI_CON_2, ~0, - HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN); - /* disable bluescreen */ - hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - /* choose bluescreen (fecal) color */ - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12); - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34); - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56); -} - -static void hdmi_timing_apply(struct hdmi_device *hdev, - const struct hdmi_timings *t) -{ - /* setting core registers */ - hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg); - hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3, - (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg); - hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol); - hdmi_writebn(hdev, HDMI_V_BLANK_0, 3, - (t->vact[0].beg << 11) | t->vact[0].end); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3, - (t->vsyn[0].beg << 12) | t->vsyn[0].end); - if (t->interlaced) { - u32 vsyn_trans = t->hsyn.beg + t->vsyn_off; - - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1); - hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, - (t->hact.end << 12) | t->vact[1].end); - hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3, - (t->vact[1].end << 11) | t->vact[1].beg); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3, - (t->vsyn[1].beg << 12) | t->vsyn[1].end); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3, - (vsyn_trans << 12) | vsyn_trans); - } else { - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0); - hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, - (t->hact.end << 12) | t->vact[0].end); - } - - /* Timing generator registers */ - hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end); - hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg); - hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg); - hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg); - hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2, - t->vact[0].end - t->vact[0].beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg); - if (t->interlaced) { - hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN); - hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end); - hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end); - hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg); - } else { - hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN); - hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end); - } -} - -static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) -{ - struct device *dev = hdmi_dev->dev; - const struct hdmi_timings *conf = hdmi_dev->cur_conf; - int ret; - - dev_dbg(dev, "%s\n", __func__); - - /* skip if conf is already synchronized with HW */ - if (!hdmi_dev->cur_conf_dirty) - return 0; - - /* reset hdmiphy */ - hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); - mdelay(10); - hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); - mdelay(10); - - /* configure timings */ - ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_timings, - &hdmi_dev->cur_timings); - if (ret) { - dev_err(dev, "failed to set timings\n"); - return ret; - } - - /* resetting HDMI core */ - hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); - mdelay(10); - hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); - mdelay(10); - - hdmi_reg_init(hdmi_dev); - - /* setting core registers */ - hdmi_timing_apply(hdmi_dev, conf); - - hdmi_dev->cur_conf_dirty = 0; - - return 0; -} - -static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) -{ -#define DUMPREG(reg_id) \ - dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \ - readl(hdev->regs + reg_id)) - - dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix); - DUMPREG(HDMI_INTC_FLAG); - DUMPREG(HDMI_INTC_CON); - DUMPREG(HDMI_HPD_STATUS); - DUMPREG(HDMI_PHY_RSTOUT); - DUMPREG(HDMI_PHY_VPLL); - DUMPREG(HDMI_PHY_CMU); - DUMPREG(HDMI_CORE_RSTOUT); - - dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix); - DUMPREG(HDMI_CON_0); - DUMPREG(HDMI_CON_1); - DUMPREG(HDMI_CON_2); - DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS); - DUMPREG(HDMI_STATUS_EN); - DUMPREG(HDMI_HPD); - DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_HPD_GEN); - DUMPREG(HDMI_DC_CONTROL); - DUMPREG(HDMI_VIDEO_PATTERN_GEN); - - dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix); - DUMPREG(HDMI_H_BLANK_0); - DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V_BLANK_0); - DUMPREG(HDMI_V_BLANK_1); - DUMPREG(HDMI_V_BLANK_2); - DUMPREG(HDMI_H_V_LINE_0); - DUMPREG(HDMI_H_V_LINE_1); - DUMPREG(HDMI_H_V_LINE_2); - DUMPREG(HDMI_VSYNC_POL); - DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F_0); - DUMPREG(HDMI_V_BLANK_F_1); - DUMPREG(HDMI_V_BLANK_F_2); - DUMPREG(HDMI_H_SYNC_GEN_0); - DUMPREG(HDMI_H_SYNC_GEN_1); - DUMPREG(HDMI_H_SYNC_GEN_2); - DUMPREG(HDMI_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V_SYNC_GEN_3_2); - - dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix); - DUMPREG(HDMI_TG_CMD); - DUMPREG(HDMI_TG_H_FSZ_L); - DUMPREG(HDMI_TG_H_FSZ_H); - DUMPREG(HDMI_TG_HACT_ST_L); - DUMPREG(HDMI_TG_HACT_ST_H); - DUMPREG(HDMI_TG_HACT_SZ_L); - DUMPREG(HDMI_TG_HACT_SZ_H); - DUMPREG(HDMI_TG_V_FSZ_L); - DUMPREG(HDMI_TG_V_FSZ_H); - DUMPREG(HDMI_TG_VSYNC_L); - DUMPREG(HDMI_TG_VSYNC_H); - DUMPREG(HDMI_TG_VSYNC2_L); - DUMPREG(HDMI_TG_VSYNC2_H); - DUMPREG(HDMI_TG_VACT_ST_L); - DUMPREG(HDMI_TG_VACT_ST_H); - DUMPREG(HDMI_TG_VACT_SZ_L); - DUMPREG(HDMI_TG_VACT_SZ_H); - DUMPREG(HDMI_TG_FIELD_CHG_L); - DUMPREG(HDMI_TG_FIELD_CHG_H); - DUMPREG(HDMI_TG_VACT_ST2_L); - DUMPREG(HDMI_TG_VACT_ST2_H); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); -#undef DUMPREG -} - -static const struct hdmi_timings hdmi_timings_480p = { - .hact = { .beg = 138, .end = 858 }, - .hsyn_pol = 1, - .hsyn = { .beg = 16, .end = 16 + 62 }, - .interlaced = 0, - .vact[0] = { .beg = 42 + 3, .end = 522 + 3 }, - .vsyn_pol = 1, - .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3}, -}; - -static const struct hdmi_timings hdmi_timings_576p50 = { - .hact = { .beg = 144, .end = 864 }, - .hsyn_pol = 1, - .hsyn = { .beg = 12, .end = 12 + 64 }, - .interlaced = 0, - .vact[0] = { .beg = 44 + 5, .end = 620 + 5 }, - .vsyn_pol = 1, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_720p60 = { - .hact = { .beg = 370, .end = 1650 }, - .hsyn_pol = 0, - .hsyn = { .beg = 110, .end = 110 + 40 }, - .interlaced = 0, - .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_720p50 = { - .hact = { .beg = 700, .end = 1980 }, - .hsyn_pol = 0, - .hsyn = { .beg = 440, .end = 440 + 40 }, - .interlaced = 0, - .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_1080p24 = { - .hact = { .beg = 830, .end = 2750 }, - .hsyn_pol = 0, - .hsyn = { .beg = 638, .end = 638 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -static const struct hdmi_timings hdmi_timings_1080p60 = { - .hact = { .beg = 280, .end = 2200 }, - .hsyn_pol = 0, - .hsyn = { .beg = 88, .end = 88 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -static const struct hdmi_timings hdmi_timings_1080i60 = { - .hact = { .beg = 280, .end = 2200 }, - .hsyn_pol = 0, - .hsyn = { .beg = 88, .end = 88 + 44 }, - .interlaced = 1, - .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, - .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, - .vsyn_pol = 0, - .vsyn_off = 1100, - .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, - .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, -}; - -static const struct hdmi_timings hdmi_timings_1080i50 = { - .hact = { .beg = 720, .end = 2640 }, - .hsyn_pol = 0, - .hsyn = { .beg = 528, .end = 528 + 44 }, - .interlaced = 1, - .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, - .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, - .vsyn_pol = 0, - .vsyn_off = 1320, - .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, - .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, -}; - -static const struct hdmi_timings hdmi_timings_1080p50 = { - .hact = { .beg = 720, .end = 2640 }, - .hsyn_pol = 0, - .hsyn = { .beg = 528, .end = 528 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -/* default hdmi_timings index of the timings configured on probe */ -#define HDMI_DEFAULT_TIMINGS_IDX (0) - -static const struct { - bool reduced_fps; - const struct v4l2_dv_timings dv_timings; - const struct hdmi_timings *hdmi_timings; -} hdmi_timings[] = { - { false, V4L2_DV_BT_CEA_720X480P59_94, &hdmi_timings_480p }, - { false, V4L2_DV_BT_CEA_720X576P50, &hdmi_timings_576p50 }, - { false, V4L2_DV_BT_CEA_1280X720P50, &hdmi_timings_720p50 }, - { true, V4L2_DV_BT_CEA_1280X720P60, &hdmi_timings_720p60 }, - { false, V4L2_DV_BT_CEA_1920X1080P24, &hdmi_timings_1080p24 }, - { false, V4L2_DV_BT_CEA_1920X1080P30, &hdmi_timings_1080p60 }, - { false, V4L2_DV_BT_CEA_1920X1080P50, &hdmi_timings_1080p50 }, - { false, V4L2_DV_BT_CEA_1920X1080I50, &hdmi_timings_1080i50 }, - { false, V4L2_DV_BT_CEA_1920X1080I60, &hdmi_timings_1080i60 }, - { false, V4L2_DV_BT_CEA_1920X1080P60, &hdmi_timings_1080p60 }, -}; - -static int hdmi_streamon(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - int ret, tries; - - dev_dbg(dev, "%s\n", __func__); - - ret = hdmi_conf_apply(hdev); - if (ret) - return ret; - - ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); - if (ret) - return ret; - - /* waiting for HDMIPHY's PLL to get to steady state */ - for (tries = 100; tries; --tries) { - u32 val = hdmi_read(hdev, HDMI_PHY_STATUS); - if (val & HDMI_PHY_STATUS_READY) - break; - mdelay(1); - } - /* steady state not achieved */ - if (tries == 0) { - dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "hdmiphy - s_stream"); - return -EIO; - } - - /* starting MHL */ - ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1); - if (hdev->mhl_sd && ret) { - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "mhl - s_stream"); - return -EIO; - } - - /* hdmiphy clock is used for HDMI in streaming mode */ - clk_disable(res->sclk_hdmi); - clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy); - clk_enable(res->sclk_hdmi); - - /* enable HDMI and timing generator */ - hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN); - hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN); - hdmi_dumpregs(hdev, "streamon"); - return 0; -} - -static int hdmi_streamoff(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - - dev_dbg(dev, "%s\n", __func__); - - hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN); - hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN); - - /* pixel(vpll) clock is used for HDMI in config mode */ - clk_disable(res->sclk_hdmi); - clk_set_parent(res->sclk_hdmi, res->sclk_pixel); - clk_enable(res->sclk_hdmi); - - v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0); - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - - hdmi_dumpregs(hdev, "streamoff"); - return 0; -} - -static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - struct device *dev = hdev->dev; - - dev_dbg(dev, "%s(%d)\n", __func__, enable); - if (enable) - return hdmi_streamon(hdev); - return hdmi_streamoff(hdev); -} - -static int hdmi_resource_poweron(struct hdmi_resources *res) -{ - int ret; - - /* turn HDMI power on */ - ret = regulator_bulk_enable(res->regul_count, res->regul_bulk); - if (ret < 0) - return ret; - /* power-on hdmi physical interface */ - clk_enable(res->hdmiphy); - /* use VPP as parent clock; HDMIPHY is not working yet */ - clk_set_parent(res->sclk_hdmi, res->sclk_pixel); - /* turn clocks on */ - clk_enable(res->sclk_hdmi); - - return 0; -} - -static void hdmi_resource_poweroff(struct hdmi_resources *res) -{ - /* turn clocks off */ - clk_disable(res->sclk_hdmi); - /* power-off hdmiphy */ - clk_disable(res->hdmiphy); - /* turn HDMI power off */ - regulator_bulk_disable(res->regul_count, res->regul_bulk); -} - -static int hdmi_s_power(struct v4l2_subdev *sd, int on) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret; - - if (on) - ret = pm_runtime_get_sync(hdev->dev); - else - ret = pm_runtime_put_sync(hdev->dev); - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -static int hdmi_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - struct device *dev = hdev->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) - if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, - timings, 0, false)) - break; - if (i == ARRAY_SIZE(hdmi_timings)) { - dev_err(dev, "timings not supported\n"); - return -EINVAL; - } - hdev->cur_conf = hdmi_timings[i].hdmi_timings; - hdev->cur_conf_dirty = 1; - hdev->cur_timings = *timings; - if (!hdmi_timings[i].reduced_fps) - hdev->cur_timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; - return 0; -} - -static int hdmi_g_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - *timings = sd_to_hdmi_dev(sd)->cur_timings; - return 0; -} - -static int hdmi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - const struct hdmi_timings *t = hdev->cur_conf; - - dev_dbg(hdev->dev, "%s\n", __func__); - if (!hdev->cur_conf) - return -EINVAL; - if (format->pad) - return -EINVAL; - - memset(fmt, 0, sizeof(*fmt)); - fmt->width = t->hact.end - t->hact.beg; - fmt->height = t->vact[0].end - t->vact[0].beg; - fmt->code = MEDIA_BUS_FMT_FIXED; /* means RGB888 */ - fmt->colorspace = V4L2_COLORSPACE_SRGB; - if (t->interlaced) { - fmt->field = V4L2_FIELD_INTERLACED; - fmt->height *= 2; - } else { - fmt->field = V4L2_FIELD_NONE; - } - return 0; -} - -static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) -{ - if (timings->pad != 0) - return -EINVAL; - if (timings->index >= ARRAY_SIZE(hdmi_timings)) - return -EINVAL; - timings->timings = hdmi_timings[timings->index].dv_timings; - if (!hdmi_timings[timings->index].reduced_fps) - timings->timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; - return 0; -} - -static int hdmi_dv_timings_cap(struct v4l2_subdev *sd, - struct v4l2_dv_timings_cap *cap) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - - if (cap->pad != 0) - return -EINVAL; - - /* Let the phy fill in the pixelclock range */ - v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap); - cap->type = V4L2_DV_BT_656_1120; - cap->bt.min_width = 720; - cap->bt.max_width = 1920; - cap->bt.min_height = 480; - cap->bt.max_height = 1080; - cap->bt.standards = V4L2_DV_BT_STD_CEA861; - cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | - V4L2_DV_BT_CAP_PROGRESSIVE; - return 0; -} - -static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { - .s_power = hdmi_s_power, -}; - -static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { - .s_dv_timings = hdmi_s_dv_timings, - .g_dv_timings = hdmi_g_dv_timings, - .s_stream = hdmi_s_stream, -}; - -static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = { - .enum_dv_timings = hdmi_enum_dv_timings, - .dv_timings_cap = hdmi_dv_timings_cap, - .get_fmt = hdmi_get_fmt, -}; - -static const struct v4l2_subdev_ops hdmi_sd_ops = { - .core = &hdmi_sd_core_ops, - .video = &hdmi_sd_video_ops, - .pad = &hdmi_sd_pad_ops, -}; - -static int hdmi_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - - dev_dbg(dev, "%s\n", __func__); - v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); - hdmi_resource_poweroff(&hdev->res); - /* flag that device context is lost */ - hdev->cur_conf_dirty = 1; - return 0; -} - -static int hdmi_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret; - - dev_dbg(dev, "%s\n", __func__); - - ret = hdmi_resource_poweron(&hdev->res); - if (ret < 0) - return ret; - - /* starting MHL */ - ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); - if (hdev->mhl_sd && ret) - goto fail; - - dev_dbg(dev, "poweron succeed\n"); - - return 0; - -fail: - hdmi_resource_poweroff(&hdev->res); - dev_err(dev, "poweron failed\n"); - - return ret; -} - -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; - -static void hdmi_resource_clear_clocks(struct hdmi_resources *res) -{ - res->hdmi = ERR_PTR(-EINVAL); - res->sclk_hdmi = ERR_PTR(-EINVAL); - res->sclk_pixel = ERR_PTR(-EINVAL); - res->sclk_hdmiphy = ERR_PTR(-EINVAL); - res->hdmiphy = ERR_PTR(-EINVAL); -} - -static void hdmi_resources_cleanup(struct hdmi_device *hdev) -{ - struct hdmi_resources *res = &hdev->res; - - dev_dbg(hdev->dev, "HDMI resource cleanup\n"); - /* put clocks, power */ - if (res->regul_count) - regulator_bulk_free(res->regul_count, res->regul_bulk); - /* kfree is NULL-safe */ - kfree(res->regul_bulk); - if (!IS_ERR(res->hdmiphy)) - clk_put(res->hdmiphy); - if (!IS_ERR(res->sclk_hdmiphy)) - clk_put(res->sclk_hdmiphy); - if (!IS_ERR(res->sclk_pixel)) - clk_put(res->sclk_pixel); - if (!IS_ERR(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR(res->hdmi)) - clk_put(res->hdmi); - memset(res, 0, sizeof(*res)); - hdmi_resource_clear_clocks(res); -} - -static int hdmi_resources_init(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - static char *supply[] = { - "hdmi-en", - "vdd", - "vdd_osc", - "vdd_pll", - }; - int i, ret; - - dev_dbg(dev, "HDMI resource init\n"); - - memset(res, 0, sizeof(*res)); - hdmi_resource_clear_clocks(res); - - /* get clocks, power */ - res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR(res->hdmi)) { - dev_err(dev, "failed to get clock 'hdmi'\n"); - goto fail; - } - res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR(res->sclk_hdmi)) { - dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); - goto fail; - } - res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR(res->sclk_pixel)) { - dev_err(dev, "failed to get clock 'sclk_pixel'\n"); - goto fail; - } - res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR(res->sclk_hdmiphy)) { - dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); - goto fail; - } - res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR(res->hdmiphy)) { - dev_err(dev, "failed to get clock 'hdmiphy'\n"); - goto fail; - } - res->regul_bulk = kcalloc(ARRAY_SIZE(supply), - sizeof(res->regul_bulk[0]), GFP_KERNEL); - if (!res->regul_bulk) { - dev_err(dev, "failed to get memory for regulators\n"); - goto fail; - } - for (i = 0; i < ARRAY_SIZE(supply); ++i) { - res->regul_bulk[i].supply = supply[i]; - res->regul_bulk[i].consumer = NULL; - } - - ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); - if (ret) { - dev_err(dev, "failed to get regulators\n"); - goto fail; - } - res->regul_count = ARRAY_SIZE(supply); - - return 0; -fail: - dev_err(dev, "HDMI resource init - failed\n"); - hdmi_resources_cleanup(hdev); - return -ENODEV; -} - -static int hdmi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - struct hdmi_device *hdmi_dev = NULL; - struct s5p_hdmi_platform_data *pdata = dev->platform_data; - int ret; - - dev_dbg(dev, "probe start\n"); - - if (!pdata) { - dev_err(dev, "platform data is missing\n"); - ret = -ENODEV; - goto fail; - } - - hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL); - if (!hdmi_dev) { - dev_err(dev, "out of memory\n"); - ret = -ENOMEM; - goto fail; - } - - hdmi_dev->dev = dev; - - ret = hdmi_resources_init(hdmi_dev); - if (ret) - goto fail; - - /* mapping HDMI registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (hdmi_dev->regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0, - "hdmi", hdmi_dev); - if (ret) { - dev_err(dev, "request interrupt failed.\n"); - goto fail_init; - } - hdmi_dev->irq = res->start; - - /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */ - strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev), - sizeof(hdmi_dev->v4l2_dev.name)); - /* passing NULL owner prevents driver from erasing drvdata */ - ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); - if (ret) { - dev_err(dev, "could not register v4l2 device.\n"); - goto fail_init; - } - - /* testing if hdmiphy info is present */ - if (!pdata->hdmiphy_info) { - dev_err(dev, "hdmiphy info is missing in platform data\n"); - ret = -ENXIO; - goto fail_vdev; - } - - adapter = i2c_get_adapter(pdata->hdmiphy_bus); - if (adapter == NULL) { - dev_err(dev, "hdmiphy adapter request failed\n"); - ret = -ENXIO; - goto fail_vdev; - } - - hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, - adapter, pdata->hdmiphy_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->phy_sd == NULL) { - dev_err(dev, "missing subdev for hdmiphy\n"); - ret = -ENODEV; - goto fail_vdev; - } - - /* initialization of MHL interface if present */ - if (pdata->mhl_info) { - adapter = i2c_get_adapter(pdata->mhl_bus); - if (adapter == NULL) { - dev_err(dev, "MHL adapter request failed\n"); - ret = -ENXIO; - goto fail_vdev; - } - - hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board( - &hdmi_dev->v4l2_dev, adapter, - pdata->mhl_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->mhl_sd == NULL) { - dev_err(dev, "missing subdev for MHL\n"); - ret = -ENODEV; - goto fail_vdev; - } - } - - clk_enable(hdmi_dev->res.hdmi); - - pm_runtime_enable(dev); - - sd = &hdmi_dev->sd; - v4l2_subdev_init(sd, &hdmi_sd_ops); - sd->owner = THIS_MODULE; - - strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); - hdmi_dev->cur_timings = - hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].dv_timings; - /* FIXME: missing fail timings is not supported */ - hdmi_dev->cur_conf = - hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].hdmi_timings; - hdmi_dev->cur_conf_dirty = 1; - - /* storing subdev for call that have only access to struct device */ - dev_set_drvdata(dev, sd); - - dev_info(dev, "probe successful\n"); - - return 0; - -fail_vdev: - v4l2_device_unregister(&hdmi_dev->v4l2_dev); - -fail_init: - hdmi_resources_cleanup(hdmi_dev); - -fail: - dev_err(dev, "probe failed\n"); - return ret; -} - -static int hdmi_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd); - - pm_runtime_disable(dev); - clk_disable(hdmi_dev->res.hdmi); - v4l2_device_unregister(&hdmi_dev->v4l2_dev); - disable_irq(hdmi_dev->irq); - hdmi_resources_cleanup(hdmi_dev); - dev_info(dev, "remove successful\n"); - - return 0; -} - -static struct platform_driver hdmi_driver __refdata = { - .probe = hdmi_probe, - .remove = hdmi_remove, - .id_table = hdmi_driver_types, - .driver = { - .name = "s5p-hdmi", - .pm = &hdmi_pm_ops, - } -}; - -module_platform_driver(hdmi_driver); diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c deleted file mode 100644 index aae652351aa8..000000000000 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Samsung HDMI Physical interface driver - * - * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd - * Author: Tomasz Stanislawski <t.stanislaws@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/module.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/err.h> - -#include <media/v4l2-subdev.h> - -MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung HDMI Physical interface driver"); -MODULE_LICENSE("GPL"); - -struct hdmiphy_conf { - unsigned long pixclk; - const u8 *data; -}; - -struct hdmiphy_ctx { - struct v4l2_subdev sd; - const struct hdmiphy_conf *conf_tab; -}; - -static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, - 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 148352000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08, - 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, - 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08, - 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, - 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, - 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, - 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4, - 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08, - 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08, - 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08, - 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08, - 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08, - 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } - }, - { /* end marker */ } -}; - -static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) -{ - return container_of(sd, struct hdmiphy_ctx, sd); -} - -static const u8 *hdmiphy_find_conf(unsigned long pixclk, - const struct hdmiphy_conf *conf) -{ - for (; conf->pixclk; ++conf) - if (conf->pixclk == pixclk) - return conf->data; - return NULL; -} - -static int hdmiphy_s_power(struct v4l2_subdev *sd, int on) -{ - /* to be implemented */ - return 0; -} - -static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - const u8 *data; - u8 buffer[32]; - int ret; - struct hdmiphy_ctx *ctx = sd_to_ctx(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct device *dev = &client->dev; - unsigned long pixclk = timings->bt.pixelclock; - - dev_info(dev, "s_dv_timings\n"); - if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000) - pixclk = 74176000; - data = hdmiphy_find_conf(pixclk, ctx->conf_tab); - if (!data) { - dev_err(dev, "format not supported\n"); - return -EINVAL; - } - - /* storing configuration to the device */ - memcpy(buffer, data, 32); - ret = i2c_master_send(client, buffer, 32); - if (ret != 32) { - dev_err(dev, "failed to configure HDMIPHY via I2C\n"); - return -EIO; - } - - return 0; -} - -static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd, - struct v4l2_dv_timings_cap *cap) -{ - if (cap->pad != 0) - return -EINVAL; - - cap->type = V4L2_DV_BT_656_1120; - /* The phy only determines the pixelclock, leave the other values - * at 0 to signify that we have no information for them. */ - cap->bt.min_pixelclock = 27000000; - cap->bt.max_pixelclock = 148500000; - return 0; -} - -static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct device *dev = &client->dev; - u8 buffer[2]; - int ret; - - dev_info(dev, "s_stream(%d)\n", enable); - /* going to/from configuration from/to operation mode */ - buffer[0] = 0x1f; - buffer[1] = enable ? 0x80 : 0x00; - - ret = i2c_master_send(client, buffer, 2); - if (ret != 2) { - dev_err(dev, "stream (%d) failed\n", enable); - return -EIO; - } - return 0; -} - -static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { - .s_power = hdmiphy_s_power, -}; - -static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { - .s_dv_timings = hdmiphy_s_dv_timings, - .s_stream = hdmiphy_s_stream, -}; - -static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = { - .dv_timings_cap = hdmiphy_dv_timings_cap, -}; - -static const struct v4l2_subdev_ops hdmiphy_ops = { - .core = &hdmiphy_core_ops, - .video = &hdmiphy_video_ops, - .pad = &hdmiphy_pad_ops, -}; - -static int hdmiphy_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct hdmiphy_ctx *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data; - v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops); - - dev_info(&client->dev, "probe successful\n"); - return 0; -} - -static int hdmiphy_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct hdmiphy_ctx *ctx = sd_to_ctx(sd); - - kfree(ctx); - dev_info(&client->dev, "remove successful\n"); - - return 0; -} - -static const struct i2c_device_id hdmiphy_id[] = { - { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 }, - { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 }, - { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 }, - { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 }, - { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, hdmiphy_id); - -static struct i2c_driver hdmiphy_driver = { - .driver = { - .name = "s5p-hdmiphy", - }, - .probe = hdmiphy_probe, - .remove = hdmiphy_remove, - .id_table = hdmiphy_id, -}; - -module_i2c_driver(hdmiphy_driver); diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h deleted file mode 100644 index 869f0ce86f6e..000000000000 --- a/drivers/media/platform/s5p-tv/mixer.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#ifndef SAMSUNG_MIXER_H -#define SAMSUNG_MIXER_H - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG - #define DEBUG -#endif - -#include <linux/fb.h> -#include <linux/irqreturn.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <media/v4l2-device.h> -#include <media/videobuf2-v4l2.h> - -#include "regs-mixer.h" - -/** maximum number of output interfaces */ -#define MXR_MAX_OUTPUTS 2 -/** maximum number of input interfaces (layers) */ -#define MXR_MAX_LAYERS 3 -#define MXR_DRIVER_NAME "s5p-mixer" -/** maximal number of planes for every layer */ -#define MXR_MAX_PLANES 2 - -#define MXR_ENABLE 1 -#define MXR_DISABLE 0 - -/** description of a macroblock for packed formats */ -struct mxr_block { - /** vertical number of pixels in macroblock */ - unsigned int width; - /** horizontal number of pixels in macroblock */ - unsigned int height; - /** size of block in bytes */ - unsigned int size; -}; - -/** description of supported format */ -struct mxr_format { - /** format name/mnemonic */ - const char *name; - /** fourcc identifier */ - u32 fourcc; - /** colorspace identifier */ - enum v4l2_colorspace colorspace; - /** number of planes in image data */ - int num_planes; - /** description of block for each plane */ - struct mxr_block plane[MXR_MAX_PLANES]; - /** number of subframes in image data */ - int num_subframes; - /** specifies to which subframe belong given plane */ - int plane2subframe[MXR_MAX_PLANES]; - /** internal code, driver dependent */ - unsigned long cookie; -}; - -/** description of crop configuration for image */ -struct mxr_crop { - /** width of layer in pixels */ - unsigned int full_width; - /** height of layer in pixels */ - unsigned int full_height; - /** horizontal offset of first pixel to be displayed */ - unsigned int x_offset; - /** vertical offset of first pixel to be displayed */ - unsigned int y_offset; - /** width of displayed data in pixels */ - unsigned int width; - /** height of displayed data in pixels */ - unsigned int height; - /** indicate which fields are present in buffer */ - unsigned int field; -}; - -/** stages of geometry operations */ -enum mxr_geometry_stage { - MXR_GEOMETRY_SINK, - MXR_GEOMETRY_COMPOSE, - MXR_GEOMETRY_CROP, - MXR_GEOMETRY_SOURCE, -}; - -/* flag indicating that offset should be 0 */ -#define MXR_NO_OFFSET 0x80000000 - -/** description of transformation from source to destination image */ -struct mxr_geometry { - /** cropping for source image */ - struct mxr_crop src; - /** cropping for destination image */ - struct mxr_crop dst; - /** layer-dependant description of horizontal scaling */ - unsigned int x_ratio; - /** layer-dependant description of vertical scaling */ - unsigned int y_ratio; -}; - -/** instance of a buffer */ -struct mxr_buffer { - /** common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - /** node for layer's lists */ - struct list_head list; -}; - - -/** internal states of layer */ -enum mxr_layer_state { - /** layers is not shown */ - MXR_LAYER_IDLE = 0, - /** layer is shown */ - MXR_LAYER_STREAMING, - /** state before STREAMOFF is finished */ - MXR_LAYER_STREAMING_FINISH, -}; - -/** forward declarations */ -struct mxr_device; -struct mxr_layer; - -/** callback for layers operation */ -struct mxr_layer_ops { - /* TODO: try to port it to subdev API */ - /** handler for resource release function */ - void (*release)(struct mxr_layer *); - /** setting buffer to HW */ - void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *); - /** setting format and geometry in HW */ - void (*format_set)(struct mxr_layer *); - /** streaming stop/start */ - void (*stream_set)(struct mxr_layer *, int); - /** adjusting geometry */ - void (*fix_geometry)(struct mxr_layer *, - enum mxr_geometry_stage, unsigned long); -}; - -/** layer instance, a single window and content displayed on output */ -struct mxr_layer { - /** parent mixer device */ - struct mxr_device *mdev; - /** layer index (unique identifier) */ - int idx; - /** callbacks for layer methods */ - struct mxr_layer_ops ops; - /** format array */ - const struct mxr_format **fmt_array; - /** size of format array */ - unsigned long fmt_array_size; - - /** lock for protection of list and state fields */ - spinlock_t enq_slock; - /** list for enqueued buffers */ - struct list_head enq_list; - /** buffer currently owned by hardware in temporary registers */ - struct mxr_buffer *update_buf; - /** buffer currently owned by hardware in shadow registers */ - struct mxr_buffer *shadow_buf; - /** state of layer IDLE/STREAMING */ - enum mxr_layer_state state; - - /** mutex for protection of fields below */ - struct mutex mutex; - /** handler for video node */ - struct video_device vfd; - /** queue for output buffers */ - struct vb2_queue vb_queue; - /** current image format */ - const struct mxr_format *fmt; - /** current geometry of image */ - struct mxr_geometry geo; -}; - -/** description of mixers output interface */ -struct mxr_output { - /** name of output */ - char name[32]; - /** output subdev */ - struct v4l2_subdev *sd; - /** cookie used for configuration of registers */ - int cookie; -}; - -/** specify source of output subdevs */ -struct mxr_output_conf { - /** name of output (connector) */ - char *output_name; - /** name of module that generates output subdev */ - char *module_name; - /** cookie need for mixer HW */ - int cookie; -}; - -struct clk; -struct regulator; - -/** auxiliary resources used my mixer */ -struct mxr_resources { - /** interrupt index */ - int irq; - /** pointer to Mixer registers */ - void __iomem *mxr_regs; - /** pointer to Video Processor registers */ - void __iomem *vp_regs; - /** other resources, should used under mxr_device.mutex */ - struct clk *mixer; - struct clk *vp; - struct clk *sclk_mixer; - struct clk *sclk_hdmi; - struct clk *sclk_dac; -}; - -/* event flags used */ -enum mxr_devide_flags { - MXR_EVENT_VSYNC = 0, - MXR_EVENT_TOP = 1, -}; - -/** drivers instance */ -struct mxr_device { - /** master device */ - struct device *dev; - /** state of each layer */ - struct mxr_layer *layer[MXR_MAX_LAYERS]; - /** state of each output */ - struct mxr_output *output[MXR_MAX_OUTPUTS]; - /** number of registered outputs */ - int output_cnt; - - /* video resources */ - - /** V4L2 device */ - struct v4l2_device v4l2_dev; - /** event wait queue */ - wait_queue_head_t event_queue; - /** state flags */ - unsigned long event_flags; - - /** spinlock for protection of registers */ - spinlock_t reg_slock; - - /** mutex for protection of fields below */ - struct mutex mutex; - /** number of entities depndant on output configuration */ - int n_output; - /** number of users that do streaming */ - int n_streamer; - /** index of current output */ - int current_output; - /** auxiliary resources used my mixer */ - struct mxr_resources res; -}; - -/** transform device structure into mixer device */ -static inline struct mxr_device *to_mdev(struct device *dev) -{ - struct v4l2_device *vdev = dev_get_drvdata(dev); - return container_of(vdev, struct mxr_device, v4l2_dev); -} - -/** get current output data, should be called under mdev's mutex */ -static inline struct mxr_output *to_output(struct mxr_device *mdev) -{ - return mdev->output[mdev->current_output]; -} - -/** get current output subdev, should be called under mdev's mutex */ -static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev) -{ - struct mxr_output *out = to_output(mdev); - return out ? out->sd : NULL; -} - -/** forward declaration for mixer platform data */ -struct mxr_platform_data; - -/** acquiring common video resources */ -int mxr_acquire_video(struct mxr_device *mdev, - struct mxr_output_conf *output_cont, int output_count); - -/** releasing common video resources */ -void mxr_release_video(struct mxr_device *mdev); - -struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); -struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); -struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, const struct mxr_layer_ops *ops); - -void mxr_base_layer_release(struct mxr_layer *layer); -void mxr_layer_release(struct mxr_layer *layer); - -int mxr_base_layer_register(struct mxr_layer *layer); -void mxr_base_layer_unregister(struct mxr_layer *layer); - -unsigned long mxr_get_plane_size(const struct mxr_block *blk, - unsigned int width, unsigned int height); - -/** adds new consumer for mixer's power */ -int __must_check mxr_power_get(struct mxr_device *mdev); -/** removes consumer for mixer's power */ -void mxr_power_put(struct mxr_device *mdev); -/** add new client for output configuration */ -void mxr_output_get(struct mxr_device *mdev); -/** removes new client for output configuration */ -void mxr_output_put(struct mxr_device *mdev); -/** add new client for streaming */ -void mxr_streamer_get(struct mxr_device *mdev); -/** removes new client for streaming */ -void mxr_streamer_put(struct mxr_device *mdev); -/** returns format of data delivared to current output */ -void mxr_get_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *mbus_fmt); - -/* Debug */ - -#define mxr_err(mdev, fmt, ...) dev_err(mdev->dev, fmt, ##__VA_ARGS__) -#define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__) -#define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__) - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG - #define mxr_dbg(mdev, fmt, ...) dev_dbg(mdev->dev, fmt, ##__VA_ARGS__) -#else - #define mxr_dbg(mdev, fmt, ...) do { (void) mdev; } while (0) -#endif - -/* accessing Mixer's and Video Processor's registers */ - -void mxr_vsync_set_update(struct mxr_device *mdev, int en); -void mxr_reg_reset(struct mxr_device *mdev); -irqreturn_t mxr_irq_handler(int irq, void *dev_data); -void mxr_reg_s_output(struct mxr_device *mdev, int cookie); -void mxr_reg_streamon(struct mxr_device *mdev); -void mxr_reg_streamoff(struct mxr_device *mdev); -int mxr_reg_wait4vsync(struct mxr_device *mdev); -void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *fmt); -void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en); -void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr); -void mxr_reg_graph_format(struct mxr_device *mdev, int idx, - const struct mxr_format *fmt, const struct mxr_geometry *geo); - -void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en); -void mxr_reg_vp_buffer(struct mxr_device *mdev, - dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]); -void mxr_reg_vp_format(struct mxr_device *mdev, - const struct mxr_format *fmt, const struct mxr_geometry *geo); -void mxr_reg_dump(struct mxr_device *mdev); - -#endif /* SAMSUNG_MIXER_H */ - diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c deleted file mode 100644 index 8a5d19469ddc..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/fb.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung MIXER"); -MODULE_LICENSE("GPL"); - -/* --------- DRIVER PARAMETERS ---------- */ - -static struct mxr_output_conf mxr_output_conf[] = { - { - .output_name = "S5P HDMI connector", - .module_name = "s5p-hdmi", - .cookie = 1, - }, - { - .output_name = "S5P SDO connector", - .module_name = "s5p-sdo", - .cookie = 0, - }, -}; - -void mxr_get_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *mbus_fmt) -{ - struct v4l2_subdev *sd; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - mutex_lock(&mdev->mutex); - sd = to_outsd(mdev); - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - *mbus_fmt = fmt.format; - WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); - mutex_unlock(&mdev->mutex); -} - -void mxr_streamer_get(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - ++mdev->n_streamer; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); - if (mdev->n_streamer == 1) { - struct v4l2_subdev *sd = to_outsd(mdev); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; - struct mxr_resources *res = &mdev->res; - int ret; - - if (to_output(mdev)->cookie == 0) - clk_set_parent(res->sclk_mixer, res->sclk_dac); - else - clk_set_parent(res->sclk_mixer, res->sclk_hdmi); - mxr_reg_s_output(mdev, to_output(mdev)->cookie); - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); - ret = v4l2_subdev_call(sd, video, s_stream, 1); - WARN(ret, "starting stream failed for output %s\n", sd->name); - - mxr_reg_set_mbus_fmt(mdev, mbus_fmt); - mxr_reg_streamon(mdev); - ret = mxr_reg_wait4vsync(mdev); - WARN(ret, "failed to get vsync (%d) from output\n", ret); - } - mutex_unlock(&mdev->mutex); - mxr_reg_dump(mdev); - /* FIXME: what to do when streaming fails? */ -} - -void mxr_streamer_put(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - --mdev->n_streamer; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); - if (mdev->n_streamer == 0) { - int ret; - struct v4l2_subdev *sd = to_outsd(mdev); - - mxr_reg_streamoff(mdev); - /* vsync applies Mixer setup */ - ret = mxr_reg_wait4vsync(mdev); - WARN(ret, "failed to get vsync (%d) from output\n", ret); - ret = v4l2_subdev_call(sd, video, s_stream, 0); - WARN(ret, "stopping stream failed for output %s\n", sd->name); - } - WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", - mdev->n_streamer); - mutex_unlock(&mdev->mutex); - mxr_reg_dump(mdev); -} - -void mxr_output_get(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - ++mdev->n_output; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); - /* turn on auxiliary driver */ - if (mdev->n_output == 1) - v4l2_subdev_call(to_outsd(mdev), core, s_power, 1); - mutex_unlock(&mdev->mutex); -} - -void mxr_output_put(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - --mdev->n_output; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); - /* turn on auxiliary driver */ - if (mdev->n_output == 0) - v4l2_subdev_call(to_outsd(mdev), core, s_power, 0); - WARN(mdev->n_output < 0, "negative number of output users (%d)\n", - mdev->n_output); - mutex_unlock(&mdev->mutex); -} - -int mxr_power_get(struct mxr_device *mdev) -{ - int ret = pm_runtime_get_sync(mdev->dev); - - /* returning 1 means that power is already enabled, - * so zero success be returned */ - if (ret < 0) - return ret; - return 0; -} - -void mxr_power_put(struct mxr_device *mdev) -{ - pm_runtime_put_sync(mdev->dev); -} - -/* --------- RESOURCE MANAGEMENT -------------*/ - -static int mxr_acquire_plat_resources(struct mxr_device *mdev, - struct platform_device *pdev) -{ - struct resource *res; - int ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr"); - if (res == NULL) { - mxr_err(mdev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail; - } - - mdev->res.mxr_regs = ioremap(res->start, resource_size(res)); - if (mdev->res.mxr_regs == NULL) { - mxr_err(mdev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp"); - if (res == NULL) { - mxr_err(mdev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail_mxr_regs; - } - - mdev->res.vp_regs = ioremap(res->start, resource_size(res)); - if (mdev->res.vp_regs == NULL) { - mxr_err(mdev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail_mxr_regs; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); - if (res == NULL) { - mxr_err(mdev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail_vp_regs; - } - - ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev); - if (ret) { - mxr_err(mdev, "request interrupt failed.\n"); - goto fail_vp_regs; - } - mdev->res.irq = res->start; - - return 0; - -fail_vp_regs: - iounmap(mdev->res.vp_regs); - -fail_mxr_regs: - iounmap(mdev->res.mxr_regs); - -fail: - return ret; -} - -static void mxr_resource_clear_clocks(struct mxr_resources *res) -{ - res->mixer = ERR_PTR(-EINVAL); - res->vp = ERR_PTR(-EINVAL); - res->sclk_mixer = ERR_PTR(-EINVAL); - res->sclk_hdmi = ERR_PTR(-EINVAL); - res->sclk_dac = ERR_PTR(-EINVAL); -} - -static void mxr_release_plat_resources(struct mxr_device *mdev) -{ - free_irq(mdev->res.irq, mdev); - iounmap(mdev->res.vp_regs); - iounmap(mdev->res.mxr_regs); -} - -static void mxr_release_clocks(struct mxr_device *mdev) -{ - struct mxr_resources *res = &mdev->res; - - if (!IS_ERR(res->sclk_dac)) - clk_put(res->sclk_dac); - if (!IS_ERR(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR(res->sclk_mixer)) - clk_put(res->sclk_mixer); - if (!IS_ERR(res->vp)) - clk_put(res->vp); - if (!IS_ERR(res->mixer)) - clk_put(res->mixer); -} - -static int mxr_acquire_clocks(struct mxr_device *mdev) -{ - struct mxr_resources *res = &mdev->res; - struct device *dev = mdev->dev; - - mxr_resource_clear_clocks(res); - - res->mixer = clk_get(dev, "mixer"); - if (IS_ERR(res->mixer)) { - mxr_err(mdev, "failed to get clock 'mixer'\n"); - goto fail; - } - res->vp = clk_get(dev, "vp"); - if (IS_ERR(res->vp)) { - mxr_err(mdev, "failed to get clock 'vp'\n"); - goto fail; - } - res->sclk_mixer = clk_get(dev, "sclk_mixer"); - if (IS_ERR(res->sclk_mixer)) { - mxr_err(mdev, "failed to get clock 'sclk_mixer'\n"); - goto fail; - } - res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR(res->sclk_hdmi)) { - mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n"); - goto fail; - } - res->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR(res->sclk_dac)) { - mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); - goto fail; - } - - return 0; -fail: - mxr_release_clocks(mdev); - return -ENODEV; -} - -static int mxr_acquire_resources(struct mxr_device *mdev, - struct platform_device *pdev) -{ - int ret; - ret = mxr_acquire_plat_resources(mdev, pdev); - - if (ret) - goto fail; - - ret = mxr_acquire_clocks(mdev); - if (ret) - goto fail_plat; - - mxr_info(mdev, "resources acquired\n"); - return 0; - -fail_plat: - mxr_release_plat_resources(mdev); -fail: - mxr_err(mdev, "resources acquire failed\n"); - return ret; -} - -static void mxr_release_resources(struct mxr_device *mdev) -{ - mxr_release_clocks(mdev); - mxr_release_plat_resources(mdev); - memset(&mdev->res, 0, sizeof(mdev->res)); - mxr_resource_clear_clocks(&mdev->res); -} - -static void mxr_release_layers(struct mxr_device *mdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i) - if (mdev->layer[i]) - mxr_layer_release(mdev->layer[i]); -} - -static int mxr_acquire_layers(struct mxr_device *mdev, - struct mxr_platform_data *pdata) -{ - mdev->layer[0] = mxr_graph_layer_create(mdev, 0); - mdev->layer[1] = mxr_graph_layer_create(mdev, 1); - mdev->layer[2] = mxr_vp_layer_create(mdev, 0); - - if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) { - mxr_err(mdev, "failed to acquire layers\n"); - goto fail; - } - - return 0; - -fail: - mxr_release_layers(mdev); - return -ENODEV; -} - -/* ---------- POWER MANAGEMENT ----------- */ - -static int mxr_runtime_resume(struct device *dev) -{ - struct mxr_device *mdev = to_mdev(dev); - struct mxr_resources *res = &mdev->res; - int ret; - - mxr_dbg(mdev, "resume - start\n"); - mutex_lock(&mdev->mutex); - /* turn clocks on */ - ret = clk_prepare_enable(res->mixer); - if (ret < 0) { - dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n"); - goto fail; - } - ret = clk_prepare_enable(res->vp); - if (ret < 0) { - dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n"); - goto fail_mixer; - } - ret = clk_prepare_enable(res->sclk_mixer); - if (ret < 0) { - dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n"); - goto fail_vp; - } - /* apply default configuration */ - mxr_reg_reset(mdev); - mxr_dbg(mdev, "resume - finished\n"); - - mutex_unlock(&mdev->mutex); - return 0; - -fail_vp: - clk_disable_unprepare(res->vp); -fail_mixer: - clk_disable_unprepare(res->mixer); -fail: - mutex_unlock(&mdev->mutex); - dev_err(mdev->dev, "resume failed\n"); - return ret; -} - -static int mxr_runtime_suspend(struct device *dev) -{ - struct mxr_device *mdev = to_mdev(dev); - struct mxr_resources *res = &mdev->res; - mxr_dbg(mdev, "suspend - start\n"); - mutex_lock(&mdev->mutex); - /* turn clocks off */ - clk_disable_unprepare(res->sclk_mixer); - clk_disable_unprepare(res->vp); - clk_disable_unprepare(res->mixer); - mutex_unlock(&mdev->mutex); - mxr_dbg(mdev, "suspend - finished\n"); - return 0; -} - -static const struct dev_pm_ops mxr_pm_ops = { - .runtime_suspend = mxr_runtime_suspend, - .runtime_resume = mxr_runtime_resume, -}; - -/* --------- DRIVER INITIALIZATION ---------- */ - -static int mxr_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxr_platform_data *pdata = dev->platform_data; - struct mxr_device *mdev; - int ret; - - /* mdev does not exist yet so no mxr_dbg is used */ - dev_info(dev, "probe start\n"); - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) { - dev_err(dev, "not enough memory.\n"); - ret = -ENOMEM; - goto fail; - } - - /* setup pointer to master device */ - mdev->dev = dev; - - mutex_init(&mdev->mutex); - spin_lock_init(&mdev->reg_slock); - init_waitqueue_head(&mdev->event_queue); - - /* acquire resources: regs, irqs, clocks, regulators */ - ret = mxr_acquire_resources(mdev, pdev); - if (ret) - goto fail_mem; - - /* configure resources for video output */ - ret = mxr_acquire_video(mdev, mxr_output_conf, - ARRAY_SIZE(mxr_output_conf)); - if (ret) - goto fail_resources; - - /* configure layers */ - ret = mxr_acquire_layers(mdev, pdata); - if (ret) - goto fail_video; - - pm_runtime_enable(dev); - - mxr_info(mdev, "probe successful\n"); - return 0; - -fail_video: - mxr_release_video(mdev); - -fail_resources: - mxr_release_resources(mdev); - -fail_mem: - kfree(mdev); - -fail: - dev_info(dev, "probe failed\n"); - return ret; -} - -static int mxr_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxr_device *mdev = to_mdev(dev); - - pm_runtime_disable(dev); - - mxr_release_layers(mdev); - mxr_release_video(mdev); - mxr_release_resources(mdev); - - kfree(mdev); - - dev_info(dev, "remove successful\n"); - return 0; -} - -static struct platform_driver mxr_driver __refdata = { - .probe = mxr_probe, - .remove = mxr_remove, - .driver = { - .name = MXR_DRIVER_NAME, - .pm = &mxr_pm_ops, - } -}; - -static int __init mxr_init(void) -{ - int i, ret; - static const char banner[] __initconst = - "Samsung TV Mixer driver, " - "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; - pr_info("%s\n", banner); - - /* Loading auxiliary modules */ - for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i) - request_module(mxr_output_conf[i].module_name); - - ret = platform_driver_register(&mxr_driver); - if (ret != 0) { - pr_err("s5p-tv: registration of MIXER driver failed\n"); - return -ENXIO; - } - - return 0; -} -module_init(mxr_init); - -static void __exit mxr_exit(void) -{ - platform_driver_unregister(&mxr_driver); -} -module_exit(mxr_exit); diff --git a/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c deleted file mode 100644 index d4d2564f7de7..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_grp_layer.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include <media/videobuf2-dma-contig.h> - -/* FORMAT DEFINITIONS */ - -static const struct mxr_format mxr_fb_fmt_rgb565 = { - .name = "RGB565", - .fourcc = V4L2_PIX_FMT_RGB565, - .colorspace = V4L2_COLORSPACE_SRGB, - .num_planes = 1, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 4, -}; - -static const struct mxr_format mxr_fb_fmt_argb1555 = { - .name = "ARGB1555", - .num_planes = 1, - .fourcc = V4L2_PIX_FMT_RGB555, - .colorspace = V4L2_COLORSPACE_SRGB, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 5, -}; - -static const struct mxr_format mxr_fb_fmt_argb4444 = { - .name = "ARGB4444", - .num_planes = 1, - .fourcc = V4L2_PIX_FMT_RGB444, - .colorspace = V4L2_COLORSPACE_SRGB, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 6, -}; - -static const struct mxr_format mxr_fb_fmt_argb8888 = { - .name = "ARGB8888", - .fourcc = V4L2_PIX_FMT_BGR32, - .colorspace = V4L2_COLORSPACE_SRGB, - .num_planes = 1, - .plane = { - { .width = 1, .height = 1, .size = 4 }, - }, - .num_subframes = 1, - .cookie = 7, -}; - -static const struct mxr_format *mxr_graph_format[] = { - &mxr_fb_fmt_rgb565, - &mxr_fb_fmt_argb1555, - &mxr_fb_fmt_argb4444, - &mxr_fb_fmt_argb8888, -}; - -/* AUXILIARY CALLBACKS */ - -static void mxr_graph_layer_release(struct mxr_layer *layer) -{ - mxr_base_layer_unregister(layer); - mxr_base_layer_release(layer); -} - -static void mxr_graph_buffer_set(struct mxr_layer *layer, - struct mxr_buffer *buf) -{ - dma_addr_t addr = 0; - - if (buf) - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - mxr_reg_graph_buffer(layer->mdev, layer->idx, addr); -} - -static void mxr_graph_stream_set(struct mxr_layer *layer, int en) -{ - mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en); -} - -static void mxr_graph_format_set(struct mxr_layer *layer) -{ - mxr_reg_graph_format(layer->mdev, layer->idx, - layer->fmt, &layer->geo); -} - -static inline unsigned int closest(unsigned int x, unsigned int a, - unsigned int b, unsigned long flags) -{ - unsigned int mid = (a + b) / 2; - - /* choosing closest value with constraints according to table: - * -------------+-----+-----+-----+-------+ - * flags | 0 | LE | GE | LE|GE | - * -------------+-----+-----+-----+-------+ - * x <= a | a | a | a | a | - * a < x <= mid | a | a | b | a | - * mid < x < b | b | a | b | b | - * b <= x | b | b | b | b | - * -------------+-----+-----+-----+-------+ - */ - - /* remove all non-constraint flags */ - flags &= V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE; - - if (x <= a) - return a; - if (x >= b) - return b; - if (flags == V4L2_SEL_FLAG_LE) - return a; - if (flags == V4L2_SEL_FLAG_GE) - return b; - if (x <= mid) - return a; - return b; -} - -static inline unsigned int do_center(unsigned int center, - unsigned int size, unsigned int upper, unsigned int flags) -{ - unsigned int lower; - - if (flags & MXR_NO_OFFSET) - return 0; - - lower = center - min(center, size / 2); - return min(lower, upper - size); -} - -static void mxr_graph_fix_geometry(struct mxr_layer *layer, - enum mxr_geometry_stage stage, unsigned long flags) -{ - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *src = &geo->src; - struct mxr_crop *dst = &geo->dst; - unsigned int x_center, y_center; - - switch (stage) { - - case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ - flags = 0; - /* fall through */ - - case MXR_GEOMETRY_COMPOSE: - /* remember center of the area */ - x_center = dst->x_offset + dst->width / 2; - y_center = dst->y_offset + dst->height / 2; - /* round up/down to 2 multiple depending on flags */ - if (flags & V4L2_SEL_FLAG_LE) { - dst->width = round_down(dst->width, 2); - dst->height = round_down(dst->height, 2); - } else { - dst->width = round_up(dst->width, 2); - dst->height = round_up(dst->height, 2); - } - /* assure that compose rect is inside display area */ - dst->width = min(dst->width, dst->full_width); - dst->height = min(dst->height, dst->full_height); - - /* ensure that compose is reachable using 2x scaling */ - dst->width = min(dst->width, 2 * src->full_width); - dst->height = min(dst->height, 2 * src->full_height); - - /* setup offsets */ - dst->x_offset = do_center(x_center, dst->width, - dst->full_width, flags); - dst->y_offset = do_center(y_center, dst->height, - dst->full_height, flags); - flags = 0; - /* fall through */ - - case MXR_GEOMETRY_CROP: - /* remember center of the area */ - x_center = src->x_offset + src->width / 2; - y_center = src->y_offset + src->height / 2; - /* ensure that cropping area lies inside the buffer */ - if (src->full_width < dst->width) - src->width = dst->width / 2; - else - src->width = closest(src->width, dst->width / 2, - dst->width, flags); - - if (src->width == dst->width) - geo->x_ratio = 0; - else - geo->x_ratio = 1; - - if (src->full_height < dst->height) - src->height = dst->height / 2; - else - src->height = closest(src->height, dst->height / 2, - dst->height, flags); - - if (src->height == dst->height) - geo->y_ratio = 0; - else - geo->y_ratio = 1; - - /* setup offsets */ - src->x_offset = do_center(x_center, src->width, - src->full_width, flags); - src->y_offset = do_center(y_center, src->height, - src->full_height, flags); - flags = 0; - /* fall through */ - case MXR_GEOMETRY_SOURCE: - src->full_width = clamp_val(src->full_width, - src->width + src->x_offset, 32767); - src->full_height = clamp_val(src->full_height, - src->height + src->y_offset, 2047); - } -} - -/* PUBLIC API */ - -struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx) -{ - struct mxr_layer *layer; - int ret; - const struct mxr_layer_ops ops = { - .release = mxr_graph_layer_release, - .buffer_set = mxr_graph_buffer_set, - .stream_set = mxr_graph_stream_set, - .format_set = mxr_graph_format_set, - .fix_geometry = mxr_graph_fix_geometry, - }; - char name[32]; - - sprintf(name, "graph%d", idx); - - layer = mxr_base_layer_create(mdev, idx, name, &ops); - if (layer == NULL) { - mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); - goto fail; - } - - layer->fmt_array = mxr_graph_format; - layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format); - - ret = mxr_base_layer_register(layer); - if (ret) - goto fail_layer; - - return layer; - -fail_layer: - mxr_base_layer_release(layer); - -fail: - return NULL; -} - diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c deleted file mode 100644 index a0ec14a1da13..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" -#include "regs-mixer.h" -#include "regs-vp.h" - -#include <linux/delay.h> - -/* Register access subroutines */ - -static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id) -{ - return readl(mdev->res.vp_regs + reg_id); -} - -static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val) -{ - writel(val, mdev->res.vp_regs + reg_id); -} - -static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id, - u32 val, u32 mask) -{ - u32 old = vp_read(mdev, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, mdev->res.vp_regs + reg_id); -} - -static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id) -{ - return readl(mdev->res.mxr_regs + reg_id); -} - -static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val) -{ - writel(val, mdev->res.mxr_regs + reg_id); -} - -static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id, - u32 val, u32 mask) -{ - u32 old = mxr_read(mdev, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, mdev->res.mxr_regs + reg_id); -} - -void mxr_vsync_set_update(struct mxr_device *mdev, int en) -{ - /* block update on vsync */ - mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0, - MXR_STATUS_SYNC_ENABLE); - vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0); -} - -static void __mxr_reg_vp_reset(struct mxr_device *mdev) -{ - int tries = 100; - - vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING); - for (tries = 100; tries; --tries) { - /* waiting until VP_SRESET_PROCESSING is 0 */ - if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING) - break; - mdelay(10); - } - WARN(tries == 0, "failed to reset Video Processor\n"); -} - -static void mxr_reg_vp_default_filter(struct mxr_device *mdev); - -void mxr_reg_reset(struct mxr_device *mdev) -{ - unsigned long flags; - u32 val; /* value stored to register */ - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* set output in RGB888 mode */ - mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888); - - /* 16 beat burst in DMA */ - mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST, - MXR_STATUS_BURST_MASK); - - /* setting default layer priority: layer1 > video > layer0 - * because typical usage scenario would be - * layer0 - framebuffer - * video - video overlay - * layer1 - OSD - */ - val = MXR_LAYER_CFG_GRP0_VAL(1); - val |= MXR_LAYER_CFG_VP_VAL(2); - val |= MXR_LAYER_CFG_GRP1_VAL(3); - mxr_write(mdev, MXR_LAYER_CFG, val); - - /* use dark gray background color */ - mxr_write(mdev, MXR_BG_COLOR0, 0x808080); - mxr_write(mdev, MXR_BG_COLOR1, 0x808080); - mxr_write(mdev, MXR_BG_COLOR2, 0x808080); - - /* setting graphical layers */ - - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ - val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */ - val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ - - /* the same configuration for both layers */ - mxr_write(mdev, MXR_GRAPHIC_CFG(0), val); - mxr_write(mdev, MXR_GRAPHIC_CFG(1), val); - - /* configuration of Video Processor Registers */ - __mxr_reg_vp_reset(mdev); - mxr_reg_vp_default_filter(mdev); - - /* enable all interrupts */ - mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_graph_format(struct mxr_device *mdev, int idx, - const struct mxr_format *fmt, const struct mxr_geometry *geo) -{ - u32 val; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* setup format */ - mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx), - MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK); - - /* setup geometry */ - mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width); - val = MXR_GRP_WH_WIDTH(geo->src.width); - val |= MXR_GRP_WH_HEIGHT(geo->src.height); - val |= MXR_GRP_WH_H_SCALE(geo->x_ratio); - val |= MXR_GRP_WH_V_SCALE(geo->y_ratio); - mxr_write(mdev, MXR_GRAPHIC_WH(idx), val); - - /* setup offsets in source image */ - val = MXR_GRP_SXY_SX(geo->src.x_offset); - val |= MXR_GRP_SXY_SY(geo->src.y_offset); - mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val); - - /* setup offsets in display image */ - val = MXR_GRP_DXY_DX(geo->dst.x_offset); - val |= MXR_GRP_DXY_DY(geo->dst.y_offset); - mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_vp_format(struct mxr_device *mdev, - const struct mxr_format *fmt, const struct mxr_geometry *geo) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK); - - /* setting size of input image */ - vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) | - VP_IMG_VSIZE(geo->src.full_height)); - /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) | - VP_IMG_VSIZE(geo->src.full_height / 2)); - - vp_write(mdev, VP_SRC_WIDTH, geo->src.width); - vp_write(mdev, VP_SRC_HEIGHT, geo->src.height); - vp_write(mdev, VP_SRC_H_POSITION, - VP_SRC_H_POSITION_VAL(geo->src.x_offset)); - vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset); - - vp_write(mdev, VP_DST_WIDTH, geo->dst.width); - vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset); - if (geo->dst.field == V4L2_FIELD_INTERLACED) { - vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2); - vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2); - } else { - vp_write(mdev, VP_DST_HEIGHT, geo->dst.height); - vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset); - } - - vp_write(mdev, VP_H_RATIO, geo->x_ratio); - vp_write(mdev, VP_V_RATIO, geo->y_ratio); - - vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); - -} - -void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr) -{ - u32 val = addr ? ~0 : 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - if (idx == 0) - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); - else - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); - mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_vp_buffer(struct mxr_device *mdev, - dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]) -{ - u32 val = luma_addr[0] ? ~0 : 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE); - vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON); - /* TODO: fix tiled mode */ - vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]); - vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]); - vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]); - vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -static void mxr_irq_layer_handle(struct mxr_layer *layer) -{ - struct list_head *head = &layer->enq_list; - struct mxr_buffer *done; - - /* skip non-existing layer */ - if (layer == NULL) - return; - - spin_lock(&layer->enq_slock); - if (layer->state == MXR_LAYER_IDLE) - goto done; - - done = layer->shadow_buf; - layer->shadow_buf = layer->update_buf; - - if (list_empty(head)) { - if (layer->state != MXR_LAYER_STREAMING) - layer->update_buf = NULL; - } else { - struct mxr_buffer *next; - next = list_first_entry(head, struct mxr_buffer, list); - list_del(&next->list); - layer->update_buf = next; - } - - layer->ops.buffer_set(layer, layer->update_buf); - - if (done && done != layer->shadow_buf) - vb2_buffer_done(&done->vb.vb2_buf, VB2_BUF_STATE_DONE); - -done: - spin_unlock(&layer->enq_slock); -} - -irqreturn_t mxr_irq_handler(int irq, void *dev_data) -{ - struct mxr_device *mdev = dev_data; - u32 i, val; - - spin_lock(&mdev->reg_slock); - val = mxr_read(mdev, MXR_INT_STATUS); - - /* wake up process waiting for VSYNC */ - if (val & MXR_INT_STATUS_VSYNC) { - set_bit(MXR_EVENT_VSYNC, &mdev->event_flags); - /* toggle TOP field event if working in interlaced mode */ - if (~mxr_read(mdev, MXR_CFG) & MXR_CFG_SCAN_PROGRASSIVE) - change_bit(MXR_EVENT_TOP, &mdev->event_flags); - wake_up(&mdev->event_queue); - /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_STATUS_VSYNC; - val |= MXR_INT_CLEAR_VSYNC; - } - - /* clear interrupts */ - mxr_write(mdev, MXR_INT_STATUS, val); - - spin_unlock(&mdev->reg_slock); - /* leave on non-vsync event */ - if (~val & MXR_INT_CLEAR_VSYNC) - return IRQ_HANDLED; - /* skip layer update on bottom field */ - if (!test_bit(MXR_EVENT_TOP, &mdev->event_flags)) - return IRQ_HANDLED; - for (i = 0; i < MXR_MAX_LAYERS; ++i) - mxr_irq_layer_handle(mdev->layer[i]); - return IRQ_HANDLED; -} - -void mxr_reg_s_output(struct mxr_device *mdev, int cookie) -{ - u32 val; - - val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI; - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK); -} - -void mxr_reg_streamon(struct mxr_device *mdev) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - /* single write -> no need to block vsync update */ - - /* start MIXER */ - mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); - set_bit(MXR_EVENT_TOP, &mdev->event_flags); - - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_streamoff(struct mxr_device *mdev) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - /* single write -> no need to block vsync update */ - - /* stop MIXER */ - mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN); - - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -int mxr_reg_wait4vsync(struct mxr_device *mdev) -{ - long time_left; - - clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags); - /* TODO: consider adding interruptible */ - time_left = wait_event_timeout(mdev->event_queue, - test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), - msecs_to_jiffies(1000)); - if (time_left > 0) - return 0; - mxr_warn(mdev, "no vsync detected - timeout\n"); - return -ETIME; -} - -void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *fmt) -{ - u32 val = 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* selecting colorspace accepted by output */ - if (fmt->colorspace == V4L2_COLORSPACE_JPEG) - val |= MXR_CFG_OUT_YUV444; - else - val |= MXR_CFG_OUT_RGB888; - - /* choosing between interlace and progressive mode */ - if (fmt->field == V4L2_FIELD_INTERLACED) - val |= MXR_CFG_SCAN_INTERLACE; - else - val |= MXR_CFG_SCAN_PROGRASSIVE; - - /* choosing between porper HD and SD mode */ - if (fmt->height == 480) - val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; - else if (fmt->height == 576) - val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; - else if (fmt->height == 720) - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; - else if (fmt->height == 1080) - val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; - else - WARN(1, "unrecognized mbus height %u!\n", fmt->height); - - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK | - MXR_CFG_OUT_MASK); - - val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0; - vp_write_mask(mdev, VP_MODE, val, - VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en) -{ - /* no extra actions need to be done */ -} - -void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en) -{ - /* no extra actions need to be done */ -} - -static const u8 filter_y_horiz_tap8[] = { - 0, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 0, 0, 0, - 0, 2, 4, 5, 6, 6, 6, 6, - 6, 5, 5, 4, 3, 2, 1, 1, - 0, -6, -12, -16, -18, -20, -21, -20, - -20, -18, -16, -13, -10, -8, -5, -2, - 127, 126, 125, 121, 114, 107, 99, 89, - 79, 68, 57, 46, 35, 25, 16, 8, -}; - -static const u8 filter_y_vert_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, - 0, 5, 11, 19, 27, 37, 48, 59, - 70, 81, 92, 102, 111, 118, 124, 126, - 0, 0, -1, -1, -2, -3, -4, -5, - -6, -7, -8, -8, -8, -8, -6, -3, -}; - -static const u8 filter_cr_horiz_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, -}; - -static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, - int reg_id, const u8 *data, unsigned int size) -{ - /* assure 4-byte align */ - BUG_ON(size & 3); - for (; size; size -= 4, reg_id += 4, data += 4) { - u32 val = (data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | data[3]; - vp_write(mdev, reg_id, val); - } -} - -static void mxr_reg_vp_default_filter(struct mxr_device *mdev) -{ - mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL, - filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8)); - mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, - filter_y_vert_tap4, sizeof(filter_y_vert_tap4)); - mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL, - filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); -} - -static void mxr_reg_mxr_dump(struct mxr_device *mdev) -{ -#define DUMPREG(reg_id) \ -do { \ - mxr_dbg(mdev, #reg_id " = %08x\n", \ - (u32)readl(mdev->res.mxr_regs + reg_id)); \ -} while (0) - - DUMPREG(MXR_STATUS); - DUMPREG(MXR_CFG); - DUMPREG(MXR_INT_EN); - DUMPREG(MXR_INT_STATUS); - - DUMPREG(MXR_LAYER_CFG); - DUMPREG(MXR_VIDEO_CFG); - - DUMPREG(MXR_GRAPHIC0_CFG); - DUMPREG(MXR_GRAPHIC0_BASE); - DUMPREG(MXR_GRAPHIC0_SPAN); - DUMPREG(MXR_GRAPHIC0_WH); - DUMPREG(MXR_GRAPHIC0_SXY); - DUMPREG(MXR_GRAPHIC0_DXY); - - DUMPREG(MXR_GRAPHIC1_CFG); - DUMPREG(MXR_GRAPHIC1_BASE); - DUMPREG(MXR_GRAPHIC1_SPAN); - DUMPREG(MXR_GRAPHIC1_WH); - DUMPREG(MXR_GRAPHIC1_SXY); - DUMPREG(MXR_GRAPHIC1_DXY); -#undef DUMPREG -} - -static void mxr_reg_vp_dump(struct mxr_device *mdev) -{ -#define DUMPREG(reg_id) \ -do { \ - mxr_dbg(mdev, #reg_id " = %08x\n", \ - (u32) readl(mdev->res.vp_regs + reg_id)); \ -} while (0) - - - DUMPREG(VP_ENABLE); - DUMPREG(VP_SRESET); - DUMPREG(VP_SHADOW_UPDATE); - DUMPREG(VP_FIELD_ID); - DUMPREG(VP_MODE); - DUMPREG(VP_IMG_SIZE_Y); - DUMPREG(VP_IMG_SIZE_C); - DUMPREG(VP_PER_RATE_CTRL); - DUMPREG(VP_TOP_Y_PTR); - DUMPREG(VP_BOT_Y_PTR); - DUMPREG(VP_TOP_C_PTR); - DUMPREG(VP_BOT_C_PTR); - DUMPREG(VP_ENDIAN_MODE); - DUMPREG(VP_SRC_H_POSITION); - DUMPREG(VP_SRC_V_POSITION); - DUMPREG(VP_SRC_WIDTH); - DUMPREG(VP_SRC_HEIGHT); - DUMPREG(VP_DST_H_POSITION); - DUMPREG(VP_DST_V_POSITION); - DUMPREG(VP_DST_WIDTH); - DUMPREG(VP_DST_HEIGHT); - DUMPREG(VP_H_RATIO); - DUMPREG(VP_V_RATIO); - -#undef DUMPREG -} - -void mxr_reg_dump(struct mxr_device *mdev) -{ - mxr_reg_mxr_dump(mdev); - mxr_reg_vp_dump(mdev); -} - diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c deleted file mode 100644 index ee74e2b44d69..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ /dev/null @@ -1,1130 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 - */ - -#define pr_fmt(fmt) "s5p-tv (mixer): " fmt - -#include "mixer.h" - -#include <media/v4l2-ioctl.h> -#include <linux/videodev2.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/timer.h> -#include <media/videobuf2-dma-contig.h> - -static int find_reg_callback(struct device *dev, void *p) -{ - struct v4l2_subdev **sd = p; - - *sd = dev_get_drvdata(dev); - /* non-zero value stops iteration */ - return 1; -} - -static struct v4l2_subdev *find_and_register_subdev( - struct mxr_device *mdev, char *module_name) -{ - struct device_driver *drv; - struct v4l2_subdev *sd = NULL; - int ret; - - /* TODO: add waiting until probe is finished */ - drv = driver_find(module_name, &platform_bus_type); - if (!drv) { - mxr_warn(mdev, "module %s is missing\n", module_name); - return NULL; - } - /* driver refcnt is increased, it is safe to iterate over devices */ - ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback); - /* ret == 0 means that find_reg_callback was never executed */ - if (sd == NULL) { - mxr_warn(mdev, "module %s provides no subdev!\n", module_name); - goto done; - } - /* v4l2_device_register_subdev detects if sd is NULL */ - ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd); - if (ret) { - mxr_warn(mdev, "failed to register subdev %s\n", sd->name); - sd = NULL; - } - -done: - return sd; -} - -int mxr_acquire_video(struct mxr_device *mdev, - struct mxr_output_conf *output_conf, int output_count) -{ - struct device *dev = mdev->dev; - struct v4l2_device *v4l2_dev = &mdev->v4l2_dev; - int i; - int ret = 0; - struct v4l2_subdev *sd; - - strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name)); - /* prepare context for V4L2 device */ - ret = v4l2_device_register(dev, v4l2_dev); - if (ret) { - mxr_err(mdev, "could not register v4l2 device.\n"); - goto fail; - } - - vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32)); - - /* registering outputs */ - mdev->output_cnt = 0; - for (i = 0; i < output_count; ++i) { - struct mxr_output_conf *conf = &output_conf[i]; - struct mxr_output *out; - - sd = find_and_register_subdev(mdev, conf->module_name); - /* trying to register next output */ - if (sd == NULL) - continue; - out = kzalloc(sizeof(*out), GFP_KERNEL); - if (out == NULL) { - mxr_err(mdev, "no memory for '%s'\n", - conf->output_name); - ret = -ENOMEM; - /* registered subdevs are removed in fail_v4l2_dev */ - goto fail_output; - } - strlcpy(out->name, conf->output_name, sizeof(out->name)); - out->sd = sd; - out->cookie = conf->cookie; - mdev->output[mdev->output_cnt++] = out; - mxr_info(mdev, "added output '%s' from module '%s'\n", - conf->output_name, conf->module_name); - /* checking if maximal number of outputs is reached */ - if (mdev->output_cnt >= MXR_MAX_OUTPUTS) - break; - } - - if (mdev->output_cnt == 0) { - mxr_err(mdev, "failed to register any output\n"); - ret = -ENODEV; - /* skipping fail_output because there is nothing to free */ - goto fail_v4l2_dev; - } - - return 0; - -fail_output: - /* kfree is NULL-safe */ - for (i = 0; i < mdev->output_cnt; ++i) - kfree(mdev->output[i]); - memset(mdev->output, 0, sizeof(mdev->output)); - -fail_v4l2_dev: - /* NOTE: automatically unregister all subdevs */ - v4l2_device_unregister(v4l2_dev); - -fail: - return ret; -} - -void mxr_release_video(struct mxr_device *mdev) -{ - int i; - - /* kfree is NULL-safe */ - for (i = 0; i < mdev->output_cnt; ++i) - kfree(mdev->output[i]); - - vb2_dma_contig_clear_max_seg_size(mdev->dev); - v4l2_device_unregister(&mdev->v4l2_dev); -} - -static int mxr_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver)); - strlcpy(cap->card, layer->vfd.name, sizeof(cap->card)); - sprintf(cap->bus_info, "%d", layer->idx); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) -{ - mxr_dbg(mdev, "src.full_size = (%u, %u)\n", - geo->src.full_width, geo->src.full_height); - mxr_dbg(mdev, "src.size = (%u, %u)\n", - geo->src.width, geo->src.height); - mxr_dbg(mdev, "src.offset = (%u, %u)\n", - geo->src.x_offset, geo->src.y_offset); - mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", - geo->dst.full_width, geo->dst.full_height); - mxr_dbg(mdev, "dst.size = (%u, %u)\n", - geo->dst.width, geo->dst.height); - mxr_dbg(mdev, "dst.offset = (%u, %u)\n", - geo->dst.x_offset, geo->dst.y_offset); - mxr_dbg(mdev, "ratio = (%u, %u)\n", - geo->x_ratio, geo->y_ratio); -} - -static void mxr_layer_default_geo(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - struct v4l2_mbus_framefmt mbus_fmt; - - memset(&layer->geo, 0, sizeof(layer->geo)); - - mxr_get_mbus_fmt(mdev, &mbus_fmt); - - layer->geo.dst.full_width = mbus_fmt.width; - layer->geo.dst.full_height = mbus_fmt.height; - layer->geo.dst.width = layer->geo.dst.full_width; - layer->geo.dst.height = layer->geo.dst.full_height; - layer->geo.dst.field = mbus_fmt.field; - - layer->geo.src.full_width = mbus_fmt.width; - layer->geo.src.full_height = mbus_fmt.height; - layer->geo.src.width = layer->geo.src.full_width; - layer->geo.src.height = layer->geo.src.full_height; - - mxr_geometry_dump(mdev, &layer->geo); - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - mxr_geometry_dump(mdev, &layer->geo); -} - -static void mxr_layer_update_output(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - struct v4l2_mbus_framefmt mbus_fmt; - - mxr_get_mbus_fmt(mdev, &mbus_fmt); - /* checking if update is needed */ - if (layer->geo.dst.full_width == mbus_fmt.width && - layer->geo.dst.full_height == mbus_fmt.width) - return; - - layer->geo.dst.full_width = mbus_fmt.width; - layer->geo.dst.full_height = mbus_fmt.height; - layer->geo.dst.field = mbus_fmt.field; - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - - mxr_geometry_dump(mdev, &layer->geo); -} - -static const struct mxr_format *find_format_by_fourcc( - struct mxr_layer *layer, unsigned long fourcc); -static const struct mxr_format *find_format_by_index( - struct mxr_layer *layer, unsigned long index); - -static int mxr_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - const struct mxr_format *fmt; - - mxr_dbg(mdev, "%s\n", __func__); - fmt = find_format_by_index(layer, f->index); - if (fmt == NULL) - return -EINVAL; - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; - - return 0; -} - -static unsigned int divup(unsigned int divident, unsigned int divisor) -{ - return (divident + divisor - 1) / divisor; -} - -unsigned long mxr_get_plane_size(const struct mxr_block *blk, - unsigned int width, unsigned int height) -{ - unsigned int bl_width = divup(width, blk->width); - unsigned int bl_height = divup(height, blk->height); - - return bl_width * bl_height * blk->size; -} - -static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, - const struct mxr_format *fmt, u32 width, u32 height) -{ - int i; - - /* checking if nothing to fill */ - if (!planes) - return; - - memset(planes, 0, sizeof(*planes) * fmt->num_subframes); - for (i = 0; i < fmt->num_planes; ++i) { - struct v4l2_plane_pix_format *plane = planes - + fmt->plane2subframe[i]; - const struct mxr_block *blk = &fmt->plane[i]; - u32 bl_width = divup(width, blk->width); - u32 bl_height = divup(height, blk->height); - u32 sizeimage = bl_width * bl_height * blk->size; - u32 bytesperline = bl_width * blk->size / blk->height; - - plane->sizeimage += sizeimage; - plane->bytesperline = max(plane->bytesperline, bytesperline); - } -} - -static int mxr_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mxr_layer *layer = video_drvdata(file); - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - pix->width = layer->geo.src.full_width; - pix->height = layer->geo.src.full_height; - pix->field = V4L2_FIELD_NONE; - pix->pixelformat = layer->fmt->fourcc; - pix->colorspace = layer->fmt->colorspace; - mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height); - - return 0; -} - -static int mxr_s_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mxr_layer *layer = video_drvdata(file); - const struct mxr_format *fmt; - struct v4l2_pix_format_mplane *pix; - struct mxr_device *mdev = layer->mdev; - struct mxr_geometry *geo = &layer->geo; - - mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); - - pix = &f->fmt.pix_mp; - fmt = find_format_by_fourcc(layer, pix->pixelformat); - if (fmt == NULL) { - mxr_warn(mdev, "not recognized fourcc: %08x\n", - pix->pixelformat); - return -EINVAL; - } - layer->fmt = fmt; - /* set source size to highest accepted value */ - geo->src.full_width = max(geo->dst.full_width, pix->width); - geo->src.full_height = max(geo->dst.full_height, pix->height); - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - /* set cropping to total visible screen */ - geo->src.width = pix->width; - geo->src.height = pix->height; - geo->src.x_offset = 0; - geo->src.y_offset = 0; - /* assure consistency of geometry */ - layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); - mxr_geometry_dump(mdev, &layer->geo); - /* set full size to lowest possible value */ - geo->src.full_width = 0; - geo->src.full_height = 0; - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - - /* returning results */ - mxr_g_fmt(file, priv, f); - - return 0; -} - -static int mxr_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_geometry *geo = &layer->geo; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_CROP: - s->r.left = geo->src.x_offset; - s->r.top = geo->src.y_offset; - s->r.width = geo->src.width; - s->r.height = geo->src.height; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - s->r.left = 0; - s->r.top = 0; - s->r.width = geo->src.full_width; - s->r.height = geo->src.full_height; - break; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_PADDED: - s->r.left = geo->dst.x_offset; - s->r.top = geo->dst.y_offset; - s->r.width = geo->dst.width; - s->r.height = geo->dst.height; - break; - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - s->r.left = 0; - s->r.top = 0; - s->r.width = geo->dst.full_width; - s->r.height = geo->dst.full_height; - break; - default: - return -EINVAL; - } - - return 0; -} - -/* returns 1 if rectangle 'a' is inside 'b' */ -static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left) - return 0; - if (a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - return 1; -} - -static int mxr_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *target = NULL; - enum mxr_geometry_stage stage; - struct mxr_geometry tmp; - struct v4l2_rect res; - - memset(&res, 0, sizeof(res)); - - mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, - s->r.width, s->r.height, s->r.left, s->r.top); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - switch (s->target) { - /* ignore read-only targets */ - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - res.width = geo->src.full_width; - res.height = geo->src.full_height; - break; - - /* ignore read-only targets */ - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - res.width = geo->dst.full_width; - res.height = geo->dst.full_height; - break; - - case V4L2_SEL_TGT_CROP: - target = &geo->src; - stage = MXR_GEOMETRY_CROP; - break; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_PADDED: - target = &geo->dst; - stage = MXR_GEOMETRY_COMPOSE; - break; - default: - return -EINVAL; - } - /* apply change and update geometry if needed */ - if (target) { - /* backup current geometry if setup fails */ - memcpy(&tmp, geo, sizeof(tmp)); - - /* apply requested selection */ - target->x_offset = s->r.left; - target->y_offset = s->r.top; - target->width = s->r.width; - target->height = s->r.height; - - layer->ops.fix_geometry(layer, stage, s->flags); - - /* retrieve update selection rectangle */ - res.left = target->x_offset; - res.top = target->y_offset; - res.width = target->width; - res.height = target->height; - - mxr_geometry_dump(layer->mdev, &layer->geo); - } - - /* checking if the rectangle satisfies constraints */ - if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r)) - goto fail; - if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res)) - goto fail; - - /* return result rectangle */ - s->r = res; - - return 0; -fail: - /* restore old geometry, which is not touched if target is NULL */ - if (target) - memcpy(geo, &tmp, sizeof(tmp)); - return -ERANGE; -} - -static int mxr_enum_dv_timings(struct file *file, void *fh, - struct v4l2_enum_dv_timings *timings) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - timings->pad = 0; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), pad, enum_dv_timings, timings); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -static int mxr_s_dv_timings(struct file *file, void *fh, - struct v4l2_dv_timings *timings) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - - /* timings change cannot be done while there is an entity - * dependent on output configuration - */ - if (mdev->n_output > 0) { - mutex_unlock(&mdev->mutex); - return -EBUSY; - } - - ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_timings, timings); - - mutex_unlock(&mdev->mutex); - - mxr_layer_update_output(layer); - - /* any failure should return EINVAL according to V4L2 doc */ - return ret ? -EINVAL : 0; -} - -static int mxr_g_dv_timings(struct file *file, void *fh, - struct v4l2_dv_timings *timings) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_timings, timings); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -static int mxr_dv_timings_cap(struct file *file, void *fh, - struct v4l2_dv_timings_cap *cap) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - cap->pad = 0; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), pad, dv_timings_cap, cap); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - - /* standard change cannot be done while there is an entity - * dependent on output configuration - */ - if (mdev->n_output > 0) { - mutex_unlock(&mdev->mutex); - return -EBUSY; - } - - ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, norm); - - mutex_unlock(&mdev->mutex); - - mxr_layer_update_output(layer); - - return ret ? -EINVAL : 0; -} - -static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - struct mxr_output *out; - struct v4l2_subdev *sd; - - if (a->index >= mdev->output_cnt) - return -EINVAL; - out = mdev->output[a->index]; - BUG_ON(out == NULL); - sd = out->sd; - strlcpy(a->name, out->name, sizeof(a->name)); - - /* try to obtain supported tv norms */ - v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); - a->capabilities = 0; - if (sd->ops->video && sd->ops->video->s_dv_timings) - a->capabilities |= V4L2_OUT_CAP_DV_TIMINGS; - if (sd->ops->video && sd->ops->video->s_std_output) - a->capabilities |= V4L2_OUT_CAP_STD; - a->type = V4L2_OUTPUT_TYPE_ANALOG; - - return 0; -} - -static int mxr_s_output(struct file *file, void *fh, unsigned int i) -{ - struct video_device *vfd = video_devdata(file); - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - - if (i >= mdev->output_cnt || mdev->output[i] == NULL) - return -EINVAL; - - mutex_lock(&mdev->mutex); - if (mdev->n_output > 0) { - mutex_unlock(&mdev->mutex); - return -EBUSY; - } - mdev->current_output = i; - vfd->tvnorms = 0; - v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, - &vfd->tvnorms); - mutex_unlock(&mdev->mutex); - - /* update layers geometry */ - mxr_layer_update_output(layer); - - mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); - - return 0; -} - -static int mxr_g_output(struct file *file, void *fh, unsigned int *p) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - - mutex_lock(&mdev->mutex); - *p = mdev->current_output; - mutex_unlock(&mdev->mutex); - - return 0; -} - -static int mxr_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_reqbufs(&layer->vb_queue, p); -} - -static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_querybuf(&layer->vb_queue, p); -} - -static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index); - return vb2_qbuf(&layer->vb_queue, p); -} - -static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); -} - -static int mxr_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_expbuf(&layer->vb_queue, eb); -} - -static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_streamon(&layer->vb_queue, i); -} - -static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_streamoff(&layer->vb_queue, i); -} - -static const struct v4l2_ioctl_ops mxr_ioctl_ops = { - .vidioc_querycap = mxr_querycap, - /* format handling */ - .vidioc_enum_fmt_vid_out_mplane = mxr_enum_fmt, - .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt, - .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt, - /* buffer control */ - .vidioc_reqbufs = mxr_reqbufs, - .vidioc_querybuf = mxr_querybuf, - .vidioc_qbuf = mxr_qbuf, - .vidioc_dqbuf = mxr_dqbuf, - .vidioc_expbuf = mxr_expbuf, - /* Streaming control */ - .vidioc_streamon = mxr_streamon, - .vidioc_streamoff = mxr_streamoff, - /* DV Timings functions */ - .vidioc_enum_dv_timings = mxr_enum_dv_timings, - .vidioc_s_dv_timings = mxr_s_dv_timings, - .vidioc_g_dv_timings = mxr_g_dv_timings, - .vidioc_dv_timings_cap = mxr_dv_timings_cap, - /* analog TV standard functions */ - .vidioc_s_std = mxr_s_std, - .vidioc_g_std = mxr_g_std, - /* Output handling */ - .vidioc_enum_output = mxr_enum_output, - .vidioc_s_output = mxr_s_output, - .vidioc_g_output = mxr_g_output, - /* selection ioctls */ - .vidioc_g_selection = mxr_g_selection, - .vidioc_s_selection = mxr_s_selection, -}; - -static int mxr_video_open(struct file *file) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret = 0; - - mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); - if (mutex_lock_interruptible(&layer->mutex)) - return -ERESTARTSYS; - /* assure device probe is finished */ - wait_for_device_probe(); - /* creating context for file descriptor */ - ret = v4l2_fh_open(file); - if (ret) { - mxr_err(mdev, "v4l2_fh_open failed\n"); - goto unlock; - } - - /* leaving if layer is already initialized */ - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - /* FIXME: should power be enabled on open? */ - ret = mxr_power_get(mdev); - if (ret) { - mxr_err(mdev, "power on failed\n"); - goto fail_fh_open; - } - - ret = vb2_queue_init(&layer->vb_queue); - if (ret != 0) { - mxr_err(mdev, "failed to initialize vb2 queue\n"); - goto fail_power; - } - /* set default format, first on the list */ - layer->fmt = layer->fmt_array[0]; - /* setup default geometry */ - mxr_layer_default_geo(layer); - mutex_unlock(&layer->mutex); - - return 0; - -fail_power: - mxr_power_put(mdev); - -fail_fh_open: - v4l2_fh_release(file); - -unlock: - mutex_unlock(&layer->mutex); - - return ret; -} - -static unsigned int -mxr_video_poll(struct file *file, struct poll_table_struct *wait) -{ - struct mxr_layer *layer = video_drvdata(file); - unsigned int res; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - mutex_lock(&layer->mutex); - res = vb2_poll(&layer->vb_queue, file, wait); - mutex_unlock(&layer->mutex); - return res; -} - -static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct mxr_layer *layer = video_drvdata(file); - int ret; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - if (mutex_lock_interruptible(&layer->mutex)) - return -ERESTARTSYS; - ret = vb2_mmap(&layer->vb_queue, vma); - mutex_unlock(&layer->mutex); - return ret; -} - -static int mxr_video_release(struct file *file) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - mutex_lock(&layer->mutex); - if (v4l2_fh_is_singular_file(file)) { - vb2_queue_release(&layer->vb_queue); - mxr_power_put(layer->mdev); - } - v4l2_fh_release(file); - mutex_unlock(&layer->mutex); - return 0; -} - -static const struct v4l2_file_operations mxr_fops = { - .owner = THIS_MODULE, - .open = mxr_video_open, - .poll = mxr_video_poll, - .mmap = mxr_video_mmap, - .release = mxr_video_release, - .unlocked_ioctl = video_ioctl2, -}; - -static int queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - const struct mxr_format *fmt = layer->fmt; - int i; - struct mxr_device *mdev = layer->mdev; - struct v4l2_plane_pix_format planes[3]; - - mxr_dbg(mdev, "%s\n", __func__); - /* checking if format was configured */ - if (fmt == NULL) - return -EINVAL; - mxr_dbg(mdev, "fmt = %s\n", fmt->name); - mxr_mplane_fill(planes, fmt, layer->geo.src.full_width, - layer->geo.src.full_height); - - *nplanes = fmt->num_subframes; - for (i = 0; i < fmt->num_subframes; ++i) { - sizes[i] = planes[i].sizeimage; - mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); - } - - if (*nbuffers == 0) - *nbuffers = 1; - - return 0; -} - -static void buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct mxr_buffer *buffer = container_of(vbuf, struct mxr_buffer, vb); - struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - spin_lock_irqsave(&layer->enq_slock, flags); - list_add_tail(&buffer->list, &layer->enq_list); - spin_unlock_irqrestore(&layer->enq_slock, flags); - - mxr_dbg(mdev, "queuing buffer\n"); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - mxr_dbg(mdev, "%s\n", __func__); - - /* block any changes in output configuration */ - mxr_output_get(mdev); - - mxr_layer_update_output(layer); - layer->ops.format_set(layer); - /* enabling layer in hardware */ - spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_STREAMING; - spin_unlock_irqrestore(&layer->enq_slock, flags); - - layer->ops.stream_set(layer, MXR_ENABLE); - mxr_streamer_get(mdev); - - return 0; -} - -static void mxr_watchdog(unsigned long arg) -{ - struct mxr_layer *layer = (struct mxr_layer *) arg; - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name); - - spin_lock_irqsave(&layer->enq_slock, flags); - - if (layer->update_buf == layer->shadow_buf) - layer->update_buf = NULL; - if (layer->update_buf) { - vb2_buffer_done(&layer->update_buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - layer->update_buf = NULL; - } - if (layer->shadow_buf) { - vb2_buffer_done(&layer->shadow_buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - layer->shadow_buf = NULL; - } - spin_unlock_irqrestore(&layer->enq_slock, flags); -} - -static void stop_streaming(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - struct timer_list watchdog; - struct mxr_buffer *buf, *buf_tmp; - - mxr_dbg(mdev, "%s\n", __func__); - - spin_lock_irqsave(&layer->enq_slock, flags); - - /* reset list */ - layer->state = MXR_LAYER_STREAMING_FINISH; - - /* set all buffer to be done */ - list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&layer->enq_slock, flags); - - /* give 1 seconds to complete to complete last buffers */ - setup_timer_on_stack(&watchdog, mxr_watchdog, - (unsigned long)layer); - mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000)); - - /* wait until all buffers are goes to done state */ - vb2_wait_for_all_buffers(vq); - - /* stop timer if all synchronization is done */ - del_timer_sync(&watchdog); - destroy_timer_on_stack(&watchdog); - - /* stopping hardware */ - spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_IDLE; - spin_unlock_irqrestore(&layer->enq_slock, flags); - - /* disabling layer in hardware */ - layer->ops.stream_set(layer, MXR_DISABLE); - /* remove one streamer */ - mxr_streamer_put(mdev); - /* allow changes in output configuration */ - mxr_output_put(mdev); -} - -static struct vb2_ops mxr_video_qops = { - .queue_setup = queue_setup, - .buf_queue = buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, -}; - -/* FIXME: try to put this functions to mxr_base_layer_create */ -int mxr_base_layer_register(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - int ret; - - ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1); - if (ret) - mxr_err(mdev, "failed to register video device\n"); - else - mxr_info(mdev, "registered layer %s as /dev/video%d\n", - layer->vfd.name, layer->vfd.num); - return ret; -} - -void mxr_base_layer_unregister(struct mxr_layer *layer) -{ - video_unregister_device(&layer->vfd); -} - -void mxr_layer_release(struct mxr_layer *layer) -{ - if (layer->ops.release) - layer->ops.release(layer); -} - -void mxr_base_layer_release(struct mxr_layer *layer) -{ - kfree(layer); -} - -static void mxr_vfd_release(struct video_device *vdev) -{ - pr_info("video device release\n"); -} - -struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, const struct mxr_layer_ops *ops) -{ - struct mxr_layer *layer; - - layer = kzalloc(sizeof(*layer), GFP_KERNEL); - if (layer == NULL) { - mxr_err(mdev, "not enough memory for layer.\n"); - goto fail; - } - - layer->mdev = mdev; - layer->idx = idx; - layer->ops = *ops; - - spin_lock_init(&layer->enq_slock); - INIT_LIST_HEAD(&layer->enq_list); - mutex_init(&layer->mutex); - - layer->vfd = (struct video_device) { - .minor = -1, - .release = mxr_vfd_release, - .fops = &mxr_fops, - .vfl_dir = VFL_DIR_TX, - .ioctl_ops = &mxr_ioctl_ops, - }; - strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); - - video_set_drvdata(&layer->vfd, layer); - layer->vfd.lock = &layer->mutex; - layer->vfd.v4l2_dev = &mdev->v4l2_dev; - - layer->vb_queue = (struct vb2_queue) { - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF, - .drv_priv = layer, - .buf_struct_size = sizeof(struct mxr_buffer), - .ops = &mxr_video_qops, - .min_buffers_needed = 1, - .mem_ops = &vb2_dma_contig_memops, - .lock = &layer->mutex, - .dev = mdev->dev, - }; - - return layer; - -fail: - return NULL; -} - -static const struct mxr_format *find_format_by_fourcc( - struct mxr_layer *layer, unsigned long fourcc) -{ - int i; - - for (i = 0; i < layer->fmt_array_size; ++i) - if (layer->fmt_array[i]->fourcc == fourcc) - return layer->fmt_array[i]; - return NULL; -} - -static const struct mxr_format *find_format_by_index( - struct mxr_layer *layer, unsigned long index) -{ - if (index >= layer->fmt_array_size) - return NULL; - return layer->fmt_array[index]; -} - diff --git a/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c deleted file mode 100644 index 6fa6f673f53b..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_vp_layer.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include "regs-vp.h" - -#include <media/videobuf2-dma-contig.h> - -/* FORMAT DEFINITIONS */ -static const struct mxr_format mxr_fmt_nv12 = { - .name = "NV12", - .fourcc = V4L2_PIX_FMT_NV12, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 1, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv21 = { - .name = "NV21", - .fourcc = V4L2_PIX_FMT_NV21, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 1, - .cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv12m = { - .name = "NV12 (mplane)", - .fourcc = V4L2_PIX_FMT_NV12M, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 2, - .plane2subframe = {0, 1}, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv12mt = { - .name = "NV12 tiled (mplane)", - .fourcc = V4L2_PIX_FMT_NV12MT, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 128, .height = 32, .size = 4096 }, - { .width = 128, .height = 32, .size = 2048 }, - }, - .num_subframes = 2, - .plane2subframe = {0, 1}, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED, -}; - -static const struct mxr_format *mxr_video_format[] = { - &mxr_fmt_nv12, - &mxr_fmt_nv21, - &mxr_fmt_nv12m, - &mxr_fmt_nv12mt, -}; - -/* AUXILIARY CALLBACKS */ - -static void mxr_vp_layer_release(struct mxr_layer *layer) -{ - mxr_base_layer_unregister(layer); - mxr_base_layer_release(layer); -} - -static void mxr_vp_buffer_set(struct mxr_layer *layer, - struct mxr_buffer *buf) -{ - dma_addr_t luma_addr[2] = {0, 0}; - dma_addr_t chroma_addr[2] = {0, 0}; - - if (buf == NULL) { - mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); - return; - } - luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - if (layer->fmt->num_subframes == 2) { - chroma_addr[0] = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1); - } else { - /* FIXME: mxr_get_plane_size compute integer division, - * which is slow and should not be performed in interrupt */ - chroma_addr[0] = luma_addr[0] + mxr_get_plane_size( - &layer->fmt->plane[0], layer->geo.src.full_width, - layer->geo.src.full_height); - } - if (layer->fmt->cookie & VP_MODE_MEM_TILED) { - luma_addr[1] = luma_addr[0] + 0x40; - chroma_addr[1] = chroma_addr[0] + 0x40; - } else { - luma_addr[1] = luma_addr[0] + layer->geo.src.full_width; - chroma_addr[1] = chroma_addr[0]; - } - mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); -} - -static void mxr_vp_stream_set(struct mxr_layer *layer, int en) -{ - mxr_reg_vp_layer_stream(layer->mdev, en); -} - -static void mxr_vp_format_set(struct mxr_layer *layer) -{ - mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo); -} - -static inline unsigned int do_center(unsigned int center, - unsigned int size, unsigned int upper, unsigned int flags) -{ - unsigned int lower; - - if (flags & MXR_NO_OFFSET) - return 0; - - lower = center - min(center, size / 2); - return min(lower, upper - size); -} - -static void mxr_vp_fix_geometry(struct mxr_layer *layer, - enum mxr_geometry_stage stage, unsigned long flags) -{ - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *src = &geo->src; - struct mxr_crop *dst = &geo->dst; - unsigned long x_center, y_center; - - switch (stage) { - - case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ - case MXR_GEOMETRY_COMPOSE: - /* remember center of the area */ - x_center = dst->x_offset + dst->width / 2; - y_center = dst->y_offset + dst->height / 2; - - /* ensure that compose is reachable using 16x scaling */ - dst->width = clamp(dst->width, 8U, 16 * src->full_width); - dst->height = clamp(dst->height, 1U, 16 * src->full_height); - - /* setup offsets */ - dst->x_offset = do_center(x_center, dst->width, - dst->full_width, flags); - dst->y_offset = do_center(y_center, dst->height, - dst->full_height, flags); - flags = 0; /* remove possible MXR_NO_OFFSET flag */ - /* fall through */ - case MXR_GEOMETRY_CROP: - /* remember center of the area */ - x_center = src->x_offset + src->width / 2; - y_center = src->y_offset + src->height / 2; - - /* ensure scaling is between 0.25x .. 16x */ - src->width = clamp(src->width, round_up(dst->width / 16, 4), - dst->width * 4); - src->height = clamp(src->height, round_up(dst->height / 16, 4), - dst->height * 4); - - /* hardware limits */ - src->width = clamp(src->width, 32U, 2047U); - src->height = clamp(src->height, 4U, 2047U); - - /* setup offsets */ - src->x_offset = do_center(x_center, src->width, - src->full_width, flags); - src->y_offset = do_center(y_center, src->height, - src->full_height, flags); - - /* setting scaling ratio */ - geo->x_ratio = (src->width << 16) / dst->width; - geo->y_ratio = (src->height << 16) / dst->height; - /* fall through */ - - case MXR_GEOMETRY_SOURCE: - src->full_width = clamp(src->full_width, - ALIGN(src->width + src->x_offset, 8), 8192U); - src->full_height = clamp(src->full_height, - src->height + src->y_offset, 8192U); - } -} - -/* PUBLIC API */ - -struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx) -{ - struct mxr_layer *layer; - int ret; - const struct mxr_layer_ops ops = { - .release = mxr_vp_layer_release, - .buffer_set = mxr_vp_buffer_set, - .stream_set = mxr_vp_stream_set, - .format_set = mxr_vp_format_set, - .fix_geometry = mxr_vp_fix_geometry, - }; - char name[32]; - - sprintf(name, "video%d", idx); - - layer = mxr_base_layer_create(mdev, idx, name, &ops); - if (layer == NULL) { - mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); - goto fail; - } - - layer->fmt_array = mxr_video_format; - layer->fmt_array_size = ARRAY_SIZE(mxr_video_format); - - ret = mxr_base_layer_register(layer); - if (ret) - goto fail_layer; - - return layer; - -fail_layer: - mxr_base_layer_release(layer); - -fail: - return NULL; -} - diff --git a/drivers/media/platform/s5p-tv/regs-hdmi.h b/drivers/media/platform/s5p-tv/regs-hdmi.h deleted file mode 100644 index a889d1f57f28..000000000000 --- a/drivers/media/platform/s5p-tv/regs-hdmi.h +++ /dev/null @@ -1,146 +0,0 @@ -/* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * HDMI register header file for Samsung TVOUT driver - * - * 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. -*/ - -#ifndef SAMSUNG_REGS_HDMI_H -#define SAMSUNG_REGS_HDMI_H - -/* - * Register part -*/ - -#define HDMI_CTRL_BASE(x) ((x) + 0x00000000) -#define HDMI_CORE_BASE(x) ((x) + 0x00010000) -#define HDMI_TG_BASE(x) ((x) + 0x00050000) - -/* Control registers */ -#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) -#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) -#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) -#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) - -/* Core registers */ -#define HDMI_CON_0 HDMI_CORE_BASE(0x0000) -#define HDMI_CON_1 HDMI_CORE_BASE(0x0004) -#define HDMI_CON_2 HDMI_CORE_BASE(0x0008) -#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) -#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) -#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) -#define HDMI_HPD HDMI_CORE_BASE(0x0030) -#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) -#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) -#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) -#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) -#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) -#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) -#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) -#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) - -/* Timing generator registers */ -#define HDMI_TG_CMD HDMI_TG_BASE(0x0000) -#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) -#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C) -#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020) -#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024) -#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028) -#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C) -#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030) -#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034) -#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038) -#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C) -#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040) -#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044) -#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048) -#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C) -#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050) -#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054) -#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058) -#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C) -#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060) -#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064) -#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078) -#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C) -#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080) -#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084) -#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088) -#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C) -#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090) -#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094) - -/* - * Bit definition part - */ - -/* HDMI_INTC_CON */ -#define HDMI_INTC_EN_GLOBAL (1 << 6) -#define HDMI_INTC_EN_HPD_PLUG (1 << 3) -#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) - -/* HDMI_INTC_FLAG */ -#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) -#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) - -/* HDMI_PHY_RSTOUT */ -#define HDMI_PHY_SW_RSTOUT (1 << 0) - -/* HDMI_CORE_RSTOUT */ -#define HDMI_CORE_SW_RSTOUT (1 << 0) - -/* HDMI_CON_0 */ -#define HDMI_BLUE_SCR_EN (1 << 5) -#define HDMI_EN (1 << 0) - -/* HDMI_CON_2 */ -#define HDMI_DVI_PERAMBLE_EN (1 << 5) -#define HDMI_DVI_BAND_EN (1 << 1) - -/* HDMI_PHY_STATUS */ -#define HDMI_PHY_STATUS_READY (1 << 0) - -/* HDMI_MODE_SEL */ -#define HDMI_MODE_HDMI_EN (1 << 1) -#define HDMI_MODE_DVI_EN (1 << 0) -#define HDMI_MODE_MASK (3 << 0) - -/* HDMI_TG_CMD */ -#define HDMI_TG_FIELD_EN (1 << 1) -#define HDMI_TG_EN (1 << 0) - -#endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/media/platform/s5p-tv/regs-mixer.h b/drivers/media/platform/s5p-tv/regs-mixer.h deleted file mode 100644 index 158abb43d0a4..000000000000 --- a/drivers/media/platform/s5p-tv/regs-mixer.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Mixer register header file for Samsung Mixer driver - * - * 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. -*/ -#ifndef SAMSUNG_REGS_MIXER_H -#define SAMSUNG_REGS_MIXER_H - -/* - * Register part - */ -#define MXR_STATUS 0x0000 -#define MXR_CFG 0x0004 -#define MXR_INT_EN 0x0008 -#define MXR_INT_STATUS 0x000C -#define MXR_LAYER_CFG 0x0010 -#define MXR_VIDEO_CFG 0x0014 -#define MXR_GRAPHIC0_CFG 0x0020 -#define MXR_GRAPHIC0_BASE 0x0024 -#define MXR_GRAPHIC0_SPAN 0x0028 -#define MXR_GRAPHIC0_SXY 0x002C -#define MXR_GRAPHIC0_WH 0x0030 -#define MXR_GRAPHIC0_DXY 0x0034 -#define MXR_GRAPHIC0_BLANK 0x0038 -#define MXR_GRAPHIC1_CFG 0x0040 -#define MXR_GRAPHIC1_BASE 0x0044 -#define MXR_GRAPHIC1_SPAN 0x0048 -#define MXR_GRAPHIC1_SXY 0x004C -#define MXR_GRAPHIC1_WH 0x0050 -#define MXR_GRAPHIC1_DXY 0x0054 -#define MXR_GRAPHIC1_BLANK 0x0058 -#define MXR_BG_CFG 0x0060 -#define MXR_BG_COLOR0 0x0064 -#define MXR_BG_COLOR1 0x0068 -#define MXR_BG_COLOR2 0x006C - -/* for parametrized access to layer registers */ -#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20) -#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20) -#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20) -#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20) -#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20) -#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20) - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ -#define MXR_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define MXR_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit)) - -/* bits for MXR_STATUS */ -#define MXR_STATUS_16_BURST (1 << 7) -#define MXR_STATUS_BURST_MASK (1 << 7) -#define MXR_STATUS_SYNC_ENABLE (1 << 2) -#define MXR_STATUS_REG_RUN (1 << 0) - -/* bits for MXR_CFG */ -#define MXR_CFG_OUT_YUV444 (0 << 8) -#define MXR_CFG_OUT_RGB888 (1 << 8) -#define MXR_CFG_OUT_MASK (1 << 8) -#define MXR_CFG_DST_SDO (0 << 7) -#define MXR_CFG_DST_HDMI (1 << 7) -#define MXR_CFG_DST_MASK (1 << 7) -#define MXR_CFG_SCAN_HD_720 (0 << 6) -#define MXR_CFG_SCAN_HD_1080 (1 << 6) -#define MXR_CFG_GRP1_ENABLE (1 << 5) -#define MXR_CFG_GRP0_ENABLE (1 << 4) -#define MXR_CFG_VP_ENABLE (1 << 3) -#define MXR_CFG_SCAN_INTERLACE (0 << 2) -#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2) -#define MXR_CFG_SCAN_NTSC (0 << 1) -#define MXR_CFG_SCAN_PAL (1 << 1) -#define MXR_CFG_SCAN_SD (0 << 0) -#define MXR_CFG_SCAN_HD (1 << 0) -#define MXR_CFG_SCAN_MASK 0x47 - -/* bits for MXR_GRAPHICn_CFG */ -#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21) -#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) -#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) -#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) - -/* bits for MXR_GRAPHICn_WH */ -#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28) -#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12) -#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_SXY */ -#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_DXY */ -#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_INT_EN */ -#define MXR_INT_EN_VSYNC (1 << 11) -#define MXR_INT_EN_ALL (0x0f << 8) - -/* bit for MXR_INT_STATUS */ -#define MXR_INT_CLEAR_VSYNC (1 << 11) -#define MXR_INT_STATUS_VSYNC (1 << 0) - -/* bit for MXR_LAYER_CFG */ -#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) -#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) - -#endif /* SAMSUNG_REGS_MIXER_H */ - diff --git a/drivers/media/platform/s5p-tv/regs-sdo.h b/drivers/media/platform/s5p-tv/regs-sdo.h deleted file mode 100644 index 6f22fbfe2f6c..000000000000 --- a/drivers/media/platform/s5p-tv/regs-sdo.h +++ /dev/null @@ -1,63 +0,0 @@ -/* drivers/media/platform/s5p-tv/regs-sdo.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * SDO register description file - * - * 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. - */ - -#ifndef SAMSUNG_REGS_SDO_H -#define SAMSUNG_REGS_SDO_H - -/* - * Register part - */ - -#define SDO_CLKCON 0x0000 -#define SDO_CONFIG 0x0008 -#define SDO_VBI 0x0014 -#define SDO_DAC 0x003C -#define SDO_CCCON 0x0180 -#define SDO_IRQ 0x0280 -#define SDO_IRQMASK 0x0284 -#define SDO_VERSION 0x03D8 - -/* - * Bit definition part - */ - -/* SDO Clock Control Register (SDO_CLKCON) */ -#define SDO_TVOUT_SW_RESET (1 << 4) -#define SDO_TVOUT_CLOCK_READY (1 << 1) -#define SDO_TVOUT_CLOCK_ON (1 << 0) - -/* SDO Video Standard Configuration Register (SDO_CONFIG) */ -#define SDO_PROGRESSIVE (1 << 4) -#define SDO_NTSC_M 0 -#define SDO_PAL_M 1 -#define SDO_PAL_BGHID 2 -#define SDO_PAL_N 3 -#define SDO_PAL_NC 4 -#define SDO_NTSC_443 8 -#define SDO_PAL_60 9 -#define SDO_STANDARD_MASK 0xf - -/* SDO VBI Configuration Register (SDO_VBI) */ -#define SDO_CVBS_WSS_INS (1 << 14) -#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12) - -/* SDO DAC Configuration Register (SDO_DAC) */ -#define SDO_POWER_ON_DAC (1 << 0) - -/* SDO Color Compensation On/Off Control (SDO_CCCON) */ -#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4) -#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0) - -/* SDO Interrupt Request Register (SDO_IRQ) */ -#define SDO_VSYNC_IRQ_PEND (1 << 0) - -#endif /* SAMSUNG_REGS_SDO_H */ diff --git a/drivers/media/platform/s5p-tv/regs-vp.h b/drivers/media/platform/s5p-tv/regs-vp.h deleted file mode 100644 index 6c63984e11e8..000000000000 --- a/drivers/media/platform/s5p-tv/regs-vp.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Video processor register header file for Samsung Mixer driver - * - * 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. - */ - -#ifndef SAMSUNG_REGS_VP_H -#define SAMSUNG_REGS_VP_H - -/* - * Register part - */ - -#define VP_ENABLE 0x0000 -#define VP_SRESET 0x0004 -#define VP_SHADOW_UPDATE 0x0008 -#define VP_FIELD_ID 0x000C -#define VP_MODE 0x0010 -#define VP_IMG_SIZE_Y 0x0014 -#define VP_IMG_SIZE_C 0x0018 -#define VP_PER_RATE_CTRL 0x001C -#define VP_TOP_Y_PTR 0x0028 -#define VP_BOT_Y_PTR 0x002C -#define VP_TOP_C_PTR 0x0030 -#define VP_BOT_C_PTR 0x0034 -#define VP_ENDIAN_MODE 0x03CC -#define VP_SRC_H_POSITION 0x0044 -#define VP_SRC_V_POSITION 0x0048 -#define VP_SRC_WIDTH 0x004C -#define VP_SRC_HEIGHT 0x0050 -#define VP_DST_H_POSITION 0x0054 -#define VP_DST_V_POSITION 0x0058 -#define VP_DST_WIDTH 0x005C -#define VP_DST_HEIGHT 0x0060 -#define VP_H_RATIO 0x0064 -#define VP_V_RATIO 0x0068 -#define VP_POLY8_Y0_LL 0x006C -#define VP_POLY4_Y0_LL 0x00EC -#define VP_POLY4_C0_LL 0x012C - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ - -#define VP_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define VP_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & VP_MASK(high_bit, low_bit)) - - /* VP_ENABLE */ -#define VP_ENABLE_ON (1 << 0) - -/* VP_SRESET */ -#define VP_SRESET_PROCESSING (1 << 0) - -/* VP_SHADOW_UPDATE */ -#define VP_SHADOW_UPDATE_ENABLE (1 << 0) - -/* VP_MODE */ -#define VP_MODE_NV12 (0 << 6) -#define VP_MODE_NV21 (1 << 6) -#define VP_MODE_LINE_SKIP (1 << 5) -#define VP_MODE_MEM_LINEAR (0 << 4) -#define VP_MODE_MEM_TILED (1 << 4) -#define VP_MODE_FMT_MASK (5 << 4) -#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2) -#define VP_MODE_2D_IPC (1 << 1) - -/* VP_IMG_SIZE_Y */ -/* VP_IMG_SIZE_C */ -#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16) -#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0) - -/* VP_SRC_H_POSITION */ -#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4) - -/* VP_ENDIAN_MODE */ -#define VP_ENDIAN_MODE_LITTLE (1 << 0) - -#endif /* SAMSUNG_REGS_VP_H */ diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c deleted file mode 100644 index c75d4354d182..000000000000 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Samsung Standard Definition Output (SDO) driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> - -#include <media/v4l2-subdev.h> - -#include "regs-sdo.h" - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)"); -MODULE_LICENSE("GPL"); - -#define SDO_DEFAULT_STD V4L2_STD_PAL - -struct sdo_format { - v4l2_std_id id; - /* all modes are 720 pixels wide */ - unsigned int height; - unsigned int cookie; -}; - -struct sdo_device { - /** pointer to device parent */ - struct device *dev; - /** base address of SDO registers */ - void __iomem *regs; - /** SDO interrupt */ - unsigned int irq; - /** DAC source clock */ - struct clk *sclk_dac; - /** DAC clock */ - struct clk *dac; - /** DAC physical interface */ - struct clk *dacphy; - /** clock for control of VPLL */ - struct clk *fout_vpll; - /** vpll rate before sdo stream was on */ - unsigned long vpll_rate; - /** regulator for SDO IP power */ - struct regulator *vdac; - /** regulator for SDO plug detection */ - struct regulator *vdet; - /** subdev used as device interface */ - struct v4l2_subdev sd; - /** current format */ - const struct sdo_format *fmt; -}; - -static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sdo_device, sd); -} - -static inline -void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask) -{ - u32 old = readl(sdev->regs + reg_id); - value = (value & mask) | (old & ~mask); - writel(value, sdev->regs + reg_id); -} - -static inline -void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value) -{ - writel(value, sdev->regs + reg_id); -} - -static inline -u32 sdo_read(struct sdo_device *sdev, u32 reg_id) -{ - return readl(sdev->regs + reg_id); -} - -static irqreturn_t sdo_irq_handler(int irq, void *dev_data) -{ - struct sdo_device *sdev = dev_data; - - /* clear interrupt */ - sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND); - return IRQ_HANDLED; -} - -static void sdo_reg_debug(struct sdo_device *sdev) -{ -#define DBGREG(reg_id) \ - dev_info(sdev->dev, #reg_id " = %08x\n", \ - sdo_read(sdev, reg_id)) - - DBGREG(SDO_CLKCON); - DBGREG(SDO_CONFIG); - DBGREG(SDO_VBI); - DBGREG(SDO_DAC); - DBGREG(SDO_IRQ); - DBGREG(SDO_IRQMASK); - DBGREG(SDO_VERSION); -} - -static const struct sdo_format sdo_format[] = { - { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N }, - { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC }, - { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M }, - { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 }, - { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 }, - { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID }, - { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M }, -}; - -static const struct sdo_format *sdo_find_format(v4l2_std_id id) -{ - int i; - for (i = 0; i < ARRAY_SIZE(sdo_format); ++i) - if (sdo_format[i].id & id) - return &sdo_format[i]; - return NULL; -} - -static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL | - V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | - V4L2_STD_NTSC_443 | V4L2_STD_PAL_60; - return 0; -} - -static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - const struct sdo_format *fmt; - fmt = sdo_find_format(std); - if (fmt == NULL) - return -EINVAL; - sdev->fmt = fmt; - return 0; -} - -static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *std = sd_to_sdev(sd)->fmt->id; - return 0; -} - -static int sdo_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct sdo_device *sdev = sd_to_sdev(sd); - - if (!sdev->fmt) - return -ENXIO; - if (format->pad) - return -EINVAL; - /* all modes are 720 pixels wide */ - fmt->width = 720; - fmt->height = sdev->fmt->height; - fmt->code = MEDIA_BUS_FMT_FIXED; - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_JPEG; - return 0; -} - -static int sdo_s_power(struct v4l2_subdev *sd, int on) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - struct device *dev = sdev->dev; - int ret; - - dev_info(dev, "sdo_s_power(%d)\n", on); - - if (on) - ret = pm_runtime_get_sync(dev); - else - ret = pm_runtime_put_sync(dev); - - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -static int sdo_streamon(struct sdo_device *sdev) -{ - int ret; - - /* set proper clock for Timing Generator */ - sdev->vpll_rate = clk_get_rate(sdev->fout_vpll); - ret = clk_set_rate(sdev->fout_vpll, 54000000); - if (ret < 0) { - dev_err(sdev->dev, "Failed to set vpll rate\n"); - return ret; - } - dev_info(sdev->dev, "fout_vpll.rate = %lu\n", - clk_get_rate(sdev->fout_vpll)); - /* enable clock in SDO */ - sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON); - ret = clk_prepare_enable(sdev->dacphy); - if (ret < 0) { - dev_err(sdev->dev, "clk_prepare_enable(dacphy) failed\n"); - goto fail; - } - /* enable DAC */ - sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC); - sdo_reg_debug(sdev); - return 0; - -fail: - sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); - clk_set_rate(sdev->fout_vpll, sdev->vpll_rate); - return ret; -} - -static int sdo_streamoff(struct sdo_device *sdev) -{ - int tries; - - sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC); - clk_disable_unprepare(sdev->dacphy); - sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); - for (tries = 100; tries; --tries) { - if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY) - break; - mdelay(1); - } - if (tries == 0) - dev_err(sdev->dev, "failed to stop streaming\n"); - clk_set_rate(sdev->fout_vpll, sdev->vpll_rate); - return tries ? 0 : -EIO; -} - -static int sdo_s_stream(struct v4l2_subdev *sd, int on) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - return on ? sdo_streamon(sdev) : sdo_streamoff(sdev); -} - -static const struct v4l2_subdev_core_ops sdo_sd_core_ops = { - .s_power = sdo_s_power, -}; - -static const struct v4l2_subdev_video_ops sdo_sd_video_ops = { - .s_std_output = sdo_s_std_output, - .g_std_output = sdo_g_std_output, - .g_tvnorms_output = sdo_g_tvnorms_output, - .s_stream = sdo_s_stream, -}; - -static const struct v4l2_subdev_pad_ops sdo_sd_pad_ops = { - .get_fmt = sdo_get_fmt, -}; - -static const struct v4l2_subdev_ops sdo_sd_ops = { - .core = &sdo_sd_core_ops, - .video = &sdo_sd_video_ops, - .pad = &sdo_sd_pad_ops, -}; - -static int sdo_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sdo_device *sdev = sd_to_sdev(sd); - - dev_info(dev, "suspend\n"); - regulator_disable(sdev->vdet); - regulator_disable(sdev->vdac); - clk_disable_unprepare(sdev->sclk_dac); - return 0; -} - -static int sdo_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sdo_device *sdev = sd_to_sdev(sd); - int ret; - - dev_info(dev, "resume\n"); - - ret = clk_prepare_enable(sdev->sclk_dac); - if (ret < 0) - return ret; - - ret = regulator_enable(sdev->vdac); - if (ret < 0) - goto dac_clk_dis; - - ret = regulator_enable(sdev->vdet); - if (ret < 0) - goto vdac_r_dis; - - /* software reset */ - sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET); - mdelay(10); - sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET); - - /* setting TV mode */ - sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK); - /* XXX: forcing interlaced mode using undocumented bit */ - sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE); - /* turn all VBI off */ - sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS | - SDO_CVBS_CLOSED_CAPTION_MASK); - /* turn all post processing off */ - sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF | - SDO_COMPENSATION_CVBS_COMP_OFF); - sdo_reg_debug(sdev); - return 0; - -vdac_r_dis: - regulator_disable(sdev->vdac); -dac_clk_dis: - clk_disable_unprepare(sdev->sclk_dac); - return ret; -} - -static const struct dev_pm_ops sdo_pm_ops = { - .runtime_suspend = sdo_runtime_suspend, - .runtime_resume = sdo_runtime_resume, -}; - -static int sdo_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct sdo_device *sdev; - struct resource *res; - int ret = 0; - struct clk *sclk_vpll; - - dev_info(dev, "probe start\n"); - sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); - if (!sdev) { - dev_err(dev, "not enough memory.\n"); - ret = -ENOMEM; - goto fail; - } - sdev->dev = dev; - - /* mapping registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail; - } - - sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (sdev->regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail; - } - - /* acquiring interrupt */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail; - } - ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0, - "s5p-sdo", sdev); - if (ret) { - dev_err(dev, "request interrupt failed.\n"); - goto fail; - } - sdev->irq = res->start; - - /* acquire clocks */ - sdev->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR(sdev->sclk_dac)) { - dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = PTR_ERR(sdev->sclk_dac); - goto fail; - } - sdev->dac = clk_get(dev, "dac"); - if (IS_ERR(sdev->dac)) { - dev_err(dev, "failed to get clock 'dac'\n"); - ret = PTR_ERR(sdev->dac); - goto fail_sclk_dac; - } - sdev->dacphy = clk_get(dev, "dacphy"); - if (IS_ERR(sdev->dacphy)) { - dev_err(dev, "failed to get clock 'dacphy'\n"); - ret = PTR_ERR(sdev->dacphy); - goto fail_dac; - } - sclk_vpll = clk_get(dev, "sclk_vpll"); - if (IS_ERR(sclk_vpll)) { - dev_err(dev, "failed to get clock 'sclk_vpll'\n"); - ret = PTR_ERR(sclk_vpll); - goto fail_dacphy; - } - clk_set_parent(sdev->sclk_dac, sclk_vpll); - clk_put(sclk_vpll); - sdev->fout_vpll = clk_get(dev, "fout_vpll"); - if (IS_ERR(sdev->fout_vpll)) { - dev_err(dev, "failed to get clock 'fout_vpll'\n"); - ret = PTR_ERR(sdev->fout_vpll); - goto fail_dacphy; - } - dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll)); - - /* acquire regulator */ - sdev->vdac = devm_regulator_get(dev, "vdd33a_dac"); - if (IS_ERR(sdev->vdac)) { - dev_err(dev, "failed to get regulator 'vdac'\n"); - ret = PTR_ERR(sdev->vdac); - goto fail_fout_vpll; - } - sdev->vdet = devm_regulator_get(dev, "vdet"); - if (IS_ERR(sdev->vdet)) { - dev_err(dev, "failed to get regulator 'vdet'\n"); - ret = PTR_ERR(sdev->vdet); - goto fail_fout_vpll; - } - - /* enable gate for dac clock, because mixer uses it */ - ret = clk_prepare_enable(sdev->dac); - if (ret < 0) { - dev_err(dev, "clk_prepare_enable(dac) failed\n"); - goto fail_fout_vpll; - } - - /* configure power management */ - pm_runtime_enable(dev); - - /* configuration of interface subdevice */ - v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); - sdev->sd.owner = THIS_MODULE; - strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name)); - - /* set default format */ - sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); - BUG_ON(sdev->fmt == NULL); - - /* keeping subdev in device's private for use by other drivers */ - dev_set_drvdata(dev, &sdev->sd); - - dev_info(dev, "probe succeeded\n"); - return 0; - -fail_fout_vpll: - clk_put(sdev->fout_vpll); -fail_dacphy: - clk_put(sdev->dacphy); -fail_dac: - clk_put(sdev->dac); -fail_sclk_dac: - clk_put(sdev->sclk_dac); -fail: - dev_info(dev, "probe failed\n"); - return ret; -} - -static int sdo_remove(struct platform_device *pdev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); - struct sdo_device *sdev = sd_to_sdev(sd); - - pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(sdev->dac); - clk_put(sdev->fout_vpll); - clk_put(sdev->dacphy); - clk_put(sdev->dac); - clk_put(sdev->sclk_dac); - - dev_info(&pdev->dev, "remove successful\n"); - return 0; -} - -static struct platform_driver sdo_driver __refdata = { - .probe = sdo_probe, - .remove = sdo_remove, - .driver = { - .name = "s5p-sdo", - .pm = &sdo_pm_ops, - } -}; - -module_platform_driver(sdo_driver); diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c deleted file mode 100644 index 0a97f9ab4f76..000000000000 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Samsung MHL interface driver - * - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Author: Tomasz Stanislawski <t.stanislaws@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/delay.h> -#include <linux/err.h> -#include <linux/freezer.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/kthread.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/machine.h> -#include <linux/slab.h> - -#include <linux/platform_data/media/sii9234.h> -#include <media/v4l2-subdev.h> - -MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung MHL interface driver"); -MODULE_LICENSE("GPL"); - -struct sii9234_context { - struct i2c_client *client; - struct regulator *power; - int gpio_n_reset; - struct v4l2_subdev sd; -}; - -static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sii9234_context, sd); -} - -static inline int sii9234_readb(struct i2c_client *client, int addr) -{ - return i2c_smbus_read_byte_data(client, addr); -} - -static inline int sii9234_writeb(struct i2c_client *client, int addr, int value) -{ - return i2c_smbus_write_byte_data(client, addr, value); -} - -static inline int sii9234_writeb_mask(struct i2c_client *client, int addr, - int value, int mask) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, addr); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (value & mask); - return i2c_smbus_write_byte_data(client, addr, ret); -} - -static inline int sii9234_readb_idx(struct i2c_client *client, int addr) -{ - int ret; - ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); - if (ret < 0) - return ret; - return i2c_smbus_read_byte_data(client, 0xbe); -} - -static inline int sii9234_writeb_idx(struct i2c_client *client, int addr, - int value) -{ - int ret; - ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbe, value); - return ret; -} - -static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr, - int value, int mask) -{ - int ret; - - ret = sii9234_readb_idx(client, addr); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (value & mask); - return sii9234_writeb_idx(client, addr, ret); -} - -static int sii9234_reset(struct sii9234_context *ctx) -{ - struct i2c_client *client = ctx->client; - struct device *dev = &client->dev; - int ret, tries; - - gpio_direction_output(ctx->gpio_n_reset, 1); - mdelay(1); - gpio_direction_output(ctx->gpio_n_reset, 0); - mdelay(1); - gpio_direction_output(ctx->gpio_n_reset, 1); - mdelay(1); - - /* going to TTPI mode */ - ret = sii9234_writeb(client, 0xc7, 0); - if (ret < 0) { - dev_err(dev, "failed to set TTPI mode\n"); - return ret; - } - for (tries = 0; tries < 100 ; ++tries) { - ret = sii9234_readb(client, 0x1b); - if (ret > 0) - break; - if (ret < 0) { - dev_err(dev, "failed to reset device\n"); - return -EIO; - } - mdelay(1); - } - if (tries == 100) { - dev_err(dev, "maximal number of tries reached\n"); - return -EIO; - } - - return 0; -} - -static int sii9234_verify_version(struct i2c_client *client) -{ - struct device *dev = &client->dev; - int family, rev, tpi_rev, dev_id, sub_id, hdcp, id; - - family = sii9234_readb(client, 0x1b); - rev = sii9234_readb(client, 0x1c) & 0x0f; - tpi_rev = sii9234_readb(client, 0x1d) & 0x7f; - dev_id = sii9234_readb_idx(client, 0x0103); - sub_id = sii9234_readb_idx(client, 0x0102); - hdcp = sii9234_readb(client, 0x30); - - if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 || - sub_id < 0 || hdcp < 0) { - dev_err(dev, "failed to read chip's version\n"); - return -EIO; - } - - id = (dev_id << 8) | sub_id; - - dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n", - id, family, rev); - dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp); - if (id != 0x9234) { - dev_err(dev, "not supported chip\n"); - return -ENODEV; - } - - return 0; -} - -static u8 data[][3] = { -/* setup from driver created by doonsoo45.kim */ - { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */ - { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */ - { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */ - { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */ - { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */ - { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */ - { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */ - { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */ - { 0x01, 0x92, 0x46 }, /* Force MHD mode */ - { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */ - { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */ - { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */ - { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */ - { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */ - { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */ - { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */ - { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */ - { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */ - { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */ - { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */ - { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */ - { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */ - { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */ - { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */ - { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */ - { 0x03, 0x1a, 0x20 }, /* VCO Cal */ - { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */ - { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */ - { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */ - { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */ - { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */ - { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */ - { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */ - { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */ - { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */ - { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */ - { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */ - { 0x03, 0x4c, 0xa0 }, /* Manual zone control */ - { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */ -}; - -static int sii9234_set_internal(struct sii9234_context *ctx) -{ - struct i2c_client *client = ctx->client; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(data); ++i) { - int addr = (data[i][0] << 8) | data[i][1]; - ret = sii9234_writeb_idx(client, addr, data[i][2]); - if (ret < 0) - return ret; - } - return 0; -} - -static int sii9234_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sii9234_context *ctx = sd_to_context(sd); - struct i2c_client *client = ctx->client; - - dev_info(dev, "suspend start\n"); - - sii9234_writeb_mask(client, 0x1e, 3, 3); - regulator_disable(ctx->power); - - return 0; -} - -static int sii9234_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sii9234_context *ctx = sd_to_context(sd); - struct i2c_client *client = ctx->client; - int ret; - - dev_info(dev, "resume start\n"); - ret = regulator_enable(ctx->power); - if (ret < 0) - return ret; - - ret = sii9234_reset(ctx); - if (ret) - goto fail; - - /* enable tpi */ - ret = sii9234_writeb_mask(client, 0x1e, 1, 0); - if (ret < 0) - goto fail; - ret = sii9234_set_internal(ctx); - if (ret < 0) - goto fail; - - return 0; - -fail: - dev_err(dev, "failed to resume\n"); - regulator_disable(ctx->power); - - return ret; -} - -static const struct dev_pm_ops sii9234_pm_ops = { - .runtime_suspend = sii9234_runtime_suspend, - .runtime_resume = sii9234_runtime_resume, -}; - -static int sii9234_s_power(struct v4l2_subdev *sd, int on) -{ - struct sii9234_context *ctx = sd_to_context(sd); - int ret; - - if (on) - ret = pm_runtime_get_sync(&ctx->client->dev); - else - ret = pm_runtime_put(&ctx->client->dev); - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -static int sii9234_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct sii9234_context *ctx = sd_to_context(sd); - - /* (dis/en)able TDMS output */ - sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4); - return 0; -} - -static const struct v4l2_subdev_core_ops sii9234_core_ops = { - .s_power = sii9234_s_power, -}; - -static const struct v4l2_subdev_video_ops sii9234_video_ops = { - .s_stream = sii9234_s_stream, -}; - -static const struct v4l2_subdev_ops sii9234_ops = { - .core = &sii9234_core_ops, - .video = &sii9234_video_ops, -}; - -static int sii9234_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct sii9234_platform_data *pdata = dev->platform_data; - struct sii9234_context *ctx; - int ret; - - ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - dev_err(dev, "out of memory\n"); - ret = -ENOMEM; - goto fail; - } - ctx->client = client; - - ctx->power = devm_regulator_get(dev, "hdmi-en"); - if (IS_ERR(ctx->power)) { - dev_err(dev, "failed to acquire regulator hdmi-en\n"); - return PTR_ERR(ctx->power); - } - - ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); - if (ret) { - dev_err(dev, "failed to acquire MHL_RST gpio\n"); - return ret; - } - - v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops); - - pm_runtime_enable(dev); - - /* enable device */ - ret = pm_runtime_get_sync(dev); - if (ret) - goto fail_pm; - - /* verify chip version */ - ret = sii9234_verify_version(client); - if (ret) - goto fail_pm_get; - - /* stop processing */ - pm_runtime_put(dev); - - dev_info(dev, "probe successful\n"); - - return 0; - -fail_pm_get: - pm_runtime_put_sync(dev); - -fail_pm: - pm_runtime_disable(dev); - -fail: - dev_err(dev, "probe failed\n"); - - return ret; -} - -static int sii9234_remove(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - pm_runtime_disable(dev); - - dev_info(dev, "remove successful\n"); - - return 0; -} - - -static const struct i2c_device_id sii9234_id[] = { - { "SII9234", 0 }, - { }, -}; - -MODULE_DEVICE_TABLE(i2c, sii9234_id); -static struct i2c_driver sii9234_driver = { - .driver = { - .name = "sii9234", - .pm = &sii9234_pm_ops, - }, - .probe = sii9234_probe, - .remove = sii9234_remove, - .id_table = sii9234_id, -}; - -module_i2c_driver(sii9234_driver); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index e1f39b4cf1cd..ef2a519bcd4c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -362,7 +362,7 @@ static void sh_vou_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&vou_dev->lock, flags); } -static struct vb2_ops sh_vou_qops = { +static const struct vb2_ops sh_vou_qops = { .queue_setup = sh_vou_queue_setup, .buf_prepare = sh_vou_buf_prepare, .buf_queue = sh_vou_buf_queue, @@ -937,7 +937,10 @@ static int sh_vou_s_selection(struct file *file, void *fh, { struct v4l2_rect *rect = &sel->r; struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; + struct v4l2_subdev_selection sd_sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_COMPOSE, + }; struct v4l2_pix_format *pix = &vou_dev->pix; struct sh_vou_geometry geo; struct v4l2_subdev_format format = { @@ -978,14 +981,14 @@ static int sh_vou_s_selection(struct file *file, void *fh, geo.in_height = pix->height; /* Configure the encoder one-to-one, position at 0, ignore errors */ - sd_crop.c.width = geo.output.width; - sd_crop.c.height = geo.output.height; + sd_sel.r.width = geo.output.width; + sd_sel.r.height = geo.output.height; /* - * We first issue a S_CROP, so that the subsequent S_FMT delivers the + * We first issue a S_SELECTION, so that the subsequent S_FMT delivers the * final encoder configuration. */ - v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_crop, &sd_crop); + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, + set_selection, NULL, &sd_sel); format.format.width = geo.output.width; format.format.height = geo.output.height; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 39f66414f621..86d74788544f 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -17,31 +17,6 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing -config VIDEO_PXA27x - tristate "PXA27x Quick Capture Interface driver" - depends on VIDEO_DEV && PXA27x && SOC_CAMERA - select VIDEOBUF_DMA_SG - select SG_SPLIT - ---help--- - This is a v4l2 driver for the PXA27x Quick Capture Interface - -config VIDEO_RCAR_VIN_OLD - tristate "R-Car Video Input (VIN) support (DEPRECATED)" - depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_RENESAS || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - select SOC_CAMERA_SCALE_CROP - ---help--- - This is a v4l2 driver for the R-Car VIN Interface - -config VIDEO_SH_MOBILE_CSI2 - tristate "SuperH Mobile MIPI CSI-2 Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK - depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST - ---help--- - This is a v4l2 driver for the SuperH MIPI CSI-2 Interface - config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7703cb7ce456..7633a0f2f66f 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -7,7 +7,4 @@ obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o -obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o -obj-$(CONFIG_VIDEO_RCAR_VIN_OLD) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 30211f6b4483..46de657c3e6d 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -536,7 +536,7 @@ static void stop_streaming(struct vb2_queue *vq) pm_runtime_put(ici->v4l2_dev.dev); } -static struct vb2_ops isi_video_qops = { +static const struct vb2_ops isi_video_qops = { .queue_setup = queue_setup, .buf_init = buffer_init, .buf_prepare = buffer_prepare, diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c deleted file mode 100644 index 9c137522c660..000000000000 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * SoC-camera host driver for Renesas R-Car VIN unit - * - * Copyright (C) 2011-2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> - * - * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c" - * - * Copyright (C) 2008 Magnus Damm - * - * 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/delay.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/videodev2.h> - -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-mediabus.h> -#include <media/v4l2-of.h> -#include <media/v4l2-subdev.h> -#include <media/videobuf2-dma-contig.h> - -#include "soc_scale_crop.h" - -#define DRV_NAME "rcar_vin" - -/* Register offsets for R-Car VIN */ -#define VNMC_REG 0x00 /* Video n Main Control Register */ -#define VNMS_REG 0x04 /* Video n Module Status Register */ -#define VNFC_REG 0x08 /* Video n Frame Capture Register */ -#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ -#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ -#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ -#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ -#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ -#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ -#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ -#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ -#define VNIS_REG 0x2C /* Video n Image Stride Register */ -#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ -#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ -#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ -#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ -#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ -#define VNYS_REG 0x50 /* Video n Y Scale Register */ -#define VNXS_REG 0x54 /* Video n X Scale Register */ -#define VNDMR_REG 0x58 /* Video n Data Mode Register */ -#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ -#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ -#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ -#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ -#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ -#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ -#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ -#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ -#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ -#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ -#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ -#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ -#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ -#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ -#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ -#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ -#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ -#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ -#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ -#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ -#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ -#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ -#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ -#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ -#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ -#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ - -/* Register bit fields for R-Car VIN */ -/* Video n Main Control Register bits */ -#define VNMC_FOC (1 << 21) -#define VNMC_YCAL (1 << 19) -#define VNMC_INF_YUV8_BT656 (0 << 16) -#define VNMC_INF_YUV8_BT601 (1 << 16) -#define VNMC_INF_YUV10_BT656 (2 << 16) -#define VNMC_INF_YUV10_BT601 (3 << 16) -#define VNMC_INF_YUV16 (5 << 16) -#define VNMC_INF_RGB888 (6 << 16) -#define VNMC_VUP (1 << 10) -#define VNMC_IM_ODD (0 << 3) -#define VNMC_IM_ODD_EVEN (1 << 3) -#define VNMC_IM_EVEN (2 << 3) -#define VNMC_IM_FULL (3 << 3) -#define VNMC_BPS (1 << 1) -#define VNMC_ME (1 << 0) - -/* Video n Module Status Register bits */ -#define VNMS_FBS_MASK (3 << 3) -#define VNMS_FBS_SHIFT 3 -#define VNMS_AV (1 << 1) -#define VNMS_CA (1 << 0) - -/* Video n Frame Capture Register bits */ -#define VNFC_C_FRAME (1 << 1) -#define VNFC_S_FRAME (1 << 0) - -/* Video n Interrupt Enable Register bits */ -#define VNIE_FIE (1 << 4) -#define VNIE_EFE (1 << 1) - -/* Video n Data Mode Register bits */ -#define VNDMR_EXRGB (1 << 8) -#define VNDMR_BPSM (1 << 4) -#define VNDMR_DTMD_YCSEP (1 << 1) -#define VNDMR_DTMD_ARGB (1 << 0) - -/* Video n Data Mode Register 2 bits */ -#define VNDMR2_VPS (1 << 30) -#define VNDMR2_HPS (1 << 29) -#define VNDMR2_FTEV (1 << 17) -#define VNDMR2_VLV(n) ((n & 0xf) << 12) - -#define VIN_MAX_WIDTH 2048 -#define VIN_MAX_HEIGHT 2048 - -#define TIMEOUT_MS 100 - -#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0) -#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1) -#define RCAR_VIN_BT601 (1 << 2) -#define RCAR_VIN_BT656 (1 << 3) - -enum chip_id { - RCAR_GEN3, - RCAR_GEN2, - RCAR_H1, - RCAR_M1, - RCAR_E1, -}; - -struct vin_coeff { - unsigned short xs_value; - u32 coeff_set[24]; -}; - -static const struct vin_coeff vin_coeff_set[] = { - { 0x0000, { - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000 }, - }, - { 0x1000, { - 0x000fa400, 0x000fa400, 0x09625902, - 0x000003f8, 0x00000403, 0x3de0d9f0, - 0x001fffed, 0x00000804, 0x3cc1f9c3, - 0x001003de, 0x00000c01, 0x3cb34d7f, - 0x002003d2, 0x00000c00, 0x3d24a92d, - 0x00200bca, 0x00000bff, 0x3df600d2, - 0x002013cc, 0x000007ff, 0x3ed70c7e, - 0x00100fde, 0x00000000, 0x3f87c036 }, - }, - { 0x1200, { - 0x002ffff1, 0x002ffff1, 0x02a0a9c8, - 0x002003e7, 0x001ffffa, 0x000185bc, - 0x002007dc, 0x000003ff, 0x3e52859c, - 0x00200bd4, 0x00000002, 0x3d53996b, - 0x00100fd0, 0x00000403, 0x3d04ad2d, - 0x00000bd5, 0x00000403, 0x3d35ace7, - 0x3ff003e4, 0x00000801, 0x3dc674a1, - 0x3fffe800, 0x00000800, 0x3e76f461 }, - }, - { 0x1400, { - 0x00100be3, 0x00100be3, 0x04d1359a, - 0x00000fdb, 0x002003ed, 0x0211fd93, - 0x00000fd6, 0x002003f4, 0x0002d97b, - 0x000007d6, 0x002ffffb, 0x3e93b956, - 0x3ff003da, 0x001003ff, 0x3db49926, - 0x3fffefe9, 0x00100001, 0x3d655cee, - 0x3fffd400, 0x00000003, 0x3d65f4b6, - 0x000fb421, 0x00000402, 0x3dc6547e }, - }, - { 0x1600, { - 0x00000bdd, 0x00000bdd, 0x06519578, - 0x3ff007da, 0x00000be3, 0x03c24973, - 0x3ff003d9, 0x00000be9, 0x01b30d5f, - 0x3ffff7df, 0x001003f1, 0x0003c542, - 0x000fdfec, 0x001003f7, 0x3ec4711d, - 0x000fc400, 0x002ffffd, 0x3df504f1, - 0x001fa81a, 0x002ffc00, 0x3d957cc2, - 0x002f8c3c, 0x00100000, 0x3db5c891 }, - }, - { 0x1800, { - 0x3ff003dc, 0x3ff003dc, 0x0791e558, - 0x000ff7dd, 0x3ff007de, 0x05328554, - 0x000fe7e3, 0x3ff00be2, 0x03232546, - 0x000fd7ee, 0x000007e9, 0x0143bd30, - 0x001fb800, 0x000007ee, 0x00044511, - 0x002fa015, 0x000007f4, 0x3ef4bcee, - 0x002f8832, 0x001003f9, 0x3e4514c7, - 0x001f7853, 0x001003fd, 0x3de54c9f }, - }, - { 0x1a00, { - 0x000fefe0, 0x000fefe0, 0x08721d3c, - 0x001fdbe7, 0x000ffbde, 0x0652a139, - 0x001fcbf0, 0x000003df, 0x0463292e, - 0x002fb3ff, 0x3ff007e3, 0x0293a91d, - 0x002f9c12, 0x3ff00be7, 0x01241905, - 0x001f8c29, 0x000007ed, 0x3fe470eb, - 0x000f7c46, 0x000007f2, 0x3f04b8ca, - 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, - }, - { 0x1c00, { - 0x001fd3e9, 0x001fd3e9, 0x08f23d26, - 0x002fbff3, 0x001fe3e4, 0x0712ad23, - 0x002fa800, 0x000ff3e0, 0x05631d1b, - 0x001f9810, 0x000ffbe1, 0x03b3890d, - 0x000f8c23, 0x000003e3, 0x0233e8fa, - 0x3fef843b, 0x000003e7, 0x00f430e4, - 0x3fbf8456, 0x3ff00bea, 0x00046cc8, - 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, - }, - { 0x1e00, { - 0x001fbbf4, 0x001fbbf4, 0x09425112, - 0x001fa800, 0x002fc7ed, 0x0792b110, - 0x000f980e, 0x001fdbe6, 0x0613110a, - 0x3fff8c20, 0x001fe7e3, 0x04a368fd, - 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, - 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, - 0x3f5f9c61, 0x000003e6, 0x00e428c5, - 0x3f1fb07b, 0x000003eb, 0x3fe440af }, - }, - { 0x2000, { - 0x000fa400, 0x000fa400, 0x09625902, - 0x3fff980c, 0x001fb7f5, 0x0812b0ff, - 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, - 0x3faf902d, 0x001fd3e8, 0x055348f1, - 0x3f7f983f, 0x001fe3e5, 0x04038ce3, - 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, - 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, - 0x3ecfd880, 0x000fffe6, 0x00c404ac }, - }, - { 0x2200, { - 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, - 0x3fbf9818, 0x3fffa400, 0x0842a8f1, - 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, - 0x3f5fa037, 0x000fc3ef, 0x05d330e4, - 0x3f2fac49, 0x001fcfea, 0x04a364d9, - 0x3effc05c, 0x001fdbe7, 0x038394ca, - 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, - 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, - }, - { 0x2400, { - 0x3f9fa014, 0x3f9fa014, 0x098260e6, - 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, - 0x3f4fa431, 0x3fefa400, 0x0742d8e1, - 0x3f1fb440, 0x3fffb3f8, 0x062310d9, - 0x3eefc850, 0x000fbbf2, 0x050340d0, - 0x3ecfe062, 0x000fcbec, 0x041364c2, - 0x3ea00073, 0x001fd3ea, 0x03037cb5, - 0x3e902086, 0x001fdfe8, 0x022388a5 }, - }, - { 0x2600, { - 0x3f5fa81e, 0x3f5fa81e, 0x096258da, - 0x3f3fac2b, 0x3f8fa412, 0x088290d8, - 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, - 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, - 0x3ecfe456, 0x3fefaffa, 0x05531cc6, - 0x3eb00066, 0x3fffbbf3, 0x047334bb, - 0x3ea01c77, 0x000fc7ee, 0x039348ae, - 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, - }, - { 0x2800, { - 0x3f2fb426, 0x3f2fb426, 0x094250ce, - 0x3f0fc032, 0x3f4fac1b, 0x086284cd, - 0x3eefd040, 0x3f7fa811, 0x0782acc9, - 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, - 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, - 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, - 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, - 0x3ec06884, 0x000fbff2, 0x03031c9e }, - }, - { 0x2a00, { - 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, - 0x3eefd439, 0x3f2fb822, 0x08526cc2, - 0x3edfe845, 0x3f4fb018, 0x078294bf, - 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, - 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, - 0x3ec0386b, 0x3fafac00, 0x0502e8ac, - 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, - 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, - }, - { 0x2c00, { - 0x3eefdc31, 0x3eefdc31, 0x08e238b8, - 0x3edfec3d, 0x3f0fc828, 0x082258b9, - 0x3ed00049, 0x3f1fc01e, 0x077278b6, - 0x3ed01455, 0x3f3fb815, 0x06c294b2, - 0x3ed03460, 0x3f5fb40d, 0x0602acac, - 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, - 0x3f107476, 0x3f9fb400, 0x0472c89d, - 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, - }, - { 0x2e00, { - 0x3eefec37, 0x3eefec37, 0x088220b0, - 0x3ee00041, 0x3effdc2d, 0x07f244ae, - 0x3ee0144c, 0x3f0fd023, 0x07625cad, - 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, - 0x3f004861, 0x3f3fbc13, 0x060288a6, - 0x3f20686b, 0x3f5fb80c, 0x05529c9e, - 0x3f408c74, 0x3f6fb805, 0x04b2ac96, - 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, - }, - { 0x3000, { - 0x3ef0003a, 0x3ef0003a, 0x084210a6, - 0x3ef01045, 0x3effec32, 0x07b228a7, - 0x3f00284e, 0x3f0fdc29, 0x073244a4, - 0x3f104058, 0x3f0fd420, 0x06a258a2, - 0x3f305c62, 0x3f2fc818, 0x0612689d, - 0x3f508069, 0x3f3fc011, 0x05728496, - 0x3f80a072, 0x3f4fc00a, 0x04d28c90, - 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, - }, - { 0x3200, { - 0x3f00103e, 0x3f00103e, 0x07f1fc9e, - 0x3f102447, 0x3f000035, 0x0782149d, - 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, - 0x3f405458, 0x3f0fe424, 0x06924099, - 0x3f607061, 0x3f1fd41d, 0x06024c97, - 0x3f909068, 0x3f2fcc16, 0x05726490, - 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, - 0x0000d077, 0x3f4fc409, 0x04627484 }, - }, - { 0x3400, { - 0x3f202040, 0x3f202040, 0x07a1e898, - 0x3f303449, 0x3f100c38, 0x0741fc98, - 0x3f504c50, 0x3f10002f, 0x06e21495, - 0x3f706459, 0x3f1ff028, 0x06722492, - 0x3fa08060, 0x3f1fe421, 0x05f2348f, - 0x3fd09c67, 0x3f1fdc19, 0x05824c89, - 0x0000bc6e, 0x3f2fd014, 0x04f25086, - 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, - }, - { 0x3600, { - 0x3f403042, 0x3f403042, 0x0761d890, - 0x3f504848, 0x3f301c3b, 0x0701f090, - 0x3f805c50, 0x3f200c33, 0x06a2008f, - 0x3fa07458, 0x3f10002b, 0x06520c8d, - 0x3fd0905e, 0x3f1ff424, 0x05e22089, - 0x0000ac65, 0x3f1fe81d, 0x05823483, - 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, - 0x0080e871, 0x3f2fd412, 0x0482407c }, - }, - { 0x3800, { - 0x3f604043, 0x3f604043, 0x0721c88a, - 0x3f80544a, 0x3f502c3c, 0x06d1d88a, - 0x3fb06851, 0x3f301c35, 0x0681e889, - 0x3fd08456, 0x3f30082f, 0x0611fc88, - 0x00009c5d, 0x3f200027, 0x05d20884, - 0x0030b863, 0x3f2ff421, 0x05621880, - 0x0070d468, 0x3f2fe81b, 0x0502247c, - 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, - }, - { 0x3a00, { - 0x3f904c44, 0x3f904c44, 0x06e1b884, - 0x3fb0604a, 0x3f70383e, 0x0691c885, - 0x3fe07451, 0x3f502c36, 0x0661d483, - 0x00009055, 0x3f401831, 0x0601ec81, - 0x0030a85b, 0x3f300c2a, 0x05b1f480, - 0x0070c061, 0x3f300024, 0x0562047a, - 0x00b0d867, 0x3f3ff41e, 0x05020c77, - 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, - }, - { 0x3c00, { - 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, - 0x3fe06c4b, 0x3f902c3f, 0x0681c081, - 0x0000844f, 0x3f703838, 0x0631cc7d, - 0x00309855, 0x3f602433, 0x05d1d47e, - 0x0060b459, 0x3f50142e, 0x0581e47b, - 0x00a0c85f, 0x3f400828, 0x0531f078, - 0x00e0e064, 0x3f300021, 0x0501fc73, - 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, - }, - { 0x3e00, { - 0x3fe06444, 0x3fe06444, 0x0681a07a, - 0x00007849, 0x3fc0503f, 0x0641b07a, - 0x0020904d, 0x3fa0403a, 0x05f1c07a, - 0x0060a453, 0x3f803034, 0x05c1c878, - 0x0090b858, 0x3f70202f, 0x0571d477, - 0x00d0d05d, 0x3f501829, 0x0531e073, - 0x0110e462, 0x3f500825, 0x04e1e471, - 0x01510065, 0x3f40001f, 0x04a1f06d }, - }, - { 0x4000, { - 0x00007044, 0x00007044, 0x06519476, - 0x00208448, 0x3fe05c3f, 0x0621a476, - 0x0050984d, 0x3fc04c3a, 0x05e1b075, - 0x0080ac52, 0x3fa03c35, 0x05a1b875, - 0x00c0c056, 0x3f803030, 0x0561c473, - 0x0100d45b, 0x3f70202b, 0x0521d46f, - 0x0140e860, 0x3f601427, 0x04d1d46e, - 0x01810064, 0x3f500822, 0x0491dc6b }, - }, - { 0x5000, { - 0x0110a442, 0x0110a442, 0x0551545e, - 0x0140b045, 0x00e0983f, 0x0531585f, - 0x0160c047, 0x00c08c3c, 0x0511645e, - 0x0190cc4a, 0x00908039, 0x04f1685f, - 0x01c0dc4c, 0x00707436, 0x04d1705e, - 0x0200e850, 0x00506833, 0x04b1785b, - 0x0230f453, 0x00305c30, 0x0491805a, - 0x02710056, 0x0010542d, 0x04718059 }, - }, - { 0x6000, { - 0x01c0bc40, 0x01c0bc40, 0x04c13052, - 0x01e0c841, 0x01a0b43d, 0x04c13851, - 0x0210cc44, 0x0180a83c, 0x04a13453, - 0x0230d845, 0x0160a03a, 0x04913c52, - 0x0260e047, 0x01409838, 0x04714052, - 0x0280ec49, 0x01208c37, 0x04514c50, - 0x02b0f44b, 0x01008435, 0x04414c50, - 0x02d1004c, 0x00e07c33, 0x0431544f }, - }, - { 0x7000, { - 0x0230c83e, 0x0230c83e, 0x04711c4c, - 0x0250d03f, 0x0210c43c, 0x0471204b, - 0x0270d840, 0x0200b83c, 0x0451244b, - 0x0290dc42, 0x01e0b43a, 0x0441244c, - 0x02b0e443, 0x01c0b038, 0x0441284b, - 0x02d0ec44, 0x01b0a438, 0x0421304a, - 0x02f0f445, 0x0190a036, 0x04213449, - 0x0310f847, 0x01709c34, 0x04213848 }, - }, - { 0x8000, { - 0x0280d03d, 0x0280d03d, 0x04310c48, - 0x02a0d43e, 0x0270c83c, 0x04311047, - 0x02b0dc3e, 0x0250c83a, 0x04311447, - 0x02d0e040, 0x0240c03a, 0x04211446, - 0x02e0e840, 0x0220bc39, 0x04111847, - 0x0300e842, 0x0210b438, 0x04012445, - 0x0310f043, 0x0200b037, 0x04012045, - 0x0330f444, 0x01e0ac36, 0x03f12445 }, - }, - { 0xefff, { - 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, - 0x0340e03a, 0x0330e039, 0x03c0f03e, - 0x0350e03b, 0x0330dc39, 0x03c0ec3e, - 0x0350e43a, 0x0320dc38, 0x03c0f43e, - 0x0360e43b, 0x0320d839, 0x03b0f03e, - 0x0360e83b, 0x0310d838, 0x03c0fc3b, - 0x0370e83b, 0x0310d439, 0x03a0f83d, - 0x0370e83c, 0x0300d438, 0x03b0fc3c }, - } -}; - -enum rcar_vin_state { - STOPPED = 0, - RUNNING, - STOPPING, -}; - -struct rcar_vin_priv { - void __iomem *base; - spinlock_t lock; - int sequence; - /* State of the VIN module in capturing mode */ - enum rcar_vin_state state; - struct soc_camera_host ici; - struct list_head capture; -#define MAX_BUFFER_NUM 3 - struct vb2_v4l2_buffer *queue_buf[MAX_BUFFER_NUM]; - enum v4l2_field field; - unsigned int pdata_flags; - unsigned int vb_count; - unsigned int nr_hw_slots; - bool request_to_stop; - struct completion capture_stop; - enum chip_id chip; -}; - -#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM) - -struct rcar_vin_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ - struct rcar_vin_buffer, \ - vb)->list) - -struct rcar_vin_cam { - /* VIN offsets within the camera output, before the VIN scaler */ - unsigned int vin_left; - unsigned int vin_top; - /* Client output, as seen by the VIN */ - unsigned int width; - unsigned int height; - /* User window from S_FMT */ - unsigned int out_width; - unsigned int out_height; - /* - * User window from S_CROP / G_CROP, produced by client cropping and - * scaling, VIN scaling and VIN cropping, mapped back onto the client - * input window - */ - struct v4l2_rect subrect; - /* Camera cropping rectangle */ - struct v4l2_rect rect; - const struct soc_mbus_pixelfmt *extra_fmt; -}; - -/* - * .queue_setup() is called to check whether the driver can accept the requested - * number of buffers and to fill in plane sizes for the current frame format if - * required - */ -static int rcar_vin_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, - unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - - if (!vq->num_buffers) - priv->sequence = 0; - - if (!*count) - *count = 2; - priv->vb_count = *count; - - /* Number of hardware slots */ - if (is_continuous_transfer(priv)) - priv->nr_hw_slots = MAX_BUFFER_NUM; - else - priv->nr_hw_slots = 1; - - if (*num_planes) - return sizes[0] < icd->sizeimage ? -EINVAL : 0; - - sizes[0] = icd->sizeimage; - *num_planes = 1; - - dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); - - return 0; -} - -static int rcar_vin_setup(struct rcar_vin_priv *priv) -{ - struct soc_camera_device *icd = priv->ici.icd; - struct rcar_vin_cam *cam = icd->host_priv; - u32 vnmc, dmr, interrupts; - bool progressive = false, output_is_yuv = false, input_is_yuv = false; - - switch (priv->field) { - case V4L2_FIELD_TOP: - vnmc = VNMC_IM_ODD; - break; - case V4L2_FIELD_BOTTOM: - vnmc = VNMC_IM_EVEN; - break; - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - vnmc = VNMC_IM_FULL; - break; - case V4L2_FIELD_INTERLACED_BT: - vnmc = VNMC_IM_FULL | VNMC_FOC; - break; - case V4L2_FIELD_NONE: - if (is_continuous_transfer(priv)) { - vnmc = VNMC_IM_ODD_EVEN; - progressive = true; - } else { - vnmc = VNMC_IM_ODD; - } - break; - default: - vnmc = VNMC_IM_ODD; - break; - } - - /* input interface */ - switch (icd->current_fmt->code) { - case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_RGB888_1X24: - vnmc |= VNMC_INF_RGB888; - break; - case MEDIA_BUS_FMT_YUYV10_2X10: - /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; - input_is_yuv = true; - break; - default: - break; - } - - /* output format */ - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV16: - iowrite32(ALIGN(cam->width * cam->height, 0x80), - priv->base + VNUVAOF_REG); - dmr = VNDMR_DTMD_YCSEP; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_YUYV: - dmr = VNDMR_BPSM; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_UYVY: - dmr = 0; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_RGB555X: - dmr = VNDMR_DTMD_ARGB; - break; - case V4L2_PIX_FMT_RGB565: - dmr = 0; - break; - case V4L2_PIX_FMT_RGB32: - if (priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 && - priv->chip != RCAR_E1) - goto e_format; - - dmr = VNDMR_EXRGB; - break; - case V4L2_PIX_FMT_ARGB32: - if (priv->chip != RCAR_GEN3) - goto e_format; - - dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB; - break; - default: - goto e_format; - } - - /* Always update on field change */ - vnmc |= VNMC_VUP; - - /* If input and output use the same colorspace, use bypass mode */ - if (input_is_yuv == output_is_yuv) - vnmc |= VNMC_BPS; - - /* progressive or interlaced mode */ - interrupts = progressive ? VNIE_FIE : VNIE_EFE; - - /* ack interrupts */ - iowrite32(interrupts, priv->base + VNINTS_REG); - /* enable interrupts */ - iowrite32(interrupts, priv->base + VNIE_REG); - /* start capturing */ - iowrite32(dmr, priv->base + VNDMR_REG); - iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG); - - return 0; - -e_format: - dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", - icd->current_fmt->host_fmt->fourcc); - return -EINVAL; -} - -static void rcar_vin_capture(struct rcar_vin_priv *priv) -{ - if (is_continuous_transfer(priv)) - /* Continuous Frame Capture Mode */ - iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG); - else - /* Single Frame Capture Mode */ - iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG); -} - -static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv) -{ - priv->state = STOPPING; - - /* set continuous & single transfer off */ - iowrite32(0, priv->base + VNFC_REG); - /* disable capture (release DMA buffer), reset */ - iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, - priv->base + VNMC_REG); - - /* update the status if stopped already */ - if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA)) - priv->state = STOPPED; -} - -static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv) -{ - int slot; - - for (slot = 0; slot < priv->nr_hw_slots; slot++) - if (priv->queue_buf[slot] == NULL) - return slot; - - return -1; -} - -static int rcar_vin_hw_ready(struct rcar_vin_priv *priv) -{ - /* Ensure all HW slots are filled */ - return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0; -} - -/* Moves a buffer from the queue to the HW slots */ -static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) -{ - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr_top; - int slot; - - if (list_empty(&priv->capture)) - return 0; - - /* Find a free HW slot */ - slot = rcar_vin_get_free_hw_slot(priv); - if (slot < 0) - return 0; - - vbuf = &list_entry(priv->capture.next, - struct rcar_vin_buffer, list)->vb; - list_del_init(to_buf_list(vbuf)); - priv->queue_buf[slot] = vbuf; - phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); - - return 1; -} - -static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - unsigned long size; - - size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", - vb->index, vb2_plane_size(vb, 0), size); - goto error; - } - - vb2_set_plane_payload(vb, 0, size); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - spin_lock_irq(&priv->lock); - - list_add_tail(to_buf_list(vbuf), &priv->capture); - rcar_vin_fill_hw_slot(priv); - - /* If we weren't running, and have enough buffers, start capturing! */ - if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) { - if (rcar_vin_setup(priv)) { - /* Submit error */ - list_del_init(to_buf_list(vbuf)); - spin_unlock_irq(&priv->lock); - goto error; - } - priv->request_to_stop = false; - init_completion(&priv->capture_stop); - priv->state = RUNNING; - rcar_vin_capture(priv); - } - - spin_unlock_irq(&priv->lock); - - return; - -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -/* - * Wait for capture to stop and all in-flight buffers to be finished with by - * the video hardware. This must be called under &priv->lock - * - */ -static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv) -{ - while (priv->state != STOPPED) { - /* issue stop if running */ - if (priv->state == RUNNING) - rcar_vin_request_capture_stop(priv); - - /* wait until capturing has been stopped */ - if (priv->state == STOPPING) { - priv->request_to_stop = true; - spin_unlock_irq(&priv->lock); - if (!wait_for_completion_timeout( - &priv->capture_stop, - msecs_to_jiffies(TIMEOUT_MS))) - priv->state = STOPPED; - spin_lock_irq(&priv->lock); - } - } -} - -static void rcar_vin_stop_streaming(struct vb2_queue *vq) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - struct list_head *buf_head, *tmp; - int i; - - spin_lock_irq(&priv->lock); - rcar_vin_wait_stop_streaming(priv); - - for (i = 0; i < MAX_BUFFER_NUM; i++) { - if (priv->queue_buf[i]) { - vb2_buffer_done(&priv->queue_buf[i]->vb2_buf, - VB2_BUF_STATE_ERROR); - priv->queue_buf[i] = NULL; - } - } - - list_for_each_safe(buf_head, tmp, &priv->capture) { - vb2_buffer_done(&list_entry(buf_head, - struct rcar_vin_buffer, list)->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - list_del_init(buf_head); - } - spin_unlock_irq(&priv->lock); -} - -static struct vb2_ops rcar_vin_vb2_ops = { - .queue_setup = rcar_vin_videobuf_setup, - .buf_queue = rcar_vin_videobuf_queue, - .stop_streaming = rcar_vin_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static irqreturn_t rcar_vin_irq(int irq, void *data) -{ - struct rcar_vin_priv *priv = data; - u32 int_status; - bool can_run = false, hw_stopped; - int slot; - unsigned int handled = 0; - - spin_lock(&priv->lock); - - int_status = ioread32(priv->base + VNINTS_REG); - if (!int_status) - goto done; - /* ack interrupts */ - iowrite32(int_status, priv->base + VNINTS_REG); - handled = 1; - - /* nothing to do if capture status is 'STOPPED' */ - if (priv->state == STOPPED) - goto done; - - hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA); - - if (!priv->request_to_stop) { - if (is_continuous_transfer(priv)) - slot = (ioread32(priv->base + VNMS_REG) & - VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; - else - slot = 0; - - priv->queue_buf[slot]->field = priv->field; - priv->queue_buf[slot]->sequence = priv->sequence++; - priv->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf, - VB2_BUF_STATE_DONE); - priv->queue_buf[slot] = NULL; - - if (priv->state != STOPPING) - can_run = rcar_vin_fill_hw_slot(priv); - - if (hw_stopped || !can_run) { - priv->state = STOPPED; - } else if (is_continuous_transfer(priv) && - list_empty(&priv->capture) && - priv->state == RUNNING) { - /* - * The continuous capturing requires an explicit stop - * operation when there is no buffer to be set into - * the VnMBm registers. - */ - rcar_vin_request_capture_stop(priv); - } else { - rcar_vin_capture(priv); - } - - } else if (hw_stopped) { - priv->state = STOPPED; - priv->request_to_stop = false; - complete(&priv->capture_stop); - } - -done: - spin_unlock(&priv->lock); - - return IRQ_RETVAL(handled); -} - -static int rcar_vin_add_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - int i; - - for (i = 0; i < MAX_BUFFER_NUM; i++) - priv->queue_buf[i] = NULL; - - pm_runtime_get_sync(ici->v4l2_dev.dev); - - dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void rcar_vin_remove_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - struct vb2_v4l2_buffer *vbuf; - int i; - - /* disable capture, disable interrupts */ - iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, - priv->base + VNMC_REG); - iowrite32(0, priv->base + VNIE_REG); - - priv->state = STOPPED; - priv->request_to_stop = false; - - /* make sure active buffer is cancelled */ - spin_lock_irq(&priv->lock); - for (i = 0; i < MAX_BUFFER_NUM; i++) { - vbuf = priv->queue_buf[i]; - if (vbuf) { - list_del_init(to_buf_list(vbuf)); - vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR); - } - } - spin_unlock_irq(&priv->lock); - - pm_runtime_put(ici->v4l2_dev.dev); - - dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n", - icd->devnum); -} - -static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs) -{ - int i; - const struct vin_coeff *p_prev_set = NULL; - const struct vin_coeff *p_set = NULL; - - /* Look for suitable coefficient values */ - for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { - p_prev_set = p_set; - p_set = &vin_coeff_set[i]; - - if (xs < p_set->xs_value) - break; - } - - /* Use previous value if its XS value is closer */ - if (p_prev_set && p_set && - xs - p_prev_set->xs_value < p_set->xs_value - xs) - p_set = p_prev_set; - - /* Set coefficient registers */ - iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG); - iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG); - iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG); - - iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG); - iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG); - iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG); - - iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG); - iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG); - iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG); - - iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG); - iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG); - iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG); - - iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG); - iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG); - iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG); - - iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG); - iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG); - iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG); - - iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG); - iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG); - iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG); - - iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG); - iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG); - iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG); -} - -/* rect is guaranteed to not exceed the scaled camera rectangle */ -static int rcar_vin_set_rect(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_cam *cam = icd->host_priv; - struct rcar_vin_priv *priv = ici->priv; - unsigned int left_offset, top_offset; - unsigned char dsize = 0; - struct v4l2_rect *cam_subrect = &cam->subrect; - u32 value; - - dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n", - icd->user_width, icd->user_height, cam->vin_left, cam->vin_top); - - left_offset = cam->vin_left; - top_offset = cam->vin_top; - - if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 && - priv->chip == RCAR_E1) - dsize = 1; - - dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n", - cam->width, cam->height, cam->vin_left, cam->vin_top); - dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n", - cam_subrect->width, cam_subrect->height, - cam_subrect->left, cam_subrect->top); - - /* Set Start/End Pixel/Line Pre-Clip */ - iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG); - iowrite32((left_offset + cam_subrect->width - 1) << dsize, - priv->base + VNEPPRC_REG); - switch (priv->field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - iowrite32(top_offset / 2, priv->base + VNSLPRC_REG); - iowrite32((top_offset + cam_subrect->height) / 2 - 1, - priv->base + VNELPRC_REG); - break; - default: - iowrite32(top_offset, priv->base + VNSLPRC_REG); - iowrite32(top_offset + cam_subrect->height - 1, - priv->base + VNELPRC_REG); - break; - } - - /* Set scaling coefficient */ - value = 0; - if (cam_subrect->height != cam->out_height) - value = (4096 * cam_subrect->height) / cam->out_height; - dev_dbg(icd->parent, "YS Value: %x\n", value); - iowrite32(value, priv->base + VNYS_REG); - - value = 0; - if (cam_subrect->width != cam->out_width) - value = (4096 * cam_subrect->width) / cam->out_width; - - /* Horizontal upscaling is up to double size */ - if (0 < value && value < 2048) - value = 2048; - - dev_dbg(icd->parent, "XS Value: %x\n", value); - iowrite32(value, priv->base + VNXS_REG); - - /* Horizontal upscaling is carried out by scaling down from double size */ - if (value < 4096) - value *= 2; - - set_coeff(priv, value); - - /* Set Start/End Pixel/Line Post-Clip */ - iowrite32(0, priv->base + VNSPPOC_REG); - iowrite32(0, priv->base + VNSLPOC_REG); - iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG); - switch (priv->field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - iowrite32(cam->out_height / 2 - 1, - priv->base + VNELPOC_REG); - break; - default: - iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG); - break; - } - - iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG); - - return 0; -} - -static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc) -{ - *vnmc = ioread32(priv->base + VNMC_REG); - /* module disable */ - iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG); -} - -static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc) -{ - unsigned long timeout = jiffies + 10 * HZ; - - /* - * Wait until the end of the current frame. It can take a long time, - * but if it has been aborted by a MRST1 reset, it should exit sooner. - */ - while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) && - time_before(jiffies, timeout)) - msleep(1); - - if (time_after(jiffies, timeout)) { - dev_err(priv->ici.v4l2_dev.dev, - "Timeout waiting for frame end! Interface problem?\n"); - return; - } - - iowrite32(vnmc, priv->base + VNMC_REG); -} - -#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - -static int rcar_vin_set_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_config cfg; - unsigned long common_flags; - u32 vnmc; - u32 val; - int ret; - - capture_stop_preserve(priv, &vnmc); - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); - if (!common_flags) { - dev_warn(icd->parent, - "MBUS flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, VIN_MBUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = VIN_MBUS_FLAGS; - } - - /* Make choises, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (priv->pdata_flags & RCAR_VIN_HSYNC_ACTIVE_LOW) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (priv->pdata_flags & RCAR_VIN_VSYNC_ACTIVE_LOW) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - val = VNDMR2_FTEV | VNDMR2_VLV(1); - if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) - val |= VNDMR2_VPS; - if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) - val |= VNDMR2_HPS; - iowrite32(val, priv->base + VNDMR2_REG); - - ret = rcar_vin_set_rect(icd); - if (ret < 0) - return ret; - - capture_restore(priv, vnmc); - - return 0; -} - -static int rcar_vin_try_bus_param(struct soc_camera_device *icd, - unsigned char buswidth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_config cfg; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (ret == -ENOIOCTLCMD) - return 0; - else if (ret) - return ret; - - if (buswidth > 24) - return -EINVAL; - - /* check is there common mbus flags */ - ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); - if (ret) - return 0; - - dev_warn(icd->parent, - "MBUS flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, VIN_MBUS_FLAGS); - - return -EINVAL; -} - -static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static const struct soc_mbus_pixelfmt rcar_vin_formats[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .name = "NV16", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "ARGB1555", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888", - .bits_per_sample = 32, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB32, - .name = "ARGB8888", - .bits_per_sample = 32, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}; - -static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int ret, k, n; - int formats = 0; - struct rcar_vin_cam *cam; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); - return 0; - } - - ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - if (!icd->host_priv) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct v4l2_rect rect; - struct device *dev = icd->parent; - int shift; - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - /* Cache current client geometry */ - ret = soc_camera_client_g_rect(sd, &rect); - if (ret == -ENOIOCTLCMD) { - /* Sensor driver doesn't support cropping */ - rect.left = 0; - rect.top = 0; - rect.width = mf->width; - rect.height = mf->height; - } else if (ret < 0) { - return ret; - } - - /* - * If sensor proposes too large format then try smaller ones: - * 1280x960, 640x480, 320x240 - */ - for (shift = 0; shift < 3; shift++) { - if (mf->width <= VIN_MAX_WIDTH && - mf->height <= VIN_MAX_HEIGHT) - break; - - mf->width = 1280 >> shift; - mf->height = 960 >> shift; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), - pad, set_fmt, NULL, - &fmt); - if (ret < 0) - return ret; - } - - if (shift == 3) { - dev_err(dev, - "Failed to configure the client below %ux%u\n", - mf->width, mf->height); - return -EIO; - } - - dev_dbg(dev, "camera fmt %ux%u\n", mf->width, mf->height); - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - /* - * We are called with current camera crop, - * initialise subrect with it - */ - cam->rect = rect; - cam->subrect = rect; - cam->width = mf->width; - cam->height = mf->height; - cam->out_width = mf->width; - cam->out_height = mf->height; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - - /* Beginning of a pass */ - if (!idx) - cam->extra_fmt = NULL; - - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV10_2X10: - case MEDIA_BUS_FMT_RGB888_1X24: - if (cam->extra_fmt) - break; - - /* Add all our formats that can be generated by VIN */ - cam->extra_fmt = rcar_vin_formats; - - n = ARRAY_SIZE(rcar_vin_formats); - formats += n; - for (k = 0; xlate && k < n; k++, xlate++) { - xlate->host_fmt = &rcar_vin_formats[k]; - xlate->code = code.code; - dev_dbg(dev, "Providing format %s using code %d\n", - rcar_vin_formats[k].name, code.code); - } - break; - default: - if (!rcar_vin_packing_supported(fmt)) - return 0; - - dev_dbg(dev, "Providing format %s in pass-through mode\n", - fmt->name); - break; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - - return formats; -} - -static void rcar_vin_put_formats(struct soc_camera_device *icd) -{ - kfree(icd->host_priv); - icd->host_priv = NULL; -} - -static int rcar_vin_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - const struct v4l2_rect *rect = &a_writable.c; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - struct v4l2_crop cam_crop; - struct rcar_vin_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_crop.c; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - u32 vnmc; - int ret, i; - - dev_dbg(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, - rect->left, rect->top); - - /* During camera cropping its output window can change too, stop VIN */ - capture_stop_preserve(priv, &vnmc); - dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc); - - /* Apply iterative camera S_CROP for new input window. */ - ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, - &cam->rect, &cam->subrect); - if (ret < 0) - return ret; - - dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - - /* On success cam_crop contains current camera crop */ - - /* Retrieve camera output window */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->width > VIN_MAX_WIDTH || mf->height > VIN_MAX_HEIGHT) - return -EINVAL; - - /* Cache camera output window */ - cam->width = mf->width; - cam->height = mf->height; - - icd->user_width = cam->width; - icd->user_height = cam->height; - - cam->vin_left = rect->left & ~1; - cam->vin_top = rect->top & ~1; - - /* Use VIN cropping to crop to the new window. */ - ret = rcar_vin_set_rect(icd); - if (ret < 0) - return ret; - - cam->subrect = *rect; - - dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n", - icd->user_width, icd->user_height, - cam->vin_left, cam->vin_top); - - /* Restore capture */ - for (i = 0; i < MAX_BUFFER_NUM; i++) { - if (priv->queue_buf[i] && priv->state == STOPPED) { - vnmc |= VNMC_ME; - break; - } - } - capture_restore(priv, vnmc); - - /* Even if only camera cropping succeeded */ - return ret; -} - -static int rcar_vin_get_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) -{ - struct rcar_vin_cam *cam = icd->host_priv; - - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->c = cam->subrect; - - return 0; -} - -/* Similar to set_crop multistage iterative algorithm */ -static int rcar_vin_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct rcar_vin_cam *cam = icd->host_priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; - struct device *dev = icd->parent; - __u32 pixfmt = pix->pixelformat; - const struct soc_camera_format_xlate *xlate; - unsigned int vin_sub_width = 0, vin_sub_height = 0; - int ret; - bool can_scale; - enum v4l2_field field; - v4l2_std_id std; - - dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n", - pixfmt, pix->width, pix->height); - - switch (pix->field) { - default: - pix->field = V4L2_FIELD_NONE; - /* fall-through */ - case V4L2_FIELD_NONE: - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - field = pix->field; - break; - case V4L2_FIELD_INTERLACED: - /* Query for standard if not explicitly mentioned _TB/_BT */ - ret = v4l2_subdev_call(sd, video, querystd, &std); - if (ret == -ENOIOCTLCMD) { - field = V4L2_FIELD_NONE; - } else if (ret < 0) { - return ret; - } else { - field = std & V4L2_STD_625_50 ? - V4L2_FIELD_INTERLACED_TB : - V4L2_FIELD_INTERLACED_BT; - } - break; - } - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - /* Calculate client output geometry */ - soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, - 12); - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; - - switch (pixfmt) { - case V4L2_PIX_FMT_RGB32: - can_scale = priv->chip != RCAR_E1; - break; - case V4L2_PIX_FMT_ARGB32: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB555X: - can_scale = true; - break; - default: - can_scale = false; - break; - } - - dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height); - - ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, - &mf, &vin_sub_width, &vin_sub_height, - can_scale, 12); - - /* Done with the camera. Now see if we can improve the result */ - dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", - ret, mf.width, mf.height, pix->width, pix->height); - - if (ret == -ENOIOCTLCMD) - dev_dbg(dev, "Sensor doesn't support scaling\n"); - else if (ret < 0) - return ret; - - if (mf.code != xlate->code) - return -EINVAL; - - /* Prepare VIN crop */ - cam->width = mf.width; - cam->height = mf.height; - - /* Use VIN scaling to scale to the requested user window. */ - - /* We cannot scale up */ - if (pix->width > vin_sub_width) - vin_sub_width = pix->width; - - if (pix->height > vin_sub_height) - vin_sub_height = pix->height; - - pix->colorspace = mf.colorspace; - - if (!can_scale) { - pix->width = vin_sub_width; - pix->height = vin_sub_height; - } - - /* - * We have calculated CFLCR, the actual configuration will be performed - * in rcar_vin_set_bus_param() - */ - - dev_dbg(dev, "W: %u : %u, H: %u : %u\n", - vin_sub_width, pix->width, vin_sub_height, pix->height); - - cam->out_width = pix->width; - cam->out_height = pix->height; - - icd->current_fmt = xlate; - - priv->field = field; - - return 0; -} - -static int rcar_vin_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int width, height; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - xlate = icd->current_fmt; - dev_dbg(icd->parent, "Format %x not found, keeping %x\n", - pixfmt, xlate->host_fmt->fourcc); - pixfmt = xlate->host_fmt->fourcc; - pix->pixelformat = pixfmt; - pix->colorspace = icd->colorspace; - } - - /* FIXME: calculate using depth and bus width */ - v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1, - &pix->height, 4, VIN_MAX_HEIGHT, 2, 0); - - width = pix->width; - height = pix->height; - - /* let soc-camera calculate these values */ - pix->bytesperline = 0; - pix->sizeimage = 0; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->code = xlate->code; - mf->colorspace = pix->colorspace; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - /* Adjust only if VIN cannot scale */ - if (pix->width > mf->width * 2) - pix->width = mf->width * 2; - if (pix->height > mf->height * 3) - pix->height = mf->height * 3; - - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - if (pixfmt == V4L2_PIX_FMT_NV16) { - /* FIXME: check against rect_max after converting soc-camera */ - /* We can scale precisely, need a bigger image from camera */ - if (pix->width < width || pix->height < height) { - /* - * We presume, the sensor behaves sanely, i.e. if - * requested a bigger rectangle, it will not return a - * smaller one. - */ - mf->width = VIN_MAX_WIDTH; - mf->height = VIN_MAX_HEIGHT; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), - pad, set_fmt, &pad_cfg, - &format); - if (ret < 0) { - dev_err(icd->parent, - "client try_fmt() = %d\n", ret); - return ret; - } - } - /* We will scale exactly */ - if (mf->width > width) - pix->width = width; - if (mf->height > height) - pix->height = height; - } - - return ret; -} - -static unsigned int rcar_vin_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int rcar_vin_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr); - - return 0; -} - -static int rcar_vin_init_videobuf2(struct vb2_queue *vq, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vq->io_modes = VB2_MMAP | VB2_USERPTR; - vq->drv_priv = icd; - vq->ops = &rcar_vin_vb2_ops; - vq->mem_ops = &vb2_dma_contig_memops; - vq->buf_struct_size = sizeof(struct rcar_vin_buffer); - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vq->lock = &ici->host_lock; - vq->dev = ici->v4l2_dev.dev; - - return vb2_queue_init(vq); -} - -static struct soc_camera_host_ops rcar_vin_host_ops = { - .owner = THIS_MODULE, - .add = rcar_vin_add_device, - .remove = rcar_vin_remove_device, - .get_formats = rcar_vin_get_formats, - .put_formats = rcar_vin_put_formats, - .get_crop = rcar_vin_get_crop, - .set_crop = rcar_vin_set_crop, - .try_fmt = rcar_vin_try_fmt, - .set_fmt = rcar_vin_set_fmt, - .poll = rcar_vin_poll, - .querycap = rcar_vin_querycap, - .set_bus_param = rcar_vin_set_bus_param, - .init_videobuf2 = rcar_vin_init_videobuf2, -}; - -#ifdef CONFIG_OF -static const struct of_device_id rcar_vin_of_table[] = { - { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_GEN3 }, - { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, - { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, - { .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 }, - { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, - { }, -}; -MODULE_DEVICE_TABLE(of, rcar_vin_of_table); -#endif - -static int rcar_vin_probe(struct platform_device *pdev) -{ - const struct of_device_id *match = NULL; - struct rcar_vin_priv *priv; - struct v4l2_of_endpoint ep; - struct device_node *np; - struct resource *mem; - unsigned int pdata_flags; - int irq, ret; - - match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev); - - np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!np) { - dev_err(&pdev->dev, "could not find endpoint\n"); - return -EINVAL; - } - - ret = v4l2_of_parse_endpoint(np, &ep); - if (ret) { - dev_err(&pdev->dev, "could not parse endpoint\n"); - return ret; - } - - if (ep.bus_type == V4L2_MBUS_BT656) - pdata_flags = RCAR_VIN_BT656; - else { - pdata_flags = 0; - if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW; - if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW; - } - - of_node_put(np); - - dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags); - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem == NULL) - return -EINVAL; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; - - priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv), - GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED, - dev_name(&pdev->dev), priv); - if (ret) - return ret; - - priv->ici.priv = priv; - priv->ici.v4l2_dev.dev = &pdev->dev; - priv->ici.drv_name = dev_name(&pdev->dev); - priv->ici.ops = &rcar_vin_host_ops; - - priv->pdata_flags = pdata_flags; - if (!match) { - priv->ici.nr = pdev->id; - priv->chip = pdev->id_entry->driver_data; - } else { - priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin"); - priv->chip = (enum chip_id)match->data; - } - - spin_lock_init(&priv->lock); - INIT_LIST_HEAD(&priv->capture); - - priv->state = STOPPED; - - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); - - ret = soc_camera_host_register(&priv->ici); - if (ret) - goto cleanup; - - return 0; - -cleanup: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int rcar_vin_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - - soc_camera_host_unregister(soc_host); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver rcar_vin_driver = { - .probe = rcar_vin_probe, - .remove = rcar_vin_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(rcar_vin_of_table), - }, -}; - -module_platform_driver(rcar_vin_driver); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:rcar_vin"); -MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 02b519dde42a..a15bfb5aea47 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -41,7 +41,6 @@ #include <media/v4l2-dev.h> #include <media/soc_camera.h> #include <media/drv-intf/sh_mobile_ceu.h> -#include <media/drv-intf/sh_mobile_csi2.h> #include <media/videobuf2-dma-contig.h> #include <media/v4l2-mediabus.h> #include <media/drv-intf/soc_mediabus.h> @@ -99,11 +98,6 @@ struct sh_mobile_ceu_buffer { struct sh_mobile_ceu_dev { struct soc_camera_host ici; - /* Asynchronous CSI2 linking */ - struct v4l2_async_subdev *csi2_asd; - struct v4l2_subdev *csi2_sd; - /* Synchronous probing compatibility */ - struct platform_device *csi2_pdev; unsigned int irq; void __iomem *base; @@ -140,7 +134,7 @@ struct sh_mobile_ceu_cam { unsigned int width; unsigned int height; /* - * User window from S_CROP / G_CROP, produced by client cropping and + * User window from S_SELECTION / G_SELECTION, produced by client cropping and * scaling, CEU scaling and CEU cropping, mapped back onto the client * input window */ @@ -470,7 +464,7 @@ static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) sh_mobile_ceu_soft_reset(pcdev); } -static struct vb2_ops sh_mobile_ceu_videobuf_ops = { +static const struct vb2_ops sh_mobile_ceu_videobuf_ops = { .queue_setup = sh_mobile_ceu_videobuf_setup, .buf_prepare = sh_mobile_ceu_videobuf_prepare, .buf_queue = sh_mobile_ceu_videobuf_queue, @@ -517,74 +511,20 @@ out: return IRQ_HANDLED; } -static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) -{ - struct v4l2_subdev *sd; - - if (pcdev->csi2_sd) - return pcdev->csi2_sd; - - if (pcdev->csi2_asd) { - char name[] = "sh-mobile-csi2"; - v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) - if (!strncmp(name, sd->name, sizeof(name) - 1)) { - pcdev->csi2_sd = sd; - return sd; - } - } - - return NULL; -} - -static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev, - struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = pcdev->csi2_sd; - - return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL; -} - static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *csi2_sd = find_csi2(pcdev); - int ret; - - if (csi2_sd) { - csi2_sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(csi2_sd, icd); - } - - ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - /* - * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver - * has not found this soc-camera device among its clients - */ - if (csi2_sd && ret == -ENODEV) - csi2_sd->grp_id = 0; - dev_info(icd->parent, - "SuperH Mobile CEU%s driver attached to camera %d\n", - csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum); + "SuperH Mobile CEU driver attached to camera %d\n", + icd->devnum); return 0; } static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *csi2_sd = find_csi2(pcdev); - dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", icd->devnum); - - v4l2_subdev_call(csi2_sd, core, s_power, 0); } /* Called with .host_lock held */ @@ -704,12 +644,6 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) cdwdr_width *= 2; } - /* CSI2 special configuration */ - if (csi2_subdev(pcdev, icd)) { - in_width = ((in_width - 2) * 2); - left_offset *= 2; - } - /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ camor = left_offset | (top_offset << 16); @@ -758,13 +692,6 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) ceu_write(pcdev, CAPSR, capsr); } -/* Find the bus subdevice driver, e.g., CSI2 */ -static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev, - struct soc_camera_device *icd) -{ - return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd); -} - #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ V4L2_MBUS_PCLK_SAMPLE_RISING | \ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ @@ -778,7 +705,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long value, common_flags = CEU_BUS_FLAGS; @@ -866,9 +793,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - if (csi2_subdev(pcdev, icd)) /* CSI2 mode */ - value |= 3 << 12; - else if (pcdev->is_16bit) + if (pcdev->is_16bit) value |= 1 << 12; else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT) value |= 2 << 12; @@ -923,9 +848,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); unsigned long common_flags = CEU_BUS_FLAGS; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; int ret; @@ -1046,12 +969,9 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int return 0; } - if (!csi2_subdev(pcdev, icd)) { - /* Are there any restrictions in the CSI-2 case? */ - ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - } + ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; if (!icd->host_priv) { struct v4l2_subdev_format fmt = { @@ -1189,17 +1109,16 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of * scaling and cropping algorithms and for the meaning of referenced here steps. */ -static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { - struct v4l2_crop a_writable = *a; - const struct v4l2_rect *rect = &a_writable.c; + struct v4l2_rect *rect = &sel->r; struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_crop cam_crop; + struct v4l2_selection cam_sel; struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_crop.c; + struct v4l2_rect *cam_rect = &cam_sel.r; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -1211,7 +1130,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, u32 capsr, cflcr; int ret; - dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + dev_geo(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); /* During camera cropping its output window can change too, stop CEU */ @@ -1219,10 +1138,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); /* - * 1. - 2. Apply iterative camera S_CROP for new input window, read back + * 1. - 2. Apply iterative camera S_SELECTION for new input window, read back * actual camera rectangle. */ - ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, + ret = soc_camera_client_s_selection(sd, sel, &cam_sel, &cam->rect, &cam->subrect); if (ret < 0) return ret; @@ -1331,13 +1250,12 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, return ret; } -static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) +static int sh_mobile_ceu_get_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct sh_mobile_ceu_cam *cam = icd->host_priv; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->c = cam->subrect; + sel->r = cam->subrect; return 0; } @@ -1579,8 +1497,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, return ret; } -static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +static int sh_mobile_ceu_set_liveselection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -1599,7 +1517,7 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, "Client failed to stop the stream: %d\n", ret); else /* Do the crop, if it fails, there's nothing more we can do */ - sh_mobile_ceu_set_crop(icd, a); + sh_mobile_ceu_set_selection(icd, sel); dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); @@ -1680,9 +1598,9 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .clock_stop = sh_mobile_ceu_clock_stop, .get_formats = sh_mobile_ceu_get_formats, .put_formats = sh_mobile_ceu_put_formats, - .get_crop = sh_mobile_ceu_get_crop, - .set_crop = sh_mobile_ceu_set_crop, - .set_livecrop = sh_mobile_ceu_set_livecrop, + .get_selection = sh_mobile_ceu_get_selection, + .set_selection = sh_mobile_ceu_set_selection, + .set_liveselection = sh_mobile_ceu_set_liveselection, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, .poll = sh_mobile_ceu_poll, @@ -1721,12 +1639,11 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; unsigned int irq; - int err, i; + int err; struct bus_wait wait = { .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), .notifier.notifier_call = bus_notify, }; - struct sh_mobile_ceu_companion *csi2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -1821,132 +1738,16 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; if (pcdev->pdata && pcdev->pdata->asd_sizes) { - struct v4l2_async_subdev **asd; - char name[] = "sh-mobile-csi2"; - int j; - - /* - * CSI2 interfacing: several groups can use CSI2, pick up the - * first one - */ - asd = pcdev->pdata->asd; - for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { - for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { - dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", - __func__, i, (*asd)->match_type); - if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME && - !strncmp(name, (*asd)->match.device_name.name, - sizeof(name) - 1)) { - pcdev->csi2_asd = *asd; - break; - } - } - if (pcdev->csi2_asd) - break; - } - pcdev->ici.asd = pcdev->pdata->asd; pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes; } - /* Legacy CSI2 interfacing */ - csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL; - if (csi2) { - /* - * TODO: remove this once all users are converted to - * asynchronous CSI2 probing. If it has to be kept, csi2 - * platform device resources have to be added, using - * platform_device_add_resources() - */ - struct platform_device *csi2_pdev = - platform_device_alloc("sh-mobile-csi2", csi2->id); - struct sh_csi2_pdata *csi2_pdata = csi2->platform_data; - - if (!csi2_pdev) { - err = -ENOMEM; - goto exit_free_clk; - } - - pcdev->csi2_pdev = csi2_pdev; - - err = platform_device_add_data(csi2_pdev, csi2_pdata, - sizeof(*csi2_pdata)); - if (err < 0) - goto exit_pdev_put; - - csi2_pdev->resource = csi2->resource; - csi2_pdev->num_resources = csi2->num_resources; - - err = platform_device_add(csi2_pdev); - if (err < 0) - goto exit_pdev_put; - - wait.dev = &csi2_pdev->dev; - - err = bus_register_notifier(&platform_bus_type, &wait.notifier); - if (err < 0) - goto exit_pdev_unregister; - - /* - * From this point the driver module will not unload, until - * we complete the completion. - */ - - if (!csi2_pdev->dev.driver) { - complete(&wait.completion); - /* Either too late, or probing failed */ - bus_unregister_notifier(&platform_bus_type, &wait.notifier); - err = -ENXIO; - goto exit_pdev_unregister; - } - - /* - * The module is still loaded, in the worst case it is hanging - * in device release on our completion. So, _now_ dereferencing - * the "owner" is safe! - */ - - err = try_module_get(csi2_pdev->dev.driver->owner); - - /* Let notifier complete, if it has been locked */ - complete(&wait.completion); - bus_unregister_notifier(&platform_bus_type, &wait.notifier); - if (!err) { - err = -ENODEV; - goto exit_pdev_unregister; - } - - pcdev->csi2_sd = platform_get_drvdata(csi2_pdev); - } - err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_csi2_unregister; - - if (csi2) { - err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev, - pcdev->csi2_sd); - dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n", - __func__, err); - if (err < 0) - goto exit_host_unregister; - /* v4l2_device_register_subdev() took a reference too */ - module_put(pcdev->csi2_sd->owner); - } + goto exit_free_clk; return 0; -exit_host_unregister: - soc_camera_host_unregister(&pcdev->ici); -exit_csi2_unregister: - if (csi2) { - module_put(pcdev->csi2_pdev->dev.driver->owner); -exit_pdev_unregister: - platform_device_del(pcdev->csi2_pdev); -exit_pdev_put: - pcdev->csi2_pdev->resource = NULL; - platform_device_put(pcdev->csi2_pdev); - } exit_free_clk: pm_runtime_disable(&pdev->dev); exit_release_mem: @@ -1958,21 +1759,11 @@ exit_release_mem: static int sh_mobile_ceu_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, - struct sh_mobile_ceu_dev, ici); - struct platform_device *csi2_pdev = pcdev->csi2_pdev; soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - if (csi2_pdev && csi2_pdev->dev.driver) { - struct module *csi2_drv = csi2_pdev->dev.driver->owner; - platform_device_del(csi2_pdev); - csi2_pdev->resource = NULL; - platform_device_put(csi2_pdev); - module_put(csi2_drv); - } return 0; } @@ -2012,8 +1803,6 @@ static struct platform_driver sh_mobile_ceu_driver = { static int __init sh_mobile_ceu_init(void) { - /* Whatever return code */ - request_module("sh_mobile_csi2"); return platform_driver_register(&sh_mobile_ceu_driver); } diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c deleted file mode 100644 index 09b18365a4b1..000000000000 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Driver for the SH-Mobile MIPI CSI-2 unit - * - * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * 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/delay.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/drv-intf/sh_mobile_ceu.h> -#include <media/drv-intf/sh_mobile_csi2.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-mediabus.h> -#include <media/v4l2-subdev.h> - -#define SH_CSI2_TREF 0x00 -#define SH_CSI2_SRST 0x04 -#define SH_CSI2_PHYCNT 0x08 -#define SH_CSI2_CHKSUM 0x0C -#define SH_CSI2_VCDT 0x10 - -struct sh_csi2 { - struct v4l2_subdev subdev; - unsigned int irq; - unsigned long mipi_flags; - void __iomem *base; - struct platform_device *pdev; - struct sh_csi2_client_config *client; -}; - -static void sh_csi2_hwinit(struct sh_csi2 *priv); - -static int sh_csi2_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - struct v4l2_mbus_framefmt *mf = &format->format; - u32 tmp = (priv->client->channel & 3) << 8; - - if (format->pad) - return -EINVAL; - - if (mf->width > 8188) - mf->width = 8188; - else if (mf->width & 1) - mf->width &= ~1; - - switch (pdata->type) { - case SH_CSI2C: - switch (mf->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422 */ - case MEDIA_BUS_FMT_YUYV8_1_5X8: /* YUV420 */ - case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */ - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - break; - default: - /* All MIPI CSI-2 devices must support one of primary formats */ - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - } - break; - case SH_CSI2I: - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */ - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: /* RAW10 */ - case MEDIA_BUS_FMT_SBGGR12_1X12: /* RAW12 */ - break; - default: - /* All MIPI CSI-2 devices must support one of primary formats */ - mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; - } - break; - } - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - if (mf->width > 8188 || mf->width & 1) - return -EINVAL; - - switch (mf->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - tmp |= 0x1e; /* YUV422 8 bit */ - break; - case MEDIA_BUS_FMT_YUYV8_1_5X8: - tmp |= 0x18; /* YUV420 8 bit */ - break; - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: - tmp |= 0x21; /* RGB555 */ - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - tmp |= 0x22; /* RGB565 */ - break; - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - tmp |= 0x2a; /* RAW8 */ - break; - default: - return -EINVAL; - } - - iowrite32(tmp, priv->base + SH_CSI2_VCDT); - - return 0; -} - -static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - - if (!priv->mipi_flags) { - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); - struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - unsigned long common_flags, csi2_flags; - struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; - int ret; - - /* Check if we can support this camera */ - csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_1_LANE; - - switch (pdata->type) { - case SH_CSI2C: - if (priv->client->lanes != 1) - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; - break; - case SH_CSI2I: - switch (priv->client->lanes) { - default: - csi2_flags |= V4L2_MBUS_CSI2_4_LANE; - case 3: - csi2_flags |= V4L2_MBUS_CSI2_3_LANE; - case 2: - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; - } - } - - ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg); - if (ret == -ENOIOCTLCMD) - common_flags = csi2_flags; - else if (!ret) - common_flags = soc_mbus_config_compatible(&client_cfg, - csi2_flags); - else - common_flags = 0; - - if (!common_flags) - return -EINVAL; - - /* All good: camera MIPI configuration supported */ - priv->mipi_flags = common_flags; - } - - if (cfg) { - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - } - - return 0; -} - -static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); - struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; - int ret = sh_csi2_g_mbus_config(sd, NULL); - - if (ret < 0) - return ret; - - pm_runtime_get_sync(&priv->pdev->dev); - - sh_csi2_hwinit(priv); - - client_cfg.flags = priv->mipi_flags; - - return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg); -} - -static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { - .g_mbus_config = sh_csi2_g_mbus_config, - .s_mbus_config = sh_csi2_s_mbus_config, -}; - -static struct v4l2_subdev_pad_ops sh_csi2_subdev_pad_ops = { - .set_fmt = sh_csi2_set_fmt, -}; - -static void sh_csi2_hwinit(struct sh_csi2 *priv) -{ - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ - - /* Reflect registers immediately */ - iowrite32(0x00000001, priv->base + SH_CSI2_TREF); - /* reset CSI2 harware */ - iowrite32(0x00000001, priv->base + SH_CSI2_SRST); - udelay(5); - iowrite32(0x00000000, priv->base + SH_CSI2_SRST); - - switch (pdata->type) { - case SH_CSI2C: - if (priv->client->lanes == 1) - tmp |= 1; - else - /* Default - both lanes */ - tmp |= 3; - break; - case SH_CSI2I: - if (!priv->client->lanes || priv->client->lanes > 4) - /* Default - all 4 lanes */ - tmp |= 0xf; - else - tmp |= (1 << priv->client->lanes) - 1; - } - - if (priv->client->phy == SH_CSI2_PHY_MAIN) - tmp |= 0x8000; - - iowrite32(tmp, priv->base + SH_CSI2_PHYCNT); - - tmp = 0; - if (pdata->flags & SH_CSI2_ECC) - tmp |= 2; - if (pdata->flags & SH_CSI2_CRC) - tmp |= 1; - iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); -} - -static int sh_csi2_client_connect(struct sh_csi2 *priv) -{ - struct device *dev = v4l2_get_subdevdata(&priv->subdev); - struct sh_csi2_pdata *pdata = dev->platform_data; - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev); - int i; - - if (priv->client) - return -EBUSY; - - for (i = 0; i < pdata->num_clients; i++) - if ((pdata->clients[i].pdev && - &pdata->clients[i].pdev->dev == icd->pdev) || - (icd->control && - strcmp(pdata->clients[i].name, dev_name(icd->control)))) - break; - - dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i); - - if (i == pdata->num_clients) - return -ENODEV; - - priv->client = pdata->clients + i; - - return 0; -} - -static void sh_csi2_client_disconnect(struct sh_csi2 *priv) -{ - if (!priv->client) - return; - - priv->client = NULL; - - pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); -} - -static int sh_csi2_s_power(struct v4l2_subdev *sd, int on) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - - if (on) - return sh_csi2_client_connect(priv); - - sh_csi2_client_disconnect(priv); - return 0; -} - -static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = { - .s_power = sh_csi2_s_power, -}; - -static struct v4l2_subdev_ops sh_csi2_subdev_ops = { - .core = &sh_csi2_subdev_core_ops, - .video = &sh_csi2_subdev_video_ops, - .pad = &sh_csi2_subdev_pad_ops, -}; - -static int sh_csi2_probe(struct platform_device *pdev) -{ - struct resource *res; - unsigned int irq; - int ret; - struct sh_csi2 *priv; - /* Platform data specify the PHY, lanes, ECC, CRC */ - struct sh_csi2_pdata *pdata = pdev->dev.platform_data; - - if (!pdata) - return -EINVAL; - - priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - /* Interrupt unused so far */ - irq = platform_get_irq(pdev, 0); - - if (!res || (int)irq <= 0) { - dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); - return -ENODEV; - } - - /* TODO: Add support for CSI2I. Careful: different register layout! */ - if (pdata->type != SH_CSI2C) { - dev_err(&pdev->dev, "Only CSI2C supported ATM.\n"); - return -EINVAL; - } - - priv->irq = irq; - - priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - priv->pdev = pdev; - priv->subdev.owner = THIS_MODULE; - priv->subdev.dev = &pdev->dev; - platform_set_drvdata(pdev, &priv->subdev); - - v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); - v4l2_set_subdevdata(&priv->subdev, &pdev->dev); - - snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi", - dev_name(&pdev->dev)); - - ret = v4l2_async_register_subdev(&priv->subdev); - if (ret < 0) - return ret; - - pm_runtime_enable(&pdev->dev); - - dev_dbg(&pdev->dev, "CSI2 probed.\n"); - - return 0; -} - -static int sh_csi2_remove(struct platform_device *pdev) -{ - struct v4l2_subdev *subdev = platform_get_drvdata(pdev); - struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev); - - v4l2_async_unregister_subdev(&priv->subdev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver __refdata sh_csi2_pdrv = { - .remove = sh_csi2_remove, - .probe = sh_csi2_probe, - .driver = { - .name = "sh-mobile-csi2", - }, -}; - -module_platform_driver(sh_csi2_pdrv); - -MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sh-mobile-csi2"); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 46c7186f7867..edd1c1de4e33 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -581,7 +581,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); - /* We always call try_fmt() before set_fmt() or set_crop() */ + /* We always call try_fmt() before set_fmt() or set_selection() */ ret = soc_camera_try_fmt(icd, f); if (ret < 0) return ret; @@ -1025,72 +1025,6 @@ static int soc_camera_streamoff(struct file *file, void *priv, return ret; } -static int soc_camera_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->cropcap(icd, a); -} - -static int soc_camera_g_crop(struct file *file, void *fh, - struct v4l2_crop *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - ret = ici->ops->get_crop(icd, a); - - return ret; -} - -/* - * According to the V4L2 API, drivers shall not update the struct v4l2_crop - * argument with the actual geometry, instead, the user shall use G_CROP to - * retrieve it. - */ -static int soc_camera_s_crop(struct file *file, void *fh, - const struct v4l2_crop *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct v4l2_rect *rect = &a->c; - struct v4l2_crop current_crop; - int ret; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", - rect->width, rect->height, rect->left, rect->top); - - current_crop.type = a->type; - - /* If get_crop fails, we'll let host and / or client drivers decide */ - ret = ici->ops->get_crop(icd, ¤t_crop); - - /* Prohibit window size change with initialised buffers */ - if (ret < 0) { - dev_err(icd->pdev, - "S_CROP denied: getting current crop failed\n"); - } else if ((a->c.width == current_crop.c.width && - a->c.height == current_crop.c.height) || - !is_streaming(ici, icd)) { - /* same size or not streaming - use .set_crop() */ - ret = ici->ops->set_crop(icd, a); - } else if (ici->ops->set_livecrop) { - ret = ici->ops->set_livecrop(icd, a); - } else { - dev_err(icd->pdev, - "S_CROP denied: queue initialised and sizes differ\n"); - ret = -EBUSY; - } - - return ret; -} - static int soc_camera_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -1101,9 +1035,6 @@ static int soc_camera_g_selection(struct file *file, void *fh, if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!ici->ops->get_selection) - return -ENOTTY; - return ici->ops->get_selection(icd, s); } @@ -1135,10 +1066,11 @@ static int soc_camera_s_selection(struct file *file, void *fh, return -EBUSY; } - if (!ici->ops->set_selection) - return -ENOTTY; - - ret = ici->ops->set_selection(icd, s); + if (s->target == V4L2_SEL_TGT_CROP && is_streaming(ici, icd) && + ici->ops->set_liveselection) + ret = ici->ops->set_liveselection(icd, s); + else + ret = ici->ops->set_selection(icd, s); if (!ret && s->target == V4L2_SEL_TGT_COMPOSE) { icd->user_width = s->r.width; @@ -1881,23 +1813,40 @@ static int soc_camera_remove(struct soc_camera_device *icd) return 0; } -static int default_cropcap(struct soc_camera_device *icd, - struct v4l2_cropcap *a) +static int default_g_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, cropcap, a); -} + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + int ret; -static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, g_crop, a); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; } -static int default_s_crop(struct soc_camera_device *icd, const struct v4l2_crop *a) +static int default_s_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, s_crop, a); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; } static int default_g_parm(struct soc_camera_device *icd, @@ -1968,12 +1917,10 @@ int soc_camera_host_register(struct soc_camera_host *ici) !ici->v4l2_dev.dev) return -EINVAL; - if (!ici->ops->set_crop) - ici->ops->set_crop = default_s_crop; - if (!ici->ops->get_crop) - ici->ops->get_crop = default_g_crop; - if (!ici->ops->cropcap) - ici->ops->cropcap = default_cropcap; + if (!ici->ops->set_selection) + ici->ops->set_selection = default_s_selection; + if (!ici->ops->get_selection) + ici->ops->get_selection = default_g_selection; if (!ici->ops->set_parm) ici->ops->set_parm = default_s_parm; if (!ici->ops->get_parm) @@ -2126,9 +2073,6 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_expbuf = soc_camera_expbuf, .vidioc_streamon = soc_camera_streamon, .vidioc_streamoff = soc_camera_streamoff, - .vidioc_cropcap = soc_camera_cropcap, - .vidioc_g_crop = soc_camera_g_crop, - .vidioc_s_crop = soc_camera_s_crop, .vidioc_g_selection = soc_camera_g_selection, .vidioc_s_selection = soc_camera_s_selection, .vidioc_g_parm = soc_camera_g_parm, diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index a51d2a42998c..534d6c3c6d60 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -76,35 +76,27 @@ static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int soc_camera_platform_g_crop(struct v4l2_subdev *sd, - struct v4l2_crop *a) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - a->c.left = 0; - a->c.top = 0; - a->c.width = p->format.width; - a->c.height = p->format.height; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, - struct v4l2_cropcap *a) +static int soc_camera_platform_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = p->format.width; - a->bounds.height = p->format.height; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = p->format.width; + sel->r.height = p->format.height; + return 0; + default: + return -EINVAL; + } } static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, @@ -120,13 +112,12 @@ static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, - .cropcap = soc_camera_platform_cropcap, - .g_crop = soc_camera_platform_g_crop, .g_mbus_config = soc_camera_platform_g_mbus_config, }; static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = { .enum_mbus_code = soc_camera_platform_enum_mbus_code, + .get_selection = soc_camera_platform_get_selection, .get_fmt = soc_camera_platform_fill_fmt, .set_fmt = soc_camera_platform_fill_fmt, }; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index bda29bc1b933..f77252d6ccd3 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -40,24 +40,22 @@ static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) /* Get and store current client crop */ int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) { - struct v4l2_crop crop; - struct v4l2_cropcap cap; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + }; int ret; - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, g_crop, &crop); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); if (!ret) { - *rect = crop.c; + *rect = sdsel.r; return ret; } - /* Camera driver doesn't support .g_crop(), assume default rectangle */ - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, cropcap, &cap); + sdsel.target = V4L2_SEL_TGT_CROP_DEFAULT; + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); if (!ret) - *rect = cap.defrect; + *rect = sdsel.r; return ret; } @@ -93,17 +91,27 @@ static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect) * 2. if (1) failed, try to double the client image until we get one big enough * 3. if (2) failed, try to request the maximum image */ -int soc_camera_client_s_crop(struct v4l2_subdev *sd, - struct v4l2_crop *crop, struct v4l2_crop *cam_crop, +int soc_camera_client_s_selection(struct v4l2_subdev *sd, + struct v4l2_selection *sel, struct v4l2_selection *cam_sel, struct v4l2_rect *target_rect, struct v4l2_rect *subrect) { - struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + struct v4l2_subdev_selection bounds = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP_BOUNDS, + }; + struct v4l2_rect *rect = &sel->r, *cam_rect = &cam_sel->r; struct device *dev = sd->v4l2_dev->dev; - struct v4l2_cropcap cap; int ret; unsigned int width, height; - v4l2_subdev_call(sd, video, s_crop, crop); + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + sel->r = sdsel.r; ret = soc_camera_client_g_rect(sd, cam_rect); if (ret < 0) return ret; @@ -113,29 +121,29 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * be within camera cropcap bounds */ if (!memcmp(rect, cam_rect, sizeof(*rect))) { - /* Even if camera S_CROP failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", + /* Even if camera S_SELECTION failed, but camera rectangle matches */ + dev_dbg(dev, "Camera S_SELECTION successful for %dx%d@%d:%d\n", rect->width, rect->height, rect->left, rect->top); *target_rect = *cam_rect; return 0; } /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", + dev_geo(dev, "Fix camera S_SELECTION for %dx%d@%d:%d to %dx%d@%d:%d\n", cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top, rect->width, rect->height, rect->left, rect->top); /* We need sensor maximum rectangle */ - ret = v4l2_subdev_call(sd, video, cropcap, &cap); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &bounds); if (ret < 0) return ret; /* Put user requested rectangle within sensor bounds */ - soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, - cap.bounds.width); - soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, - cap.bounds.height); + soc_camera_limit_side(&rect->left, &rect->width, sdsel.r.left, 2, + bounds.r.width); + soc_camera_limit_side(&rect->top, &rect->height, sdsel.r.top, 4, + bounds.r.height); /* * Popular special case - some cameras can only handle fixed sizes like @@ -150,7 +158,7 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, */ while (!ret && (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) && - (cap.bounds.width > width || cap.bounds.height > height)) { + (bounds.r.width > width || bounds.r.height > height)) { width *= 2; height *= 2; @@ -168,36 +176,40 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * Instead we just drop to the left and top bounds. */ if (cam_rect->left > rect->left) - cam_rect->left = cap.bounds.left; + cam_rect->left = bounds.r.left; if (cam_rect->left + cam_rect->width < rect->left + rect->width) cam_rect->width = rect->left + rect->width - cam_rect->left; if (cam_rect->top > rect->top) - cam_rect->top = cap.bounds.top; + cam_rect->top = bounds.r.top; if (cam_rect->top + cam_rect->height < rect->top + rect->height) cam_rect->height = rect->top + rect->height - cam_rect->top; - v4l2_subdev_call(sd, video, s_crop, cam_crop); + sdsel.r = *cam_rect; + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + *cam_rect = sdsel.r; ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, + dev_geo(dev, "Camera S_SELECTION %d for %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } - /* S_CROP must not modify the rectangle */ + /* S_SELECTION must not modify the rectangle */ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { /* * The camera failed to configure a suitable cropping, * we cannot use the current rectangle, set to max */ - *cam_rect = cap.bounds; - v4l2_subdev_call(sd, video, s_crop, cam_crop); + sdsel.r = bounds.r; + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + *cam_rect = sdsel.r; + ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, + dev_geo(dev, "Camera S_SELECTION %d for max %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } @@ -209,7 +221,7 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, return ret; } -EXPORT_SYMBOL(soc_camera_client_s_crop); +EXPORT_SYMBOL(soc_camera_client_s_selection); /* Iterative set_fmt, also updates cached client crop on success */ static int client_set_fmt(struct soc_camera_device *icd, @@ -221,7 +233,10 @@ static int client_set_fmt(struct soc_camera_device *icd, struct device *dev = icd->parent; struct v4l2_mbus_framefmt *mf = &format->format; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; - struct v4l2_cropcap cap; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP_BOUNDS, + }; bool host_1to1; int ret; @@ -243,16 +258,14 @@ static int client_set_fmt(struct soc_camera_device *icd, if (!host_can_scale) goto update_cache; - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, cropcap, &cap); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); if (ret < 0) return ret; - if (max_width > cap.bounds.width) - max_width = cap.bounds.width; - if (max_height > cap.bounds.height) - max_height = cap.bounds.height; + if (max_width > sdsel.r.width) + max_width = sdsel.r.width; + if (max_height > sdsel.r.height) + max_height = sdsel.r.height; /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h index 184a30dff541..9ca469312a1f 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.h +++ b/drivers/media/platform/soc_camera/soc_scale_crop.h @@ -16,7 +16,7 @@ struct soc_camera_device; -struct v4l2_crop; +struct v4l2_selection; struct v4l2_mbus_framefmt; struct v4l2_pix_format; struct v4l2_rect; @@ -31,8 +31,8 @@ static inline unsigned int soc_camera_shift_scale(unsigned int size, #define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out) int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); -int soc_camera_client_s_crop(struct v4l2_subdev *sd, - struct v4l2_crop *crop, struct v4l2_crop *cam_crop, +int soc_camera_client_s_selection(struct v4l2_subdev *sd, + struct v4l2_selection *sel, struct v4l2_selection *cam_sel, struct v4l2_rect *target_rect, struct v4l2_rect *subrect); int soc_camera_client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, struct v4l2_rect *subrect, diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index 3b1ac687d0df..45f82b5ddd77 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -527,7 +527,7 @@ static void bdisp_stop_streaming(struct vb2_queue *q) pm_runtime_put(ctx->bdisp_dev->dev); } -static struct vb2_ops bdisp_qops = { +static const struct vb2_ops bdisp_qops = { .queue_setup = bdisp_queue_setup, .buf_prepare = bdisp_buf_prepare, .buf_queue = bdisp_buf_queue, diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/sti/hva/Makefile new file mode 100644 index 000000000000..ffb69cebaef3 --- /dev/null +++ b/drivers/media/platform/sti/hva/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_VIDEO_STI_HVA) := st-hva.o +st-hva-y := hva-v4l2.o hva-hw.o hva-mem.o hva-h264.o diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/sti/hva/hva-h264.c new file mode 100644 index 000000000000..8cc8467c0cd3 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-h264.c @@ -0,0 +1,1050 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "hva.h" +#include "hva-hw.h" + +#define MAX_SPS_PPS_SIZE 128 + +#define BITSTREAM_OFFSET_MASK 0x7F + +/* video max size*/ +#define H264_MAX_SIZE_W 1920 +#define H264_MAX_SIZE_H 1920 + +/* macroBlocs number (width & height) */ +#define MB_W(w) ((w + 0xF) / 0x10) +#define MB_H(h) ((h + 0xF) / 0x10) + +/* formula to get temporal or spatial data size */ +#define DATA_SIZE(w, h) (MB_W(w) * MB_H(h) * 16) + +#define SEARCH_WINDOW_BUFFER_MAX_SIZE(w) ((4 * MB_W(w) + 42) * 256 * 3 / 2) +#define CABAC_CONTEXT_BUFFER_MAX_SIZE(w) (MB_W(w) * 16) +#define CTX_MB_BUFFER_MAX_SIZE(w) (MB_W(w) * 16 * 8) +#define SLICE_HEADER_SIZE (4 * 16) +#define BRC_DATA_SIZE (5 * 16) + +/* source buffer copy in YUV 420 MB-tiled format with size=16*256*3/2 */ +#define CURRENT_WINDOW_BUFFER_MAX_SIZE (16 * 256 * 3 / 2) + +/* + * 4 lines of pixels (in Luma, Chroma blue and Chroma red) of top MB + * for deblocking with size=4*16*MBx*2 + */ +#define LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(w) (4 * 16 * MB_W(w) * 2) + +/* factor for bitrate and cpb buffer size max values if profile >= high */ +#define H264_FACTOR_HIGH 1200 + +/* factor for bitrate and cpb buffer size max values if profile < high */ +#define H264_FACTOR_BASELINE 1000 + +/* number of bytes for NALU_TYPE_FILLER_DATA header and footer */ +#define H264_FILLER_DATA_SIZE 6 + +struct h264_profile { + enum v4l2_mpeg_video_h264_level level; + u32 max_mb_per_seconds; + u32 max_frame_size; + u32 max_bitrate; + u32 max_cpb_size; + u32 min_comp_ratio; +}; + +static const struct h264_profile h264_infos_list[] = { + {V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 1485, 99, 64, 175, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1B, 1485, 99, 128, 350, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_1, 3000, 396, 192, 500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_2, 6000, 396, 384, 1000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_3, 11880, 396, 768, 2000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_0, 11880, 396, 2000, 2000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_1, 19800, 792, 4000, 4000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_2, 20250, 1620, 4000, 4000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_0, 40500, 1620, 10000, 10000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 108000, 3600, 14000, 14000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_2, 216000, 5120, 20000, 20000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 245760, 8192, 20000, 25000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_1, 245760, 8192, 50000, 62500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 522240, 8704, 50000, 62500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_5_0, 589824, 22080, 135000, 135000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 983040, 36864, 240000, 240000, 2} +}; + +enum hva_brc_type { + BRC_TYPE_NONE = 0, + BRC_TYPE_CBR = 1, + BRC_TYPE_VBR = 2, + BRC_TYPE_VBR_LOW_DELAY = 3 +}; + +enum hva_entropy_coding_mode { + CAVLC = 0, + CABAC = 1 +}; + +enum hva_picture_coding_type { + PICTURE_CODING_TYPE_I = 0, + PICTURE_CODING_TYPE_P = 1, + PICTURE_CODING_TYPE_B = 2 +}; + +enum hva_h264_sampling_mode { + SAMPLING_MODE_NV12 = 0, + SAMPLING_MODE_UYVY = 1, + SAMPLING_MODE_RGB3 = 3, + SAMPLING_MODE_XRGB4 = 4, + SAMPLING_MODE_NV21 = 8, + SAMPLING_MODE_VYUY = 9, + SAMPLING_MODE_BGR3 = 11, + SAMPLING_MODE_XBGR4 = 12, + SAMPLING_MODE_RGBX4 = 20, + SAMPLING_MODE_BGRX4 = 28 +}; + +enum hva_h264_nalu_type { + NALU_TYPE_UNKNOWN = 0, + NALU_TYPE_SLICE = 1, + NALU_TYPE_SLICE_DPA = 2, + NALU_TYPE_SLICE_DPB = 3, + NALU_TYPE_SLICE_DPC = 4, + NALU_TYPE_SLICE_IDR = 5, + NALU_TYPE_SEI = 6, + NALU_TYPE_SPS = 7, + NALU_TYPE_PPS = 8, + NALU_TYPE_AU_DELIMITER = 9, + NALU_TYPE_SEQ_END = 10, + NALU_TYPE_STREAM_END = 11, + NALU_TYPE_FILLER_DATA = 12, + NALU_TYPE_SPS_EXT = 13, + NALU_TYPE_PREFIX_UNIT = 14, + NALU_TYPE_SUBSET_SPS = 15, + NALU_TYPE_SLICE_AUX = 19, + NALU_TYPE_SLICE_EXT = 20 +}; + +enum hva_h264_sei_payload_type { + SEI_BUFFERING_PERIOD = 0, + SEI_PICTURE_TIMING = 1, + SEI_STEREO_VIDEO_INFO = 21, + SEI_FRAME_PACKING_ARRANGEMENT = 45 +}; + +/** + * stereo Video Info struct + */ +struct hva_h264_stereo_video_sei { + u8 field_views_flag; + u8 top_field_is_left_view_flag; + u8 current_frame_is_left_view_flag; + u8 next_frame_is_second_view_flag; + u8 left_view_self_contained_flag; + u8 right_view_self_contained_flag; +}; + +/** + * @frame_width: width in pixels of the buffer containing the input frame + * @frame_height: height in pixels of the buffer containing the input frame + * @frame_num: the parameter to be written in the slice header + * @picture_coding_type: type I, P or B + * @pic_order_cnt_type: POC mode, as defined in H264 std : can be 0,1,2 + * @first_picture_in_sequence: flag telling to encoder that this is the + * first picture in a video sequence. + * Used for VBR + * @slice_size_type: 0 = no constraint to close the slice + * 1= a slice is closed as soon as the slice_mb_size limit + * is reached + * 2= a slice is closed as soon as the slice_byte_size limit + * is reached + * 3= a slice is closed as soon as either the slice_byte_size + * limit or the slice_mb_size limit is reached + * @slice_mb_size: defines the slice size in number of macroblocks + * (used when slice_size_type=1 or slice_size_type=3) + * @ir_param_option: defines the number of macroblocks per frame to be + * refreshed by AIR algorithm OR the refresh period + * by CIR algorithm + * @intra_refresh_type: enables the adaptive intra refresh algorithm. + * Disable=0 / Adaptative=1 and Cycle=2 as intra refresh + * @use_constrained_intra_flag: constrained_intra_pred_flag from PPS + * @transform_mode: controls the use of 4x4/8x8 transform mode + * @disable_deblocking_filter_idc: + * 0: specifies that all luma and chroma block edges of + * the slice are filtered. + * 1: specifies that deblocking is disabled for all block + * edges of the slice. + * 2: specifies that all luma and chroma block edges of + * the slice are filtered with exception of the block edges + * that coincide with slice boundaries + * @slice_alpha_c0_offset_div2: to be written in slice header, + * controls deblocking + * @slice_beta_offset_div2: to be written in slice header, + * controls deblocking + * @encoder_complexity: encoder complexity control (IME). + * 0 = I_16x16, P_16x16, Full ME Complexity + * 1 = I_16x16, I_NxN, P_16x16, Full ME Complexity + * 2 = I_16x16, I_NXN, P_16x16, P_WxH, Full ME Complexity + * 4 = I_16x16, P_16x16, Reduced ME Complexity + * 5 = I_16x16, I_NxN, P_16x16, Reduced ME Complexity + * 6 = I_16x16, I_NXN, P_16x16, P_WxH, Reduced ME Complexity + * @chroma_qp_index_offset: coming from picture parameter set + * (PPS see [H.264 STD] 7.4.2.2) + * @entropy_coding_mode: entropy coding mode. + * 0 = CAVLC + * 1 = CABAC + * @brc_type: selects the bit-rate control algorithm + * 0 = constant Qp, (no BRC) + * 1 = CBR + * 2 = VBR + * @quant: Quantization param used in case of fix QP encoding (no BRC) + * @non_VCL_NALU_Size: size of non-VCL NALUs (SPS, PPS, filler), + * used by BRC + * @cpb_buffer_size: size of Coded Picture Buffer, used by BRC + * @bit_rate: target bitrate, for BRC + * @qp_min: min QP threshold + * @qp_max: max QP threshold + * @framerate_num: target framerate numerator , used by BRC + * @framerate_den: target framerate denomurator , used by BRC + * @delay: End-to-End Initial Delay + * @strict_HRD_compliancy: flag for HDR compliancy (1) + * May impact quality encoding + * @addr_source_buffer: address of input frame buffer for current frame + * @addr_fwd_Ref_Buffer: address of reference frame buffer + * @addr_rec_buffer: address of reconstructed frame buffer + * @addr_output_bitstream_start: output bitstream start address + * @addr_output_bitstream_end: output bitstream end address + * @addr_external_sw : address of external search window + * @addr_lctx : address of context picture buffer + * @addr_local_rec_buffer: address of local reconstructed buffer + * @addr_spatial_context: address of spatial context buffer + * @bitstream_offset: offset in bits between aligned bitstream start + * address and first bit to be written by HVA. + * Range value is [0..63] + * @sampling_mode: Input picture format . + * 0: YUV420 semi_planar Interleaved + * 1: YUV422 raster Interleaved + * @addr_param_out: address of output parameters structure + * @addr_scaling_matrix: address to the coefficient of + * the inverse scaling matrix + * @addr_scaling_matrix_dir: address to the coefficient of + * the direct scaling matrix + * @addr_cabac_context_buffer: address of cabac context buffer + * @GmvX: Input information about the horizontal global displacement of + * the encoded frame versus the previous one + * @GmvY: Input information about the vertical global displacement of + * the encoded frame versus the previous one + * @window_width: width in pixels of the window to be encoded inside + * the input frame + * @window_height: width in pixels of the window to be encoded inside + * the input frame + * @window_horizontal_offset: horizontal offset in pels for input window + * within input frame + * @window_vertical_offset: vertical offset in pels for input window + * within input frame + * @addr_roi: Map of QP offset for the Region of Interest algorithm and + * also used for Error map. + * Bit 0-6 used for qp offset (value -64 to 63). + * Bit 7 used to force intra + * @addr_slice_header: address to slice header + * @slice_header_size_in_bits: size in bits of the Slice header + * @slice_header_offset0: Slice header offset where to insert + * first_Mb_in_slice + * @slice_header_offset1: Slice header offset where to insert + * slice_qp_delta + * @slice_header_offset2: Slice header offset where to insert + * num_MBs_in_slice + * @slice_synchro_enable: enable "slice ready" interrupt after each slice + * @max_slice_number: Maximum number of slice in a frame + * (0 is strictly forbidden) + * @rgb2_yuv_y_coeff: Four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the Y component. + * Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @rgb2_yuv_u_coeff: four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the Y component. + * Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @rgb2_yuv_v_coeff: Four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the U (Cb) component. + * U = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @slice_byte_size: maximum slice size in bytes + * (used when slice_size_type=2 or slice_size_type=3) + * @max_air_intra_mb_nb: Maximum number of intra macroblock in a frame + * for the AIR algorithm + * @brc_no_skip: Disable skipping in the Bitrate Controller + * @addr_brc_in_out_parameter: address of static buffer for BRC parameters + */ +struct hva_h264_td { + u16 frame_width; + u16 frame_height; + u32 frame_num; + u16 picture_coding_type; + u16 reserved1; + u16 pic_order_cnt_type; + u16 first_picture_in_sequence; + u16 slice_size_type; + u16 reserved2; + u32 slice_mb_size; + u16 ir_param_option; + u16 intra_refresh_type; + u16 use_constrained_intra_flag; + u16 transform_mode; + u16 disable_deblocking_filter_idc; + s16 slice_alpha_c0_offset_div2; + s16 slice_beta_offset_div2; + u16 encoder_complexity; + s16 chroma_qp_index_offset; + u16 entropy_coding_mode; + u16 brc_type; + u16 quant; + u32 non_vcl_nalu_size; + u32 cpb_buffer_size; + u32 bit_rate; + u16 qp_min; + u16 qp_max; + u16 framerate_num; + u16 framerate_den; + u16 delay; + u16 strict_hrd_compliancy; + u32 addr_source_buffer; + u32 addr_fwd_ref_buffer; + u32 addr_rec_buffer; + u32 addr_output_bitstream_start; + u32 addr_output_bitstream_end; + u32 addr_external_sw; + u32 addr_lctx; + u32 addr_local_rec_buffer; + u32 addr_spatial_context; + u16 bitstream_offset; + u16 sampling_mode; + u32 addr_param_out; + u32 addr_scaling_matrix; + u32 addr_scaling_matrix_dir; + u32 addr_cabac_context_buffer; + u32 reserved3; + u32 reserved4; + s16 gmv_x; + s16 gmv_y; + u16 window_width; + u16 window_height; + u16 window_horizontal_offset; + u16 window_vertical_offset; + u32 addr_roi; + u32 addr_slice_header; + u16 slice_header_size_in_bits; + u16 slice_header_offset0; + u16 slice_header_offset1; + u16 slice_header_offset2; + u32 reserved5; + u32 reserved6; + u16 reserved7; + u16 reserved8; + u16 slice_synchro_enable; + u16 max_slice_number; + u32 rgb2_yuv_y_coeff; + u32 rgb2_yuv_u_coeff; + u32 rgb2_yuv_v_coeff; + u32 slice_byte_size; + u16 max_air_intra_mb_nb; + u16 brc_no_skip; + u32 addr_temporal_context; + u32 addr_brc_in_out_parameter; +}; + +/** + * @ slice_size: slice size + * @ slice_start_time: start time + * @ slice_stop_time: stop time + * @ slice_num: slice number + */ +struct hva_h264_slice_po { + u32 slice_size; + u32 slice_start_time; + u32 slice_end_time; + u32 slice_num; +}; + +/** + * @ bitstream_size: bitstream size + * @ dct_bitstream_size: dtc bitstream size + * @ stuffing_bits: number of stuffing bits inserted by the encoder + * @ removal_time: removal time of current frame (nb of ticks 1/framerate) + * @ hvc_start_time: hvc start time + * @ hvc_stop_time: hvc stop time + * @ slice_count: slice count + */ +struct hva_h264_po { + u32 bitstream_size; + u32 dct_bitstream_size; + u32 stuffing_bits; + u32 removal_time; + u32 hvc_start_time; + u32 hvc_stop_time; + u32 slice_count; + u32 reserved0; + struct hva_h264_slice_po slice_params[16]; +}; + +struct hva_h264_task { + struct hva_h264_td td; + struct hva_h264_po po; +}; + +/** + * @seq_info: sequence information buffer + * @ref_frame: reference frame buffer + * @rec_frame: reconstructed frame buffer + * @task: task descriptor + */ +struct hva_h264_ctx { + struct hva_buffer *seq_info; + struct hva_buffer *ref_frame; + struct hva_buffer *rec_frame; + struct hva_buffer *task; +}; + +static int hva_h264_fill_slice_header(struct hva_ctx *pctx, + u8 *slice_header_addr, + struct hva_controls *ctrls, + int frame_num, + u16 *header_size, + u16 *header_offset0, + u16 *header_offset1, + u16 *header_offset2) +{ + /* + * with this HVA hardware version, part of the slice header is computed + * on host and part by hardware. + * The part of host is precomputed and available through this array. + */ + struct device *dev = ctx_to_dev(pctx); + int cabac = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC; + const unsigned char slice_header[] = { 0x00, 0x00, 0x00, 0x01, + 0x41, 0x34, 0x07, 0x00}; + int idr_pic_id = frame_num % 2; + enum hva_picture_coding_type type; + u32 frame_order = frame_num % ctrls->gop_size; + + if (!(frame_num % ctrls->gop_size)) + type = PICTURE_CODING_TYPE_I; + else + type = PICTURE_CODING_TYPE_P; + + memcpy(slice_header_addr, slice_header, sizeof(slice_header)); + + *header_size = 56; + *header_offset0 = 40; + *header_offset1 = 13; + *header_offset2 = 0; + + if (type == PICTURE_CODING_TYPE_I) { + slice_header_addr[4] = 0x65; + slice_header_addr[5] = 0x11; + + /* toggle the I frame */ + if ((frame_num / ctrls->gop_size) % 2) { + *header_size += 4; + *header_offset1 += 4; + slice_header_addr[6] = 0x04; + slice_header_addr[7] = 0x70; + + } else { + *header_size += 2; + *header_offset1 += 2; + slice_header_addr[6] = 0x09; + slice_header_addr[7] = 0xC0; + } + } else { + if (ctrls->entropy_mode == cabac) { + *header_size += 1; + *header_offset1 += 1; + slice_header_addr[7] = 0x80; + } + /* + * update slice header with P frame order + * frame order is limited to 16 (coded on 4bits only) + */ + slice_header_addr[5] += ((frame_order & 0x0C) >> 2); + slice_header_addr[6] += ((frame_order & 0x03) << 6); + } + + dev_dbg(dev, + "%s %s slice header order %d idrPicId %d header size %d\n", + pctx->name, __func__, frame_order, idr_pic_id, *header_size); + return 0; +} + +static int hva_h264_fill_data_nal(struct hva_ctx *pctx, + unsigned int stuffing_bytes, u8 *addr, + unsigned int stream_size, unsigned int *size) +{ + struct device *dev = ctx_to_dev(pctx); + const u8 start[] = { 0x00, 0x00, 0x00, 0x01 }; + + dev_dbg(dev, "%s %s stuffing bytes %d\n", pctx->name, __func__, + stuffing_bytes); + + if ((*size + stuffing_bytes + H264_FILLER_DATA_SIZE) > stream_size) { + dev_dbg(dev, "%s %s too many stuffing bytes %d\n", + pctx->name, __func__, stuffing_bytes); + return 0; + } + + /* start code */ + memcpy(addr + *size, start, sizeof(start)); + *size += sizeof(start); + + /* nal_unit_type */ + addr[*size] = NALU_TYPE_FILLER_DATA; + *size += 1; + + memset(addr + *size, 0xff, stuffing_bytes); + *size += stuffing_bytes; + + addr[*size] = 0x80; + *size += 1; + + return 0; +} + +static int hva_h264_fill_sei_nal(struct hva_ctx *pctx, + enum hva_h264_sei_payload_type type, + u8 *addr, u32 *size) +{ + struct device *dev = ctx_to_dev(pctx); + const u8 start[] = { 0x00, 0x00, 0x00, 0x01 }; + struct hva_h264_stereo_video_sei info; + u8 offset = 7; + u8 msg = 0; + + /* start code */ + memcpy(addr + *size, start, sizeof(start)); + *size += sizeof(start); + + /* nal_unit_type */ + addr[*size] = NALU_TYPE_SEI; + *size += 1; + + /* payload type */ + addr[*size] = type; + *size += 1; + + switch (type) { + case SEI_STEREO_VIDEO_INFO: + memset(&info, 0, sizeof(info)); + + /* set to top/bottom frame packing arrangement */ + info.field_views_flag = 1; + info.top_field_is_left_view_flag = 1; + + /* payload size */ + addr[*size] = 1; + *size += 1; + + /* payload */ + msg = info.field_views_flag << offset--; + + if (info.field_views_flag) { + msg |= info.top_field_is_left_view_flag << + offset--; + } else { + msg |= info.current_frame_is_left_view_flag << + offset--; + msg |= info.next_frame_is_second_view_flag << + offset--; + } + msg |= info.left_view_self_contained_flag << offset--; + msg |= info.right_view_self_contained_flag << offset--; + + addr[*size] = msg; + *size += 1; + + addr[*size] = 0x80; + *size += 1; + + return 0; + case SEI_BUFFERING_PERIOD: + case SEI_PICTURE_TIMING: + case SEI_FRAME_PACKING_ARRANGEMENT: + default: + dev_err(dev, "%s sei nal type not supported %d\n", + pctx->name, type); + return -EINVAL; + } +} + +static int hva_h264_prepare_task(struct hva_ctx *pctx, + struct hva_h264_task *task, + struct hva_frame *frame, + struct hva_stream *stream) +{ + struct hva_dev *hva = ctx_to_hdev(pctx); + struct device *dev = ctx_to_dev(pctx); + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct hva_buffer *seq_info = ctx->seq_info; + struct hva_buffer *fwd_ref_frame = ctx->ref_frame; + struct hva_buffer *loc_rec_frame = ctx->rec_frame; + struct hva_h264_td *td = &task->td; + struct hva_controls *ctrls = &pctx->ctrls; + struct v4l2_fract *time_per_frame = &pctx->ctrls.time_per_frame; + int cavlc = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC; + u32 frame_num = pctx->stream_num; + u32 addr_esram = hva->esram_addr; + enum v4l2_mpeg_video_h264_level level; + dma_addr_t paddr = 0; + u8 *slice_header_vaddr; + u32 frame_width = frame->info.aligned_width; + u32 frame_height = frame->info.aligned_height; + u32 max_cpb_buffer_size; + unsigned int payload = stream->bytesused; + u32 max_bitrate; + + /* check width and height parameters */ + if ((frame_width > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H)) || + (frame_height > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H))) { + dev_err(dev, + "%s width(%d) or height(%d) exceeds limits (%dx%d)\n", + pctx->name, frame_width, frame_height, + H264_MAX_SIZE_W, H264_MAX_SIZE_H); + return -EINVAL; + } + + level = ctrls->level; + + memset(td, 0, sizeof(struct hva_h264_td)); + + td->frame_width = frame_width; + td->frame_height = frame_height; + + /* set frame alignement */ + td->window_width = frame_width; + td->window_height = frame_height; + td->window_horizontal_offset = 0; + td->window_vertical_offset = 0; + + td->first_picture_in_sequence = (!frame_num) ? 1 : 0; + + /* pic_order_cnt_type hard coded to '2' as only I & P frames */ + td->pic_order_cnt_type = 2; + + /* useConstrainedIntraFlag set to false for better coding efficiency */ + td->use_constrained_intra_flag = false; + td->brc_type = (ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + ? BRC_TYPE_CBR : BRC_TYPE_VBR; + + td->entropy_coding_mode = (ctrls->entropy_mode == cavlc) ? CAVLC : + CABAC; + + td->bit_rate = ctrls->bitrate; + + /* set framerate, framerate = 1 n/ time per frame */ + if (time_per_frame->numerator >= 536) { + /* + * due to a hardware bug, framerate denominator can't exceed + * 536 (BRC overflow). Compute nearest framerate + */ + td->framerate_den = 1; + td->framerate_num = (time_per_frame->denominator + + (time_per_frame->numerator >> 1) - 1) / + time_per_frame->numerator; + + /* + * update bitrate to introduce a correction due to + * the new framerate + * new bitrate = (old bitrate * new framerate) / old framerate + */ + td->bit_rate /= time_per_frame->numerator; + td->bit_rate *= time_per_frame->denominator; + td->bit_rate /= td->framerate_num; + } else { + td->framerate_den = time_per_frame->numerator; + td->framerate_num = time_per_frame->denominator; + } + + /* compute maximum bitrate depending on profile */ + if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) + max_bitrate = h264_infos_list[level].max_bitrate * + H264_FACTOR_HIGH; + else + max_bitrate = h264_infos_list[level].max_bitrate * + H264_FACTOR_BASELINE; + + /* check if bitrate doesn't exceed max size */ + if (td->bit_rate > max_bitrate) { + dev_dbg(dev, + "%s bitrate (%d) larger than level and profile allow, clip to %d\n", + pctx->name, td->bit_rate, max_bitrate); + td->bit_rate = max_bitrate; + } + + /* convert cpb_buffer_size in bits */ + td->cpb_buffer_size = ctrls->cpb_size * 8000; + + /* compute maximum cpb buffer size depending on profile */ + if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) + max_cpb_buffer_size = + h264_infos_list[level].max_cpb_size * H264_FACTOR_HIGH; + else + max_cpb_buffer_size = + h264_infos_list[level].max_cpb_size * H264_FACTOR_BASELINE; + + /* check if cpb buffer size doesn't exceed max size */ + if (td->cpb_buffer_size > max_cpb_buffer_size) { + dev_dbg(dev, + "%s cpb size larger than level %d allows, clip to %d\n", + pctx->name, td->cpb_buffer_size, max_cpb_buffer_size); + td->cpb_buffer_size = max_cpb_buffer_size; + } + + /* enable skipping in the Bitrate Controller */ + td->brc_no_skip = 0; + + /* initial delay */ + if ((ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) && + td->bit_rate) + td->delay = 1000 * (td->cpb_buffer_size / td->bit_rate); + else + td->delay = 0; + + switch (frame->info.pixelformat) { + case V4L2_PIX_FMT_NV12: + td->sampling_mode = SAMPLING_MODE_NV12; + break; + case V4L2_PIX_FMT_NV21: + td->sampling_mode = SAMPLING_MODE_NV21; + break; + default: + dev_err(dev, "%s invalid source pixel format\n", + pctx->name); + return -EINVAL; + } + + /* + * fill matrix color converter (RGB to YUV) + * Y = 0,299 R + 0,587 G + 0,114 B + * Cb = -0,1687 R -0,3313 G + 0,5 B + 128 + * Cr = 0,5 R - 0,4187 G - 0,0813 B + 128 + */ + td->rgb2_yuv_y_coeff = 0x12031008; + td->rgb2_yuv_u_coeff = 0x800EF7FB; + td->rgb2_yuv_v_coeff = 0x80FEF40E; + + /* enable/disable transform mode */ + td->transform_mode = ctrls->dct8x8; + + /* encoder complexity fix to 2, ENCODE_I_16x16_I_NxN_P_16x16_P_WxH */ + td->encoder_complexity = 2; + + /* quant fix to 28, default VBR value */ + td->quant = 28; + + if (td->framerate_den == 0) { + dev_err(dev, "%s invalid framerate\n", pctx->name); + return -EINVAL; + } + + /* if automatic framerate, deactivate bitrate controller */ + if (td->framerate_num == 0) + td->brc_type = 0; + + /* compliancy fix to true */ + td->strict_hrd_compliancy = 1; + + /* set minimum & maximum quantizers */ + td->qp_min = clamp_val(ctrls->qpmin, 0, 51); + td->qp_max = clamp_val(ctrls->qpmax, 0, 51); + + td->addr_source_buffer = frame->paddr; + td->addr_fwd_ref_buffer = fwd_ref_frame->paddr; + td->addr_rec_buffer = loc_rec_frame->paddr; + + td->addr_output_bitstream_end = (u32)stream->paddr + stream->size; + + td->addr_output_bitstream_start = (u32)stream->paddr; + td->bitstream_offset = (((u32)stream->paddr & 0xF) << 3) & + BITSTREAM_OFFSET_MASK; + + td->addr_param_out = (u32)ctx->task->paddr + + offsetof(struct hva_h264_task, po); + + /* swap spatial and temporal context */ + if (frame_num % 2) { + paddr = seq_info->paddr; + td->addr_spatial_context = ALIGN(paddr, 0x100); + paddr = seq_info->paddr + DATA_SIZE(frame_width, + frame_height); + td->addr_temporal_context = ALIGN(paddr, 0x100); + } else { + paddr = seq_info->paddr; + td->addr_temporal_context = ALIGN(paddr, 0x100); + paddr = seq_info->paddr + DATA_SIZE(frame_width, + frame_height); + td->addr_spatial_context = ALIGN(paddr, 0x100); + } + + paddr = seq_info->paddr + 2 * DATA_SIZE(frame_width, frame_height); + + td->addr_brc_in_out_parameter = ALIGN(paddr, 0x100); + + paddr = td->addr_brc_in_out_parameter + BRC_DATA_SIZE; + td->addr_slice_header = ALIGN(paddr, 0x100); + td->addr_external_sw = ALIGN(addr_esram, 0x100); + + addr_esram += SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width); + td->addr_local_rec_buffer = ALIGN(addr_esram, 0x100); + + addr_esram += LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width); + td->addr_lctx = ALIGN(addr_esram, 0x100); + + addr_esram += CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height)); + td->addr_cabac_context_buffer = ALIGN(addr_esram, 0x100); + + if (!(frame_num % ctrls->gop_size)) { + td->picture_coding_type = PICTURE_CODING_TYPE_I; + stream->vbuf.flags |= V4L2_BUF_FLAG_KEYFRAME; + } else { + td->picture_coding_type = PICTURE_CODING_TYPE_P; + stream->vbuf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } + + /* fill the slice header part */ + slice_header_vaddr = seq_info->vaddr + (td->addr_slice_header - + seq_info->paddr); + + hva_h264_fill_slice_header(pctx, slice_header_vaddr, ctrls, frame_num, + &td->slice_header_size_in_bits, + &td->slice_header_offset0, + &td->slice_header_offset1, + &td->slice_header_offset2); + + td->chroma_qp_index_offset = 2; + td->slice_synchro_enable = 0; + td->max_slice_number = 1; + + /* + * check the sps/pps header size for key frame only + * sps/pps header was previously fill by libv4l + * during qbuf of stream buffer + */ + if ((stream->vbuf.flags == V4L2_BUF_FLAG_KEYFRAME) && + (payload > MAX_SPS_PPS_SIZE)) { + dev_err(dev, "%s invalid sps/pps size %d\n", pctx->name, + payload); + return -EINVAL; + } + + if (stream->vbuf.flags != V4L2_BUF_FLAG_KEYFRAME) + payload = 0; + + /* add SEI nal (video stereo info) */ + if (ctrls->sei_fp && hva_h264_fill_sei_nal(pctx, SEI_STEREO_VIDEO_INFO, + (u8 *)stream->vaddr, + &payload)) { + dev_err(dev, "%s fail to get SEI nal\n", pctx->name); + return -EINVAL; + } + + /* fill size of non-VCL NAL units (SPS, PPS, filler and SEI) */ + td->non_vcl_nalu_size = payload * 8; + + /* compute bitstream offset & new start address of bitstream */ + td->addr_output_bitstream_start += ((payload >> 4) << 4); + td->bitstream_offset += (payload - ((payload >> 4) << 4)) * 8; + + stream->bytesused = payload; + + return 0; +} + +static unsigned int hva_h264_get_stream_size(struct hva_h264_task *task) +{ + struct hva_h264_po *po = &task->po; + + return po->bitstream_size; +} + +static u32 hva_h264_get_stuffing_bytes(struct hva_h264_task *task) +{ + struct hva_h264_po *po = &task->po; + + return po->stuffing_bits >> 3; +} + +static int hva_h264_open(struct hva_ctx *pctx) +{ + struct device *dev = ctx_to_dev(pctx); + struct hva_h264_ctx *ctx; + struct hva_dev *hva = ctx_to_hdev(pctx); + u32 frame_width = pctx->frameinfo.aligned_width; + u32 frame_height = pctx->frameinfo.aligned_height; + u32 size; + int ret; + + /* check esram size necessary to encode a frame */ + size = SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width) + + LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width) + + CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height)) + + CABAC_CONTEXT_BUFFER_MAX_SIZE(frame_width); + + if (hva->esram_size < size) { + dev_err(dev, "%s not enough esram (max:%d request:%d)\n", + pctx->name, hva->esram_size, size); + ret = -EINVAL; + goto err; + } + + /* allocate context for codec */ + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err; + } + + /* allocate sequence info buffer */ + ret = hva_mem_alloc(pctx, + 2 * DATA_SIZE(frame_width, frame_height) + + SLICE_HEADER_SIZE + + BRC_DATA_SIZE, + "hva sequence info", + &ctx->seq_info); + if (ret) { + dev_err(dev, + "%s failed to allocate sequence info buffer\n", + pctx->name); + goto err_ctx; + } + + /* allocate reference frame buffer */ + ret = hva_mem_alloc(pctx, + frame_width * frame_height * 3 / 2, + "hva reference frame", + &ctx->ref_frame); + if (ret) { + dev_err(dev, "%s failed to allocate reference frame buffer\n", + pctx->name); + goto err_seq_info; + } + + /* allocate reconstructed frame buffer */ + ret = hva_mem_alloc(pctx, + frame_width * frame_height * 3 / 2, + "hva reconstructed frame", + &ctx->rec_frame); + if (ret) { + dev_err(dev, + "%s failed to allocate reconstructed frame buffer\n", + pctx->name); + goto err_ref_frame; + } + + /* allocate task descriptor */ + ret = hva_mem_alloc(pctx, + sizeof(struct hva_h264_task), + "hva task descriptor", + &ctx->task); + if (ret) { + dev_err(dev, + "%s failed to allocate task descriptor\n", + pctx->name); + goto err_rec_frame; + } + + pctx->priv = (void *)ctx; + + return 0; + +err_rec_frame: + hva_mem_free(pctx, ctx->rec_frame); +err_ref_frame: + hva_mem_free(pctx, ctx->ref_frame); +err_seq_info: + hva_mem_free(pctx, ctx->seq_info); +err_ctx: + devm_kfree(dev, ctx); +err: + return ret; +} + +static int hva_h264_close(struct hva_ctx *pctx) +{ + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct device *dev = ctx_to_dev(pctx); + + if (ctx->seq_info) + hva_mem_free(pctx, ctx->seq_info); + + if (ctx->ref_frame) + hva_mem_free(pctx, ctx->ref_frame); + + if (ctx->rec_frame) + hva_mem_free(pctx, ctx->rec_frame); + + if (ctx->task) + hva_mem_free(pctx, ctx->task); + + devm_kfree(dev, ctx); + + return 0; +} + +static int hva_h264_encode(struct hva_ctx *pctx, struct hva_frame *frame, + struct hva_stream *stream) +{ + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct hva_h264_task *task = (struct hva_h264_task *)ctx->task->vaddr; + struct hva_buffer *tmp_frame; + u32 stuffing_bytes = 0; + int ret = 0; + + ret = hva_h264_prepare_task(pctx, task, frame, stream); + if (ret) + goto err; + + ret = hva_hw_execute_task(pctx, H264_ENC, ctx->task); + if (ret) + goto err; + + pctx->stream_num++; + stream->bytesused += hva_h264_get_stream_size(task); + + stuffing_bytes = hva_h264_get_stuffing_bytes(task); + + if (stuffing_bytes) + hva_h264_fill_data_nal(pctx, stuffing_bytes, + (u8 *)stream->vaddr, + stream->size, + &stream->bytesused); + + /* switch reference & reconstructed frame */ + tmp_frame = ctx->ref_frame; + ctx->ref_frame = ctx->rec_frame; + ctx->rec_frame = tmp_frame; + + return 0; +err: + stream->bytesused = 0; + return ret; +} + +const struct hva_enc nv12h264enc = { + .name = "H264(NV12)", + .pixelformat = V4L2_PIX_FMT_NV12, + .streamformat = V4L2_PIX_FMT_H264, + .max_width = H264_MAX_SIZE_W, + .max_height = H264_MAX_SIZE_H, + .open = hva_h264_open, + .close = hva_h264_close, + .encode = hva_h264_encode, +}; + +const struct hva_enc nv21h264enc = { + .name = "H264(NV21)", + .pixelformat = V4L2_PIX_FMT_NV21, + .streamformat = V4L2_PIX_FMT_H264, + .max_width = H264_MAX_SIZE_W, + .max_height = H264_MAX_SIZE_H, + .open = hva_h264_open, + .close = hva_h264_close, + .encode = hva_h264_encode, +}; diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c new file mode 100644 index 000000000000..d341d4994528 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-hw.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include "hva.h" +#include "hva-hw.h" + +/* HVA register offsets */ +#define HVA_HIF_REG_RST 0x0100U +#define HVA_HIF_REG_RST_ACK 0x0104U +#define HVA_HIF_REG_MIF_CFG 0x0108U +#define HVA_HIF_REG_HEC_MIF_CFG 0x010CU +#define HVA_HIF_REG_CFL 0x0110U +#define HVA_HIF_FIFO_CMD 0x0114U +#define HVA_HIF_FIFO_STS 0x0118U +#define HVA_HIF_REG_SFL 0x011CU +#define HVA_HIF_REG_IT_ACK 0x0120U +#define HVA_HIF_REG_ERR_IT_ACK 0x0124U +#define HVA_HIF_REG_LMI_ERR 0x0128U +#define HVA_HIF_REG_EMI_ERR 0x012CU +#define HVA_HIF_REG_HEC_MIF_ERR 0x0130U +#define HVA_HIF_REG_HEC_STS 0x0134U +#define HVA_HIF_REG_HVC_STS 0x0138U +#define HVA_HIF_REG_HJE_STS 0x013CU +#define HVA_HIF_REG_CNT 0x0140U +#define HVA_HIF_REG_HEC_CHKSYN_DIS 0x0144U +#define HVA_HIF_REG_CLK_GATING 0x0148U +#define HVA_HIF_REG_VERSION 0x014CU +#define HVA_HIF_REG_BSM 0x0150U + +/* define value for version id register (HVA_HIF_REG_VERSION) */ +#define VERSION_ID_MASK 0x0000FFFF + +/* define values for BSM register (HVA_HIF_REG_BSM) */ +#define BSM_CFG_VAL1 0x0003F000 +#define BSM_CFG_VAL2 0x003F0000 + +/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */ +#define MIF_CFG_VAL1 0x04460446 +#define MIF_CFG_VAL2 0x04460806 +#define MIF_CFG_VAL3 0x00000000 + +/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */ +#define HEC_MIF_CFG_VAL 0x000000C4 + +/* Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */ +#define CLK_GATING_HVC BIT(0) +#define CLK_GATING_HEC BIT(1) +#define CLK_GATING_HJE BIT(2) + +/* fix hva clock rate */ +#define CLK_RATE 300000000 + +/* fix delay for pmruntime */ +#define AUTOSUSPEND_DELAY_MS 3 + +/* + * hw encode error values + * NO_ERROR: Success, Task OK + * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer + * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size) + * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size + * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached + * H264_SLICE_READY: VECH264 Slice ready + * TASK_LIST_FULL: HVA/FPC task list full + (discard latest transform command) + * UNKNOWN_COMMAND: Transform command not known by HVA/FPC + * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection + * NO_INT_COMPLETION: Time-out on interrupt completion + * LMI_ERR: Local Memory Interface Error + * EMI_ERR: External Memory Interface Error + * HECMI_ERR: HEC Memory Interface Error + */ +enum hva_hw_error { + NO_ERROR = 0x0, + H264_BITSTREAM_OVERSIZE = 0x2, + H264_FRAME_SKIPPED = 0x4, + H264_SLICE_LIMIT_SIZE = 0x5, + H264_MAX_SLICE_NUMBER = 0x7, + H264_SLICE_READY = 0x8, + TASK_LIST_FULL = 0xF0, + UNKNOWN_COMMAND = 0xF1, + WRONG_CODEC_OR_RESOLUTION = 0xF4, + NO_INT_COMPLETION = 0x100, + LMI_ERR = 0x101, + EMI_ERR = 0x102, + HECMI_ERR = 0x103, +}; + +static irqreturn_t hva_hw_its_interrupt(int irq, void *data) +{ + struct hva_dev *hva = data; + + /* read status registers */ + hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS); + hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL); + + /* acknowledge interruption */ + writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg) +{ + struct hva_dev *hva = arg; + struct device *dev = hva_to_dev(hva); + u32 status = hva->sts_reg & 0xFF; + u8 ctx_id = 0; + struct hva_ctx *ctx = NULL; + + dev_dbg(dev, "%s %s: status: 0x%02x fifo level: 0x%02x\n", + HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF); + + /* + * status: task_id[31:16] client_id[15:8] status[7:0] + * the context identifier is retrieved from the client identifier + */ + ctx_id = (hva->sts_reg & 0xFF00) >> 8; + if (ctx_id >= HVA_MAX_INSTANCES) { + dev_err(dev, "%s %s: bad context identifier: %d\n", + ctx->name, __func__, ctx_id); + ctx->hw_err = true; + goto out; + } + + ctx = hva->instances[ctx_id]; + if (!ctx) + goto out; + + switch (status) { + case NO_ERROR: + dev_dbg(dev, "%s %s: no error\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_SLICE_READY: + dev_dbg(dev, "%s %s: h264 slice ready\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_FRAME_SKIPPED: + dev_dbg(dev, "%s %s: h264 frame skipped\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_BITSTREAM_OVERSIZE: + dev_err(dev, "%s %s:h264 bitstream oversize\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case H264_SLICE_LIMIT_SIZE: + dev_err(dev, "%s %s: h264 slice limit size is reached\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case H264_MAX_SLICE_NUMBER: + dev_err(dev, "%s %s: h264 max slice number is reached\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case TASK_LIST_FULL: + dev_err(dev, "%s %s:task list full\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case UNKNOWN_COMMAND: + dev_err(dev, "%s %s: command not known\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case WRONG_CODEC_OR_RESOLUTION: + dev_err(dev, "%s %s: wrong codec or resolution\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + default: + dev_err(dev, "%s %s: status not recognized\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + } +out: + complete(&hva->interrupt); + + return IRQ_HANDLED; +} + +static irqreturn_t hva_hw_err_interrupt(int irq, void *data) +{ + struct hva_dev *hva = data; + + /* read status registers */ + hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS); + hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL); + + /* read error registers */ + hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR); + hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR); + hva->hec_mif_err_reg = readl_relaxed(hva->regs + + HVA_HIF_REG_HEC_MIF_ERR); + + /* acknowledge interruption */ + writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg) +{ + struct hva_dev *hva = arg; + struct device *dev = hva_to_dev(hva); + u8 ctx_id = 0; + struct hva_ctx *ctx; + + dev_dbg(dev, "%s status: 0x%02x fifo level: 0x%02x\n", + HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF); + + /* + * status: task_id[31:16] client_id[15:8] status[7:0] + * the context identifier is retrieved from the client identifier + */ + ctx_id = (hva->sts_reg & 0xFF00) >> 8; + if (ctx_id >= HVA_MAX_INSTANCES) { + dev_err(dev, "%s bad context identifier: %d\n", HVA_PREFIX, + ctx_id); + goto out; + } + + ctx = hva->instances[ctx_id]; + if (!ctx) + goto out; + + if (hva->lmi_err_reg) { + dev_err(dev, "%s local memory interface error: 0x%08x\n", + ctx->name, hva->lmi_err_reg); + ctx->hw_err = true; + } + + if (hva->lmi_err_reg) { + dev_err(dev, "%s external memory interface error: 0x%08x\n", + ctx->name, hva->emi_err_reg); + ctx->hw_err = true; + } + + if (hva->hec_mif_err_reg) { + dev_err(dev, "%s hec memory interface error: 0x%08x\n", + ctx->name, hva->hec_mif_err_reg); + ctx->hw_err = true; + } +out: + complete(&hva->interrupt); + + return IRQ_HANDLED; +} + +static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + unsigned long int version; + + if (pm_runtime_get_sync(dev) < 0) { + dev_err(dev, "%s failed to get pm_runtime\n", HVA_PREFIX); + mutex_unlock(&hva->protect_mutex); + return -EFAULT; + } + + version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) & + VERSION_ID_MASK; + + pm_runtime_put_autosuspend(dev); + + switch (version) { + case HVA_VERSION_V400: + dev_dbg(dev, "%s IP hardware version 0x%lx\n", + HVA_PREFIX, version); + break; + default: + dev_err(dev, "%s unknown IP hardware version 0x%lx\n", + HVA_PREFIX, version); + version = HVA_VERSION_UNKNOWN; + break; + } + + return version; +} + +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva) +{ + struct device *dev = &pdev->dev; + struct resource *regs; + struct resource *esram; + int ret; + + WARN_ON(!hva); + + /* get memory for registers */ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hva->regs = devm_ioremap_resource(dev, regs); + if (IS_ERR_OR_NULL(hva->regs)) { + dev_err(dev, "%s failed to get regs\n", HVA_PREFIX); + return PTR_ERR(hva->regs); + } + + /* get memory for esram */ + esram = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (IS_ERR_OR_NULL(esram)) { + dev_err(dev, "%s failed to get esram\n", HVA_PREFIX); + return PTR_ERR(esram); + } + hva->esram_addr = esram->start; + hva->esram_size = resource_size(esram); + + dev_info(dev, "%s esram reserved for address: 0x%x size:%d\n", + HVA_PREFIX, hva->esram_addr, hva->esram_size); + + /* get clock resource */ + hva->clk = devm_clk_get(dev, "clk_hva"); + if (IS_ERR(hva->clk)) { + dev_err(dev, "%s failed to get clock\n", HVA_PREFIX); + return PTR_ERR(hva->clk); + } + + ret = clk_prepare(hva->clk); + if (ret < 0) { + dev_err(dev, "%s failed to prepare clock\n", HVA_PREFIX); + hva->clk = ERR_PTR(-EINVAL); + return ret; + } + + /* get status interruption resource */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "%s failed to get status IRQ\n", HVA_PREFIX); + goto err_clk; + } + hva->irq_its = ret; + + ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt, + hva_hw_its_irq_thread, + IRQF_ONESHOT, + "hva_its_irq", hva); + if (ret) { + dev_err(dev, "%s failed to install status IRQ 0x%x\n", + HVA_PREFIX, hva->irq_its); + goto err_clk; + } + disable_irq(hva->irq_its); + + /* get error interruption resource */ + ret = platform_get_irq(pdev, 1); + if (ret < 0) { + dev_err(dev, "%s failed to get error IRQ\n", HVA_PREFIX); + goto err_clk; + } + hva->irq_err = ret; + + ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt, + hva_hw_err_irq_thread, + IRQF_ONESHOT, + "hva_err_irq", hva); + if (ret) { + dev_err(dev, "%s failed to install error IRQ 0x%x\n", + HVA_PREFIX, hva->irq_err); + goto err_clk; + } + disable_irq(hva->irq_err); + + /* initialise protection mutex */ + mutex_init(&hva->protect_mutex); + + /* initialise completion signal */ + init_completion(&hva->interrupt); + + /* initialise runtime power management */ + pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "%s failed to set PM\n", HVA_PREFIX); + goto err_clk; + } + + /* check IP hardware version */ + hva->ip_version = hva_hw_get_ip_version(hva); + + if (hva->ip_version == HVA_VERSION_UNKNOWN) { + ret = -EINVAL; + goto err_pm; + } + + dev_info(dev, "%s found hva device (version 0x%lx)\n", HVA_PREFIX, + hva->ip_version); + + return 0; + +err_pm: + pm_runtime_put(dev); +err_clk: + if (hva->clk) + clk_unprepare(hva->clk); + + return ret; +} + +void hva_hw_remove(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + + disable_irq(hva->irq_its); + disable_irq(hva->irq_err); + + pm_runtime_put_autosuspend(dev); + pm_runtime_disable(dev); +} + +int hva_hw_runtime_suspend(struct device *dev) +{ + struct hva_dev *hva = dev_get_drvdata(dev); + + clk_disable_unprepare(hva->clk); + + return 0; +} + +int hva_hw_runtime_resume(struct device *dev) +{ + struct hva_dev *hva = dev_get_drvdata(dev); + + if (clk_prepare_enable(hva->clk)) { + dev_err(hva->dev, "%s failed to prepare hva clk\n", + HVA_PREFIX); + return -EINVAL; + } + + if (clk_set_rate(hva->clk, CLK_RATE)) { + dev_err(dev, "%s failed to set clock frequency\n", + HVA_PREFIX); + return -EINVAL; + } + + return 0; +} + +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd, + struct hva_buffer *task) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = hva_to_dev(hva); + u8 client_id = ctx->id; + int ret; + u32 reg = 0; + + mutex_lock(&hva->protect_mutex); + + /* enable irqs */ + enable_irq(hva->irq_its); + enable_irq(hva->irq_err); + + if (pm_runtime_get_sync(dev) < 0) { + dev_err(dev, "%s failed to get pm_runtime\n", ctx->name); + ret = -EFAULT; + goto out; + } + + reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING); + switch (cmd) { + case H264_ENC: + reg |= CLK_GATING_HVC; + break; + default: + dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd); + ret = -EFAULT; + goto out; + } + writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING); + + dev_dbg(dev, "%s %s: write configuration registers\n", ctx->name, + __func__); + + /* byte swap config */ + writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM); + + /* define Max Opcode Size and Max Message Size for LMI and EMI */ + writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG); + writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG); + + /* + * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0] + * the context identifier is provided as client identifier to the + * hardware, and is retrieved in the interrupt functions from the + * status register + */ + dev_dbg(dev, "%s %s: send task (cmd: %d, task_desc: %pad)\n", + ctx->name, __func__, cmd + (client_id << 8), &task->paddr); + writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD); + writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD); + + if (!wait_for_completion_timeout(&hva->interrupt, + msecs_to_jiffies(2000))) { + dev_err(dev, "%s %s: time out on completion\n", ctx->name, + __func__); + ret = -EFAULT; + goto out; + } + + /* get encoding status */ + ret = ctx->hw_err ? -EFAULT : 0; + +out: + disable_irq(hva->irq_its); + disable_irq(hva->irq_err); + + switch (cmd) { + case H264_ENC: + reg &= ~CLK_GATING_HVC; + writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING); + break; + default: + dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd); + } + + pm_runtime_put_autosuspend(dev); + mutex_unlock(&hva->protect_mutex); + + return ret; +} diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h new file mode 100644 index 000000000000..efb45b927524 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-hw.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_HW_H +#define HVA_HW_H + +#include "hva-mem.h" + +/* HVA Versions */ +#define HVA_VERSION_UNKNOWN 0x000 +#define HVA_VERSION_V400 0x400 + +/* HVA command types */ +enum hva_hw_cmd_type { + /* RESERVED = 0x00 */ + /* RESERVED = 0x01 */ + H264_ENC = 0x02, + /* RESERVED = 0x03 */ + /* RESERVED = 0x04 */ + /* RESERVED = 0x05 */ + /* RESERVED = 0x06 */ + /* RESERVED = 0x07 */ + REMOVE_CLIENT = 0x08, + FREEZE_CLIENT = 0x09, + START_CLIENT = 0x0A, + FREEZE_ALL = 0x0B, + START_ALL = 0x0C, + REMOVE_ALL = 0x0D +}; + +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva); +void hva_hw_remove(struct hva_dev *hva); +int hva_hw_runtime_suspend(struct device *dev); +int hva_hw_runtime_resume(struct device *dev); +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd, + struct hva_buffer *task); + +#endif /* HVA_HW_H */ diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c new file mode 100644 index 000000000000..649f66007ad6 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-mem.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "hva.h" +#include "hva-mem.h" + +int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name, + struct hva_buffer **buf) +{ + struct device *dev = ctx_to_dev(ctx); + struct hva_buffer *b; + dma_addr_t paddr; + void *base; + + b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL); + if (!b) + return -ENOMEM; + + base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, + DMA_ATTR_WRITE_COMBINE); + if (!base) { + dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n", + ctx->name, __func__, name, size); + devm_kfree(dev, b); + return -ENOMEM; + } + + b->size = size; + b->paddr = paddr; + b->vaddr = base; + b->name = name; + + dev_dbg(dev, + "%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n", + ctx->name, size, b->vaddr, &b->paddr, b->name); + + /* return hva buffer to user */ + *buf = b; + + return 0; +} + +void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf) +{ + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, + "%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n", + ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name); + + dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr, + DMA_ATTR_WRITE_COMBINE); + + devm_kfree(dev, buf); +} diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/sti/hva/hva-mem.h new file mode 100644 index 000000000000..a95c728a45e6 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-mem.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_MEM_H +#define HVA_MEM_H + +/** + * struct hva_buffer - hva buffer + * + * @name: name of requester + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @size: size of buffer + */ +struct hva_buffer { + const char *name; + dma_addr_t paddr; + void *vaddr; + u32 size; +}; + +int hva_mem_alloc(struct hva_ctx *ctx, + __u32 size, + const char *name, + struct hva_buffer **buf); + +void hva_mem_free(struct hva_ctx *ctx, + struct hva_buffer *buf); + +#endif /* HVA_MEM_H */ diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c new file mode 100644 index 000000000000..6bf3c8588230 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -0,0 +1,1415 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include "hva.h" +#include "hva-hw.h" + +#define HVA_NAME "st-hva" + +#define MIN_FRAMES 1 +#define MIN_STREAMS 1 + +#define HVA_MIN_WIDTH 32 +#define HVA_MAX_WIDTH 1920 +#define HVA_MIN_HEIGHT 32 +#define HVA_MAX_HEIGHT 1920 + +/* HVA requires a 16x16 pixels alignment for frames */ +#define HVA_WIDTH_ALIGNMENT 16 +#define HVA_HEIGHT_ALIGNMENT 16 + +#define HVA_DEFAULT_WIDTH HVA_MIN_WIDTH +#define HVA_DEFAULT_HEIGHT HVA_MIN_HEIGHT +#define HVA_DEFAULT_FRAME_NUM 1 +#define HVA_DEFAULT_FRAME_DEN 30 + +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \ + "frame" : "stream") + +#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) + +/* registry of available encoders */ +static const struct hva_enc *hva_encoders[] = { + &nv12h264enc, + &nv21h264enc, +}; + +static inline int frame_size(u32 w, u32 h, u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return (w * h * 3) / 2; + default: + return 0; + } +} + +static inline int frame_stride(u32 w, u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return w; + default: + return 0; + } +} + +static inline int frame_alignment(u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + /* multiple of 2 */ + return 2; + default: + return 1; + } +} + +static inline int estimated_stream_size(u32 w, u32 h) +{ + /* + * HVA only encodes in YUV420 format, whatever the frame format. + * A compression ratio of 2 is assumed: thus, the maximum size + * of a stream is estimated to ((width x height x 3 / 2) / 2) + */ + return (w * h * 3) / 4; +} + +static void set_default_params(struct hva_ctx *ctx) +{ + struct hva_frameinfo *frameinfo = &ctx->frameinfo; + struct hva_streaminfo *streaminfo = &ctx->streaminfo; + + frameinfo->pixelformat = V4L2_PIX_FMT_NV12; + frameinfo->width = HVA_DEFAULT_WIDTH; + frameinfo->height = HVA_DEFAULT_HEIGHT; + frameinfo->aligned_width = ALIGN(frameinfo->width, + HVA_WIDTH_ALIGNMENT); + frameinfo->aligned_height = ALIGN(frameinfo->height, + HVA_HEIGHT_ALIGNMENT); + frameinfo->size = frame_size(frameinfo->aligned_width, + frameinfo->aligned_height, + frameinfo->pixelformat); + + streaminfo->streamformat = V4L2_PIX_FMT_H264; + streaminfo->width = HVA_DEFAULT_WIDTH; + streaminfo->height = HVA_DEFAULT_HEIGHT; + + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + + ctx->max_stream_size = estimated_stream_size(streaminfo->width, + streaminfo->height); +} + +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx, + u32 pixelformat, + u32 streamformat) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + const struct hva_enc *enc; + unsigned int i; + + for (i = 0; i < hva->nb_of_encoders; i++) { + enc = hva->encoders[i]; + if ((enc->pixelformat == pixelformat) && + (enc->streamformat == streamformat)) + return enc; + } + + return NULL; +} + +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats) +{ + u32 i; + bool found = false; + + for (i = 0; i < *nb_of_formats; i++) { + if (format == formats[i]) { + found = true; + break; + } + } + + if (!found) + formats[(*nb_of_formats)++] = format; +} + +static void register_formats(struct hva_dev *hva) +{ + unsigned int i; + + for (i = 0; i < hva->nb_of_encoders; i++) { + register_format(hva->encoders[i]->pixelformat, + hva->pixelformats, + &hva->nb_of_pixelformats); + + register_format(hva->encoders[i]->streamformat, + hva->streamformats, + &hva->nb_of_streamformats); + } +} + +static void register_encoders(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) { + if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) { + dev_dbg(dev, + "%s failed to register %s encoder (%d maximum reached)\n", + HVA_PREFIX, hva_encoders[i]->name, + HVA_MAX_ENCODERS); + return; + } + + hva->encoders[hva->nb_of_encoders++] = hva_encoders[i]; + dev_info(dev, "%s %s encoder registered\n", HVA_PREFIX, + hva_encoders[i]->name); + } +} + +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat, + u32 pixelformat, struct hva_enc **penc) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + struct hva_enc *enc; + int ret; + + /* find an encoder which can deal with these formats */ + enc = (struct hva_enc *)hva_find_encoder(ctx, pixelformat, + streamformat); + if (!enc) { + dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n", + ctx->name, (char *)&pixelformat, (char *)&streamformat); + return -EINVAL; + } + + dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n", + ctx->name, (char *)&pixelformat, (char *)&streamformat); + + /* update instance name */ + snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]", + hva->instance_id, (char *)&streamformat); + + /* open encoder instance */ + ret = enc->open(ctx); + if (ret) { + dev_err(dev, "%s failed to open encoder instance (%d)\n", + ctx->name, ret); + return ret; + } + + dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name); + + *penc = enc; + + return ret; +} + +/* + * V4L2 ioctl operations + */ + +static int hva_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + strlcpy(cap->driver, HVA_NAME, sizeof(cap->driver)); + strlcpy(cap->card, hva->vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + hva->pdev->name); + + return 0; +} + +static int hva_enum_fmt_stream(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + if (unlikely(f->index >= hva->nb_of_streamformats)) + return -EINVAL; + + f->pixelformat = hva->streamformats[f->index]; + + return 0; +} + +static int hva_enum_fmt_frame(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + if (unlikely(f->index >= hva->nb_of_pixelformats)) + return -EINVAL; + + f->pixelformat = hva->pixelformats[f->index]; + + return 0; +} + +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_streaminfo *streaminfo = &ctx->streaminfo; + + f->fmt.pix.width = streaminfo->width; + f->fmt.pix.height = streaminfo->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + f->fmt.pix.pixelformat = streaminfo->streamformat; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = ctx->max_stream_size; + + return 0; +} + +static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_frameinfo *frameinfo = &ctx->frameinfo; + + f->fmt.pix.width = frameinfo->width; + f->fmt.pix.height = frameinfo->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + f->fmt.pix.pixelformat = frameinfo->pixelformat; + f->fmt.pix.bytesperline = frame_stride(frameinfo->aligned_width, + frameinfo->pixelformat); + f->fmt.pix.sizeimage = frameinfo->size; + + return 0; +} + +static int hva_try_fmt_stream(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 streamformat = pix->pixelformat; + const struct hva_enc *enc; + u32 width, height; + u32 stream_size; + + enc = hva_find_encoder(ctx, ctx->frameinfo.pixelformat, streamformat); + if (!enc) { + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): unsupported format %.4s\n", + ctx->name, (char *)&pix->pixelformat); + return -EINVAL; + } + + width = pix->width; + height = pix->height; + if (ctx->flags & HVA_FLAG_FRAMEINFO) { + /* + * if the frame resolution is already fixed, only allow the + * same stream resolution + */ + pix->width = ctx->frameinfo.width; + pix->height = ctx->frameinfo.height; + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit frame resolution\n", + ctx->name, width, height, + pix->width, pix->height); + } else { + /* adjust width & height */ + v4l_bound_align_image(&pix->width, + HVA_MIN_WIDTH, enc->max_width, + 0, + &pix->height, + HVA_MIN_HEIGHT, enc->max_height, + 0, + 0); + + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n", + ctx->name, width, height, + pix->width, pix->height); + } + + stream_size = estimated_stream_size(pix->width, pix->height); + if (pix->sizeimage < stream_size) + pix->sizeimage = stream_size; + + pix->bytesperline = 0; + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int hva_try_fmt_frame(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 pixelformat = pix->pixelformat; + const struct hva_enc *enc; + u32 width, height; + + enc = hva_find_encoder(ctx, pixelformat, ctx->streaminfo.streamformat); + if (!enc) { + dev_dbg(dev, + "%s V4L2 TRY_FMT (OUTPUT): unsupported format %.4s\n", + ctx->name, (char *)&pixelformat); + return -EINVAL; + } + + /* adjust width & height */ + width = pix->width; + height = pix->height; + v4l_bound_align_image(&pix->width, + HVA_MIN_WIDTH, HVA_MAX_WIDTH, + frame_alignment(pixelformat) - 1, + &pix->height, + HVA_MIN_HEIGHT, HVA_MAX_HEIGHT, + frame_alignment(pixelformat) - 1, + 0); + + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (OUTPUT): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n", + ctx->name, width, height, pix->width, pix->height); + + width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT); + height = ALIGN(pix->height, HVA_HEIGHT_ALIGNMENT); + + if (!pix->colorspace) { + pix->colorspace = V4L2_COLORSPACE_REC709; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + } + + pix->bytesperline = frame_stride(width, pixelformat); + pix->sizeimage = frame_size(width, height, pixelformat); + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct vb2_queue *vq; + int ret; + + ret = hva_try_fmt_stream(file, fh, f); + if (ret) { + dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): unsupported format %.4s\n", + ctx->name, (char *)&f->fmt.pix.pixelformat); + return ret; + } + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_streaming(vq)) { + dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): queue busy\n", + ctx->name); + return -EBUSY; + } + + ctx->max_stream_size = f->fmt.pix.sizeimage; + ctx->streaminfo.width = f->fmt.pix.width; + ctx->streaminfo.height = f->fmt.pix.height; + ctx->streaminfo.streamformat = f->fmt.pix.pixelformat; + ctx->flags |= HVA_FLAG_STREAMINFO; + + return 0; +} + +static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct vb2_queue *vq; + int ret; + + ret = hva_try_fmt_frame(file, fh, f); + if (ret) { + dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): unsupported format %.4s\n", + ctx->name, (char *)&pix->pixelformat); + return ret; + } + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_streaming(vq)) { + dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): queue busy\n", ctx->name); + return -EBUSY; + } + + ctx->colorspace = pix->colorspace; + ctx->xfer_func = pix->xfer_func; + ctx->ycbcr_enc = pix->ycbcr_enc; + ctx->quantization = pix->quantization; + + ctx->frameinfo.aligned_width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT); + ctx->frameinfo.aligned_height = ALIGN(pix->height, + HVA_HEIGHT_ALIGNMENT); + ctx->frameinfo.size = pix->sizeimage; + ctx->frameinfo.pixelformat = pix->pixelformat; + ctx->frameinfo.width = pix->width; + ctx->frameinfo.height = pix->height; + ctx->flags |= HVA_FLAG_FRAMEINFO; + + return 0; +} + +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + sp->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + sp->parm.output.timeperframe.numerator = time_per_frame->numerator; + sp->parm.output.timeperframe.denominator = + time_per_frame->denominator; + + return 0; +} + +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (!sp->parm.output.timeperframe.numerator || + !sp->parm.output.timeperframe.denominator) + return hva_g_parm(file, fh, sp); + + sp->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + time_per_frame->numerator = sp->parm.output.timeperframe.numerator; + time_per_frame->denominator = + sp->parm.output.timeperframe.denominator; + + return 0; +} + +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + /* + * depending on the targeted compressed video format, the + * capture buffer might contain headers (e.g. H.264 SPS/PPS) + * filled in by the driver client; the size of these data is + * copied from the bytesused field of the V4L2 buffer in the + * payload field of the hva stream buffer + */ + struct vb2_queue *vq; + struct hva_stream *stream; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type); + + if (buf->index >= vq->num_buffers) { + dev_dbg(dev, "%s buffer index %d out of range (%d)\n", + ctx->name, buf->index, vq->num_buffers); + return -EINVAL; + } + + stream = (struct hva_stream *)vq->bufs[buf->index]; + stream->bytesused = buf->bytesused; + } + + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); +} + +/* V4L2 ioctl ops */ +static const struct v4l2_ioctl_ops hva_ioctl_ops = { + .vidioc_querycap = hva_querycap, + .vidioc_enum_fmt_vid_cap = hva_enum_fmt_stream, + .vidioc_enum_fmt_vid_out = hva_enum_fmt_frame, + .vidioc_g_fmt_vid_cap = hva_g_fmt_stream, + .vidioc_g_fmt_vid_out = hva_g_fmt_frame, + .vidioc_try_fmt_vid_cap = hva_try_fmt_stream, + .vidioc_try_fmt_vid_out = hva_try_fmt_frame, + .vidioc_s_fmt_vid_cap = hva_s_fmt_stream, + .vidioc_s_fmt_vid_out = hva_s_fmt_frame, + .vidioc_g_parm = hva_g_parm, + .vidioc_s_parm = hva_s_parm, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_qbuf = hva_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * V4L2 control operations + */ + +static int hva_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx, + ctrl_handler); + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name, + ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctx->ctrls.bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctx->ctrls.gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctx->ctrls.bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctx->ctrls.aspect = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctx->ctrls.profile = ctrl->val; + if (ctx->flags & HVA_FLAG_STREAMINFO) + snprintf(ctx->streaminfo.profile, + sizeof(ctx->streaminfo.profile), + "%s profile", + v4l2_ctrl_get_menu(ctrl->id)[ctrl->val]); + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctx->ctrls.level = ctrl->val; + if (ctx->flags & HVA_FLAG_STREAMINFO) + snprintf(ctx->streaminfo.level, + sizeof(ctx->streaminfo.level), + "level %s", + v4l2_ctrl_get_menu(ctrl->id)[ctrl->val]); + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + ctx->ctrls.entropy_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + ctx->ctrls.cpb_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + ctx->ctrls.dct8x8 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->ctrls.qpmin = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->ctrls.qpmax = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: + ctx->ctrls.vui_sar = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: + ctx->ctrls.vui_sar_idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + ctx->ctrls.sei_fp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: + ctx->ctrls.sei_fp_type = ctrl->val; + break; + default: + dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n", + ctx->name, ctrl->id); + return -EINVAL; + } + + return 0; +} + +/* V4L2 control ops */ +static const struct v4l2_ctrl_ops hva_ctrl_ops = { + .s_ctrl = hva_s_ctrl, +}; + +static int hva_ctrls_setup(struct hva_ctx *ctx) +{ + struct device *dev = ctx_to_dev(ctx); + u64 mask; + enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type = + V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 15); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 60, 1, 16); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 1000, 60000000, 1000, 20000000); + + mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_1x1, + mask, + V4L2_MPEG_VIDEO_ASPECT_1x1); + + mask = ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH)); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH, + mask, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + 0, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 1, 10000, 1, 3000); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 5); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, + 0, 1, 1, 1); + + mask = ~(1 << V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1, + mask, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, + 0, 1, 1, 0); + + mask = ~(1 << sei_fp_type); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE, + sei_fp_type, + mask, + sei_fp_type); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + dev_dbg(dev, "%s controls setup failed (%d)\n", + ctx->name, err); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + + /* set default time per frame */ + ctx->ctrls.time_per_frame.numerator = HVA_DEFAULT_FRAME_NUM; + ctx->ctrls.time_per_frame.denominator = HVA_DEFAULT_FRAME_DEN; + + return 0; +} + +/* + * mem-to-mem operations + */ + +static void hva_run_work(struct work_struct *work) +{ + struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work); + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct hva_enc *enc = ctx->enc; + struct hva_frame *frame; + struct hva_stream *stream; + int ret; + + /* protect instance against reentrancy */ + mutex_lock(&ctx->lock); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + frame = to_hva_frame(src_buf); + stream = to_hva_stream(dst_buf); + frame->vbuf.sequence = ctx->frame_num++; + + ret = enc->encode(ctx, frame, stream); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, stream->bytesused); + if (ret) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } else { + /* propagate frame timestamp */ + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->field = V4L2_FIELD_NONE; + dst_buf->sequence = ctx->stream_num - 1; + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + } + + mutex_unlock(&ctx->lock); + + v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static void hva_device_run(void *priv) +{ + struct hva_ctx *ctx = priv; + struct hva_dev *hva = ctx_to_hdev(ctx); + + queue_work(hva->work_queue, &ctx->run_work); +} + +static void hva_job_abort(void *priv) +{ + struct hva_ctx *ctx = priv; + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, "%s aborting job\n", ctx->name); + + ctx->aborting = true; +} + +static int hva_job_ready(void *priv) +{ + struct hva_ctx *ctx = priv; + struct device *dev = ctx_to_dev(ctx); + + if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + dev_dbg(dev, "%s job not ready: no frame buffers\n", + ctx->name); + return 0; + } + + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + dev_dbg(dev, "%s job not ready: no stream buffers\n", + ctx->name); + return 0; + } + + if (ctx->aborting) { + dev_dbg(dev, "%s job not ready: aborting\n", ctx->name); + return 0; + } + + return 1; +} + +/* mem-to-mem ops */ +static const struct v4l2_m2m_ops hva_m2m_ops = { + .device_run = hva_device_run, + .job_abort = hva_job_abort, + .job_ready = hva_job_ready, +}; + +/* + * VB2 queue operations + */ + +static int hva_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx_to_dev(ctx); + unsigned int size; + + dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name, + to_type_str(vq->type), *num_buffers); + + size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? + ctx->frameinfo.size : ctx->max_stream_size; + + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + + /* only one plane supported */ + *num_planes = 1; + sizes[0] = size; + + return 0; +} + +static int hva_buf_prepare(struct vb2_buffer *vb) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct device *dev = ctx_to_dev(ctx); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct hva_frame *frame = to_hva_frame(vbuf); + + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dev_dbg(dev, + "%s frame[%d] prepare: %d field not supported\n", + ctx->name, vb->index, vbuf->field); + return -EINVAL; + } + + if (!frame->prepared) { + /* get memory addresses */ + frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + frame->paddr = vb2_dma_contig_plane_dma_addr( + &vbuf->vb2_buf, 0); + frame->info = ctx->frameinfo; + frame->prepared = true; + + dev_dbg(dev, + "%s frame[%d] prepared; virt=%p, phy=%pad\n", + ctx->name, vb->index, + frame->vaddr, &frame->paddr); + } + } else { + struct hva_stream *stream = to_hva_stream(vbuf); + + if (!stream->prepared) { + /* get memory addresses */ + stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + stream->paddr = vb2_dma_contig_plane_dma_addr( + &vbuf->vb2_buf, 0); + stream->size = vb2_plane_size(&vbuf->vb2_buf, 0); + stream->prepared = true; + + dev_dbg(dev, + "%s stream[%d] prepared; virt=%p, phy=%pad\n", + ctx->name, vb->index, + stream->vaddr, &stream->paddr); + } + } + + return 0; +} + +static void hva_buf_queue(struct vb2_buffer *vb) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (ctx->fh.m2m_ctx) + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + struct vb2_v4l2_buffer *vbuf; + int ret; + unsigned int i; + bool found = false; + + dev_dbg(dev, "%s %s start streaming\n", ctx->name, + to_type_str(vq->type)); + + /* open encoder when both start_streaming have been called */ + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q)) + return 0; + } else { + if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; + } + + /* store the instance context in the instances array */ + for (i = 0; i < HVA_MAX_INSTANCES; i++) { + if (!hva->instances[i]) { + hva->instances[i] = ctx; + /* save the context identifier in the context */ + ctx->id = i; + found = true; + break; + } + } + + if (!found) { + dev_err(dev, "%s maximum instances reached\n", ctx->name); + ret = -ENOMEM; + goto err; + } + + hva->nb_of_instances++; + + if (!ctx->enc) { + ret = hva_open_encoder(ctx, + ctx->streaminfo.streamformat, + ctx->frameinfo.pixelformat, + &ctx->enc); + if (ret < 0) + goto err_ctx; + } + + return 0; + +err_ctx: + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; +err: + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* return of all pending buffers to vb2 (in queued state) */ + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_QUEUED); + } else { + /* return of all pending buffers to vb2 (in queued state) */ + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_QUEUED); + } + + return ret; +} + +static void hva_stop_streaming(struct vb2_queue *vq) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + const struct hva_enc *enc = ctx->enc; + struct vb2_v4l2_buffer *vbuf; + + dev_dbg(dev, "%s %s stop streaming\n", ctx->name, + to_type_str(vq->type)); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* return of all pending buffers to vb2 (in error state) */ + ctx->frame_num = 0; + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } else { + /* return of all pending buffers to vb2 (in error state) */ + ctx->stream_num = 0; + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } + + if ((V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || + (!V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { + dev_dbg(dev, "%s %s out=%d cap=%d\n", + ctx->name, to_type_str(vq->type), + vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)); + return; + } + + /* close encoder when both stop_streaming have been called */ + if (enc) { + dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name); + enc->close(ctx); + ctx->enc = NULL; + + /* clear instance context in instances array */ + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; + } + + ctx->aborting = false; +} + +/* VB2 queue ops */ +static const struct vb2_ops hva_qops = { + .queue_setup = hva_queue_setup, + .buf_prepare = hva_buf_prepare, + .buf_queue = hva_buf_queue, + .start_streaming = hva_start_streaming, + .stop_streaming = hva_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* + * V4L2 file operations + */ + +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq) +{ + vq->io_modes = VB2_MMAP | VB2_DMABUF; + vq->drv_priv = ctx; + vq->ops = &hva_qops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + vq->lock = &ctx->hva_dev->lock; + + return vb2_queue_init(vq); +} + +static int hva_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct hva_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->buf_struct_size = sizeof(struct hva_frame); + src_vq->min_buffers_needed = MIN_FRAMES; + src_vq->dev = ctx->hva_dev->dev; + + ret = queue_init(ctx, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->buf_struct_size = sizeof(struct hva_stream); + dst_vq->min_buffers_needed = MIN_STREAMS; + dst_vq->dev = ctx->hva_dev->dev; + + return queue_init(ctx, dst_vq); +} + +static int hva_open(struct file *file) +{ + struct hva_dev *hva = video_drvdata(file); + struct device *dev = hva_to_dev(hva); + struct hva_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto out; + } + ctx->hva_dev = hva; + + INIT_WORK(&ctx->run_work, hva_run_work); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ret = hva_ctrls_setup(ctx); + if (ret) { + dev_err(dev, "%s [x:x] failed to setup controls\n", + HVA_PREFIX); + goto err_fh; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + mutex_init(&ctx->lock); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx, + &hva_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + dev_err(dev, "%s failed to initialize m2m context (%d)\n", + HVA_PREFIX, ret); + goto err_ctrls; + } + + /* set the instance name */ + mutex_lock(&hva->lock); + hva->instance_id++; + snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]", + hva->instance_id); + mutex_unlock(&hva->lock); + + /* default parameters for frame and stream */ + set_default_params(ctx); + + dev_info(dev, "%s encoder instance created\n", ctx->name); + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); +err_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); +out: + return ret; +} + +static int hva_release(struct file *file) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + const struct hva_enc *enc = ctx->enc; + + if (enc) { + dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name); + enc->close(ctx); + ctx->enc = NULL; + + /* clear instance context in instances array */ + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; + } + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + dev_info(dev, "%s encoder instance released\n", ctx->name); + + kfree(ctx); + + return 0; +} + +/* V4L2 file ops */ +static const struct v4l2_file_operations hva_fops = { + .owner = THIS_MODULE, + .open = hva_open, + .release = hva_release, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, + .poll = v4l2_m2m_fop_poll, +}; + +/* + * Platform device operations + */ + +static int hva_register_device(struct hva_dev *hva) +{ + int ret; + struct video_device *vdev; + struct device *dev; + + if (!hva) + return -ENODEV; + dev = hva_to_dev(hva); + + hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops); + if (IS_ERR(hva->m2m_dev)) { + dev_err(dev, "%s failed to initialize v4l2-m2m device\n", + HVA_PREFIX); + ret = PTR_ERR(hva->m2m_dev); + goto err; + } + + vdev = video_device_alloc(); + if (!vdev) { + dev_err(dev, "%s failed to allocate video device\n", + HVA_PREFIX); + ret = -ENOMEM; + goto err_m2m_release; + } + + vdev->fops = &hva_fops; + vdev->ioctl_ops = &hva_ioctl_ops; + vdev->release = video_device_release; + vdev->lock = &hva->lock; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + vdev->v4l2_dev = &hva->v4l2_dev; + snprintf(vdev->name, sizeof(vdev->name), "%s%lx", HVA_NAME, + hva->ip_version); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dev, "%s failed to register video device\n", + HVA_PREFIX); + goto err_vdev_release; + } + + hva->vdev = vdev; + video_set_drvdata(vdev, hva); + return 0; + +err_vdev_release: + video_device_release(vdev); +err_m2m_release: + v4l2_m2m_release(hva->m2m_dev); +err: + return ret; +} + +static void hva_unregister_device(struct hva_dev *hva) +{ + if (!hva) + return; + + if (hva->m2m_dev) + v4l2_m2m_release(hva->m2m_dev); + + video_unregister_device(hva->vdev); +} + +static int hva_probe(struct platform_device *pdev) +{ + struct hva_dev *hva; + struct device *dev = &pdev->dev; + int ret; + + hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL); + if (!hva) { + ret = -ENOMEM; + goto err; + } + + hva->dev = dev; + hva->pdev = pdev; + platform_set_drvdata(pdev, hva); + + mutex_init(&hva->lock); + + /* probe hardware */ + ret = hva_hw_probe(pdev, hva); + if (ret) + goto err; + + /* register all available encoders */ + register_encoders(hva); + + /* register all supported formats */ + register_formats(hva); + + /* register on V4L2 */ + ret = v4l2_device_register(dev, &hva->v4l2_dev); + if (ret) { + dev_err(dev, "%s %s failed to register V4L2 device\n", + HVA_PREFIX, HVA_NAME); + goto err_hw; + } + + hva->work_queue = create_workqueue(HVA_NAME); + if (!hva->work_queue) { + dev_err(dev, "%s %s failed to allocate work queue\n", + HVA_PREFIX, HVA_NAME); + ret = -ENOMEM; + goto err_v4l2; + } + + /* register device */ + ret = hva_register_device(hva); + if (ret) + goto err_work_queue; + + dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX, + HVA_NAME, hva->vdev->num); + + return 0; + +err_work_queue: + destroy_workqueue(hva->work_queue); +err_v4l2: + v4l2_device_unregister(&hva->v4l2_dev); +err_hw: + hva_hw_remove(hva); +err: + return ret; +} + +static int hva_remove(struct platform_device *pdev) +{ + struct hva_dev *hva = platform_get_drvdata(pdev); + struct device *dev = hva_to_dev(hva); + + hva_unregister_device(hva); + + destroy_workqueue(hva->work_queue); + + hva_hw_remove(hva); + + v4l2_device_unregister(&hva->v4l2_dev); + + dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name); + + return 0; +} + +/* PM ops */ +static const struct dev_pm_ops hva_pm_ops = { + .runtime_suspend = hva_hw_runtime_suspend, + .runtime_resume = hva_hw_runtime_resume, +}; + +static const struct of_device_id hva_match_types[] = { + { + .compatible = "st,st-hva", + }, + { /* end node */ } +}; + +MODULE_DEVICE_TABLE(of, hva_match_types); + +static struct platform_driver hva_driver = { + .probe = hva_probe, + .remove = hva_remove, + .driver = { + .name = HVA_NAME, + .of_match_table = hva_match_types, + .pm = &hva_pm_ops, + }, +}; + +module_platform_driver(hva_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics HVA video encoder V4L2 driver"); diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h new file mode 100644 index 000000000000..caa580825541 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva.h @@ -0,0 +1,315 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_H +#define HVA_H + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> +#include <media/v4l2-mem2mem.h> + +#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) + +#define hva_to_dev(h) (h->dev) + +#define ctx_to_dev(c) (c->hva_dev->dev) + +#define ctx_to_hdev(c) (c->hva_dev) + +#define HVA_PREFIX "[---:----]" + +extern const struct hva_enc nv12h264enc; +extern const struct hva_enc nv21h264enc; + +/** + * struct hva_frameinfo - information about hva frame + * + * @pixelformat: fourcc code for uncompressed video format + * @width: width of frame + * @height: height of frame + * @aligned_width: width of frame (with encoder alignment constraint) + * @aligned_height: height of frame (with encoder alignment constraint) + * @size: maximum size in bytes required for data +*/ +struct hva_frameinfo { + u32 pixelformat; + u32 width; + u32 height; + u32 aligned_width; + u32 aligned_height; + u32 size; +}; + +/** + * struct hva_streaminfo - information about hva stream + * + * @streamformat: fourcc code of compressed video format (H.264...) + * @width: width of stream + * @height: height of stream + * @profile: profile string + * @level: level string + */ +struct hva_streaminfo { + u32 streamformat; + u32 width; + u32 height; + u8 profile[32]; + u8 level[32]; +}; + +/** + * struct hva_controls - hva controls set + * + * @time_per_frame: time per frame in seconds + * @bitrate_mode: bitrate mode (constant bitrate or variable bitrate) + * @gop_size: groupe of picture size + * @bitrate: bitrate (in bps) + * @aspect: video aspect + * @profile: H.264 profile + * @level: H.264 level + * @entropy_mode: H.264 entropy mode (CABAC or CVLC) + * @cpb_size: coded picture buffer size (in kB) + * @dct8x8: transform mode 8x8 enable + * @qpmin: minimum quantizer + * @qpmax: maximum quantizer + * @vui_sar: pixel aspect ratio enable + * @vui_sar_idc: pixel aspect ratio identifier + * @sei_fp: sei frame packing arrangement enable + * @sei_fp_type: sei frame packing arrangement type + */ +struct hva_controls { + struct v4l2_fract time_per_frame; + enum v4l2_mpeg_video_bitrate_mode bitrate_mode; + u32 gop_size; + u32 bitrate; + enum v4l2_mpeg_video_aspect aspect; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + enum v4l2_mpeg_video_h264_entropy_mode entropy_mode; + u32 cpb_size; + bool dct8x8; + u32 qpmin; + u32 qpmax; + bool vui_sar; + enum v4l2_mpeg_video_h264_vui_sar_idc vui_sar_idc; + bool sei_fp; + enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type; +}; + +/** + * struct hva_frame - hva frame buffer (output) + * + * @vbuf: video buffer information for V4L2 + * @list: V4L2 m2m list that the frame belongs to + * @info: frame information (width, height, format, alignment...) + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @prepared: true if vaddr/paddr are resolved + */ +struct hva_frame { + struct vb2_v4l2_buffer vbuf; + struct list_head list; + struct hva_frameinfo info; + dma_addr_t paddr; + void *vaddr; + bool prepared; +}; + +/* + * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame * + */ +#define to_hva_frame(vb) \ + container_of(vb, struct hva_frame, vbuf) + +/** + * struct hva_stream - hva stream buffer (capture) + * + * @v4l2: video buffer information for V4L2 + * @list: V4L2 m2m list that the frame belongs to + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @prepared: true if vaddr/paddr are resolved + * @size: size of the buffer in bytes + * @bytesused: number of bytes occupied by data in the buffer + */ +struct hva_stream { + struct vb2_v4l2_buffer vbuf; + struct list_head list; + dma_addr_t paddr; + void *vaddr; + bool prepared; + unsigned int size; + unsigned int bytesused; +}; + +/* + * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream * + */ +#define to_hva_stream(vb) \ + container_of(vb, struct hva_stream, vbuf) + +struct hva_dev; +struct hva_enc; + +/** + * struct hva_ctx - context of hva instance + * + * @hva_dev: the device that this instance is associated with + * @fh: V4L2 file handle + * @ctrl_handler: V4L2 controls handler + * @ctrls: hva controls set + * @id: instance identifier + * @aborting: true if current job aborted + * @name: instance name (debug purpose) + * @run_work: encode work + * @lock: mutex used to lock access of this context + * @flags: validity of streaminfo and frameinfo fields + * @frame_num: frame number + * @stream_num: stream number + * @max_stream_size: maximum size in bytes required for stream data + * @colorspace: colorspace identifier + * @xfer_func: transfer function identifier + * @ycbcr_enc: Y'CbCr encoding identifier + * @quantization: quantization identifier + * @streaminfo: stream properties + * @frameinfo: frame properties + * @enc: current encoder + * @priv: private codec data for this instance, allocated + * by encoder @open time + * @hw_err: true if hardware error detected + */ +struct hva_ctx { + struct hva_dev *hva_dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct hva_controls ctrls; + u8 id; + bool aborting; + char name[100]; + struct work_struct run_work; + /* mutex protecting this data structure */ + struct mutex lock; + u32 flags; + u32 frame_num; + u32 stream_num; + u32 max_stream_size; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + struct hva_streaminfo streaminfo; + struct hva_frameinfo frameinfo; + struct hva_enc *enc; + void *priv; + bool hw_err; +}; + +#define HVA_FLAG_STREAMINFO 0x0001 +#define HVA_FLAG_FRAMEINFO 0x0002 + +#define HVA_MAX_INSTANCES 16 +#define HVA_MAX_ENCODERS 10 +#define HVA_MAX_FORMATS HVA_MAX_ENCODERS + +/** + * struct hva_dev - abstraction for hva entity + * + * @v4l2_dev: V4L2 device + * @vdev: video device + * @pdev: platform device + * @dev: device + * @lock: mutex used for critical sections & V4L2 ops + * serialization + * @m2m_dev: memory-to-memory V4L2 device information + * @instances: opened instances + * @nb_of_instances: number of opened instances + * @instance_id: rolling counter identifying an instance (debug purpose) + * @regs: register io memory access + * @esram_addr: esram address + * @esram_size: esram size + * @clk: hva clock + * @irq_its: status interruption + * @irq_err: error interruption + * @work_queue: work queue to handle the encode jobs + * @protect_mutex: mutex used to lock access of hardware + * @interrupt: completion interrupt + * @ip_version: IP hardware version + * @encoders: registered encoders + * @nb_of_encoders: number of registered encoders + * @pixelformats: supported uncompressed video formats + * @nb_of_pixelformats: number of supported umcompressed video formats + * @streamformats: supported compressed video formats + * @nb_of_streamformats: number of supported compressed video formats + * @sfl_reg: status fifo level register value + * @sts_reg: status register value + * @lmi_err_reg: local memory interface error register value + * @emi_err_reg: external memory interface error register value + * @hec_mif_err_reg: HEC memory interface error register value + */ +struct hva_dev { + struct v4l2_device v4l2_dev; + struct video_device *vdev; + struct platform_device *pdev; + struct device *dev; + /* mutex protecting vb2_queue structure */ + struct mutex lock; + struct v4l2_m2m_dev *m2m_dev; + struct hva_ctx *instances[HVA_MAX_INSTANCES]; + unsigned int nb_of_instances; + unsigned int instance_id; + void __iomem *regs; + u32 esram_addr; + u32 esram_size; + struct clk *clk; + int irq_its; + int irq_err; + struct workqueue_struct *work_queue; + /* mutex protecting hardware access */ + struct mutex protect_mutex; + struct completion interrupt; + unsigned long int ip_version; + const struct hva_enc *encoders[HVA_MAX_ENCODERS]; + u32 nb_of_encoders; + u32 pixelformats[HVA_MAX_FORMATS]; + u32 nb_of_pixelformats; + u32 streamformats[HVA_MAX_FORMATS]; + u32 nb_of_streamformats; + u32 sfl_reg; + u32 sts_reg; + u32 lmi_err_reg; + u32 emi_err_reg; + u32 hec_mif_err_reg; +}; + +/** + * struct hva_enc - hva encoder + * + * @name: encoder name + * @streamformat: fourcc code for compressed video format (H.264...) + * @pixelformat: fourcc code for uncompressed video format + * @max_width: maximum width of frame for this encoder + * @max_height: maximum height of frame for this encoder + * @open: open encoder + * @close: close encoder + * @encode: encode a frame (struct hva_frame) in a stream + * (struct hva_stream) + */ + +struct hva_enc { + const char *name; + u32 streamformat; + u32 pixelformat; + u32 max_width; + u32 max_height; + int (*open)(struct hva_ctx *ctx); + int (*close)(struct hva_ctx *ctx); + int (*encode)(struct hva_ctx *ctx, struct hva_frame *frame, + struct hva_stream *stream); +}; + +#endif /* HVA_H */ diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e967fcfdc1d8..44323cb5d287 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1379,7 +1379,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) cal_runtime_put(ctx->dev); } -static struct vb2_ops cal_video_qops = { +static const struct vb2_ops cal_video_qops = { .queue_setup = cal_queue_setup, .buf_prepare = cal_buffer_prepare, .buf_queue = cal_buffer_queue, diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 55a1458ac783..0189f7f7cb03 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1878,7 +1878,7 @@ static void vpe_stop_streaming(struct vb2_queue *q) vpdma_dump_regs(ctx->dev->vpdma); } -static struct vb2_ops vpe_qops = { +static const struct vb2_ops vpe_qops = { .queue_setup = vpe_queue_setup, .buf_prepare = vpe_buf_prepare, .buf_queue = vpe_buf_queue, diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index cd0ff4a66fdc..a98f679bd88d 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -815,7 +815,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q) } } -static struct vb2_ops vim2m_qops = { +static const struct vb2_ops vim2m_qops = { .queue_setup = vim2m_queue_setup, .buf_prepare = vim2m_buf_prepare, .buf_queue = vim2m_buf_queue, diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 7f937136c3f5..5464fefbaab9 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -163,38 +163,38 @@ const struct v4l2_rect vivid_max_rect = { static const u8 vivid_hdmi_edid[256] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, - 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, - 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, - 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, - 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x08, 0xe8, + 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, + 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, - 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', - '4', 'l', '2', '-', 'h', 'd', 'm', 'i', - 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x76, + 0x69, 0x76, 0x69, 0x64, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, - - 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, - 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, - 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, - 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, - 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, - 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7b, + + 0x02, 0x03, 0x3f, 0xf0, 0x51, 0x61, 0x60, 0x5f, + 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, + 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, + 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, + 0x0c, 0x00, 0x10, 0x00, 0x00, 0x78, 0x21, 0x00, + 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, + 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xea, 0xe3, + 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, + 0xd0, 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80, 0x30, + 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, + 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, 0x1f, + 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, + 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, + 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, + 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, }; static int vidioc_querycap(struct file *file, void *priv, @@ -839,6 +839,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | V4L2_CAP_READWRITE; + ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) @@ -1033,8 +1034,10 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) */ dev->cec_workqueue = alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst); - if (!dev->cec_workqueue) + if (!dev->cec_workqueue) { + ret = -ENOMEM; goto unreg_dev; + } /* start creating the vb2 queues */ if (dev->has_vid_cap) { diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index b98089c95ef5..aceb38d9f7e7 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -761,7 +761,7 @@ static const char * const vivid_ctrl_ycbcr_enc_strings[] = { "Rec. 709", "xvYCC 601", "xvYCC 709", - "sYCC", + "", "BT.2020", "BT.2020 Constant Luminance", "SMPTE 240M", @@ -773,6 +773,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_ycbcr_enc = { .id = VIVID_CID_YCBCR_ENC, .name = "Y'CbCr Encoding", .type = V4L2_CTRL_TYPE_MENU, + .menu_skip_mask = 1 << 5, .max = ARRAY_SIZE(vivid_ctrl_ycbcr_enc_strings) - 2, .qmenu = vivid_ctrl_ycbcr_enc_strings, }; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index d404a7ce33a4..d5c84ecf2027 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -823,7 +823,7 @@ int vivid_vid_cap_g_selection(struct file *file, void *priv, if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (vivid_is_webcam(dev)) - return -EINVAL; + return -ENODATA; sel->r.left = sel->r.top = 0; switch (sel->target) { @@ -872,7 +872,7 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (vivid_is_webcam(dev)) - return -EINVAL; + return -ENODATA; switch (s->target) { case V4L2_SEL_TGT_CROP: diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 06a2ec7e5ad4..b23fa879a9aa 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -53,6 +53,7 @@ struct vsp1_uds; struct vsp1_device_info { u32 version; + const char *model; unsigned int gen; unsigned int features; unsigned int rpf_count; @@ -65,6 +66,7 @@ struct vsp1_device_info { struct vsp1_device { struct device *dev; const struct vsp1_device_info *info; + u32 version; void __iomem *mmio; struct rcar_fcp_device *fcp; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 8268b87727a7..ee8355c28f94 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -142,10 +142,15 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct vsp1_bru *bru = to_bru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&bru->entity.lock); config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } bru_try_format(bru, config, fmt->pad, &fmt->format); @@ -174,7 +179,9 @@ static int bru_set_format(struct v4l2_subdev *subdev, } } - return 0; +done: + mutex_unlock(&bru->entity.lock); + return ret; } static int bru_get_selection(struct v4l2_subdev *subdev, @@ -201,7 +208,9 @@ static int bru_get_selection(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&bru->entity.lock); sel->r = *bru_get_compose(bru, config, sel->pad); + mutex_unlock(&bru->entity.lock); return 0; default: @@ -217,6 +226,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; + int ret = 0; if (sel->pad == bru->entity.source_pad) return -EINVAL; @@ -224,11 +234,16 @@ static int bru_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; + mutex_lock(&bru->entity.lock); + config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } - /* The compose rectangle top left corner must be inside the output + /* + * The compose rectangle top left corner must be inside the output * frame. */ format = vsp1_entity_get_pad_format(&bru->entity, config, @@ -246,7 +261,9 @@ static int bru_set_selection(struct v4l2_subdev *subdev, compose = bru_get_compose(bru, config, sel->pad); *compose = sel->r; - return 0; +done: + mutex_unlock(&bru->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops bru_pad_ops = { @@ -269,14 +286,15 @@ static const struct v4l2_subdev_ops bru_ops = { static void bru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_bru *bru = to_bru(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index b63d2dbe5ea3..f2fb26e5ab4e 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -148,10 +148,15 @@ static int clu_set_format(struct v4l2_subdev *subdev, struct vsp1_clu *clu = to_clu(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&clu->entity.lock); config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -164,7 +169,7 @@ static int clu_set_format(struct v4l2_subdev *subdev, if (fmt->pad == CLU_PAD_SOURCE) { /* The CLU output format can't be modified. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -182,7 +187,9 @@ static int clu_set_format(struct v4l2_subdev *subdev, CLU_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&clu->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -207,42 +214,51 @@ static const struct v4l2_subdev_ops clu_ops = { static void clu_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_clu *clu = to_clu(&entity->subdev); struct vsp1_dl_body *dlb; unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; - /* The format can't be changed during streaming, only verify it at - * stream start and store the information internally for future partial - * reconfiguration calls. - */ - if (full) { + switch (params) { + case VSP1_ENTITY_PARAMS_INIT: { + /* + * The format can't be changed during streaming, only verify it + * at setup time and store the information internally for future + * runtime configuration calls. + */ struct v4l2_mbus_framefmt *format; format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.config, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; - return; + break; } - /* 2D mode can only be used with the YCbCr pixel encoding. */ - if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) - ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D - | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D - | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + case VSP1_ENTITY_PARAMS_PARTITION: + break; + + case VSP1_ENTITY_PARAMS_RUNTIME: + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; - vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); - spin_lock_irqsave(&clu->lock, flags); - dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + spin_lock_irqsave(&clu->lock, flags); + dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); + break; + } } static const struct vsp1_entity_operations clu_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 37c3518aa2a8..ad545aff4e35 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -21,7 +21,6 @@ #include "vsp1_dl.h" #define VSP1_DL_NUM_ENTRIES 256 -#define VSP1_DL_NUM_LISTS 3 #define VSP1_DLH_INT_ENABLE (1 << 1) #define VSP1_DLH_AUTO_START (1 << 0) @@ -71,6 +70,7 @@ struct vsp1_dl_body { * @dma: DMA address for the header * @body0: first display list body * @fragments: list of extra display list bodies + * @chain: entry in the display list partition chain */ struct vsp1_dl_list { struct list_head list; @@ -81,6 +81,9 @@ struct vsp1_dl_list { struct vsp1_dl_body body0; struct list_head fragments; + + bool has_chain; + struct list_head chain; }; enum vsp1_dl_mode { @@ -262,7 +265,6 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) memset(dl->header, 0, sizeof(*dl->header)); dl->header->lists[0].addr = dl->body0.dma; - dl->header->flags = VSP1_DLH_INT_ENABLE; } return dl; @@ -293,6 +295,12 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) if (!list_empty(&dlm->free)) { dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); list_del(&dl->list); + + /* + * The display list chain must be initialised to ensure every + * display list can assert list_empty() if it is not in a chain. + */ + INIT_LIST_HEAD(&dl->chain); } spin_unlock_irqrestore(&dlm->lock, flags); @@ -303,10 +311,24 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) /* This function must be called with the display list manager lock held.*/ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) { + struct vsp1_dl_list *dl_child; + if (!dl) return; - /* We can't free fragments here as DMA memory can only be freed in + /* + * Release any linked display-lists which were chained for a single + * hardware operation. + */ + if (dl->has_chain) { + list_for_each_entry(dl_child, &dl->chain, chain) + __vsp1_dl_list_put(dl_child); + } + + dl->has_chain = false; + + /* + * We can't free fragments here as DMA memory can only be freed in * interruptible context. Move all fragments to the display list * manager's list of fragments to be freed, they will be * garbage-collected by the work queue. @@ -383,6 +405,76 @@ int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, return 0; } +/** + * vsp1_dl_list_add_chain - Add a display list to a chain + * @head: The head display list + * @dl: The new display list + * + * Add a display list to an existing display list chain. The chained lists + * will be automatically processed by the hardware without intervention from + * the CPU. A display list end interrupt will only complete after the last + * display list in the chain has completed processing. + * + * Adding a display list to a chain passes ownership of the display list to + * the head display list item. The chain is released when the head dl item is + * put back with __vsp1_dl_list_put(). + * + * Chained display lists are only usable in header mode. Attempts to add a + * display list to a chain in header-less mode will return an error. + */ +int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, + struct vsp1_dl_list *dl) +{ + /* Chained lists are only available in header mode. */ + if (head->dlm->mode != VSP1_DL_MODE_HEADER) + return -EINVAL; + + head->has_chain = true; + list_add_tail(&dl->chain, &head->chain); + return 0; +} + +static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) +{ + struct vsp1_dl_header_list *hdr = dl->header->lists; + struct vsp1_dl_body *dlb; + unsigned int num_lists = 0; + + /* + * Fill the header with the display list bodies addresses and sizes. The + * address of the first body has already been filled when the display + * list was allocated. + */ + + hdr->num_bytes = dl->body0.num_entries + * sizeof(*dl->header->lists); + + list_for_each_entry(dlb, &dl->fragments, list) { + num_lists++; + hdr++; + + hdr->addr = dlb->dma; + hdr->num_bytes = dlb->num_entries + * sizeof(*dl->header->lists); + } + + dl->header->num_lists = num_lists; + + /* + * If this display list's chain is not empty, we are on a list, where + * the next item in the list is the display list entity which should be + * automatically queued by the hardware. + */ + if (!list_empty(&dl->chain) && !is_last) { + struct vsp1_dl_list *next = list_next_entry(dl, chain); + + dl->header->next_header = next->dma; + dl->header->flags = VSP1_DLH_AUTO_START; + } else { + dl->header->flags = VSP1_DLH_INT_ENABLE; + } +} + void vsp1_dl_list_commit(struct vsp1_dl_list *dl) { struct vsp1_dl_manager *dlm = dl->dlm; @@ -393,30 +485,26 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) spin_lock_irqsave(&dlm->lock, flags); if (dl->dlm->mode == VSP1_DL_MODE_HEADER) { - struct vsp1_dl_header_list *hdr = dl->header->lists; - struct vsp1_dl_body *dlb; - unsigned int num_lists = 0; + struct vsp1_dl_list *dl_child; - /* Fill the header with the display list bodies addresses and - * sizes. The address of the first body has already been filled - * when the display list was allocated. - * + /* * In header mode the caller guarantees that the hardware is * idle at this point. */ - hdr->num_bytes = dl->body0.num_entries - * sizeof(*dl->header->lists); - list_for_each_entry(dlb, &dl->fragments, list) { - num_lists++; - hdr++; + /* Fill the header for the head and chained display lists. */ + vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); - hdr->addr = dlb->dma; - hdr->num_bytes = dlb->num_entries - * sizeof(*dl->header->lists); + list_for_each_entry(dl_child, &dl->chain, chain) { + bool last = list_is_last(&dl_child->chain, &dl->chain); + + vsp1_dl_list_fill_header(dl_child, last); } - dl->header->num_lists = num_lists; + /* + * Commit the head display list to hardware. Chained headers + * will auto-start. + */ vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); dlm->active = dl; diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index de387cd4d745..7131aa3c5978 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -41,5 +41,6 @@ void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb); void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); +int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl); #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index fe9665e57b3b..cd209dccff1b 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -276,17 +276,18 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, } dev_dbg(vsp1->dev, - "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n", + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n", __func__, rpf_index, cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], - cfg->zpos); + &cfg->mem[2], cfg->zpos); - /* Store the format, stride, memory buffer address, crop and compose + /* + * Store the format, stride, memory buffer address, crop and compose * rectangles and Z-order position and for the input. */ - fmtinfo = vsp1_get_format_info(cfg->pixelformat); + fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat); if (!fmtinfo) { dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", cfg->pixelformat); @@ -301,7 +302,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, rpf->mem.addr[0] = cfg->mem[0]; rpf->mem.addr[1] = cfg->mem[1]; - rpf->mem.addr[2] = 0; + rpf->mem.addr[2] = cfg->mem[2]; vsp1->drm->inputs[rpf_index].crop = cfg->src; vsp1->drm->inputs[rpf_index].compose = cfg->dst; @@ -492,16 +493,13 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) { - entity->ops->configure(entity, pipe, pipe->dl, true); - entity->ops->configure(entity, pipe, pipe->dl, false); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_INIT); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_RUNTIME); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_PARTITION); } - - /* The memory buffer address must be applied after configuring - * the RPF to make sure the crop offset are computed. - */ - if (entity->type == VSP1_ENTITY_RPF) - vsp1_rwpf_set_memory(to_rwpf(&entity->subdev), - pipe->dl); } vsp1_dl_list_commit(pipe->dl); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index cc316d281687..57c713a4e1df 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -60,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i)); vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); - if (status & VI6_WFP_IRQ_STA_FRE) { + if (status & VI6_WFP_IRQ_STA_DFE) { vsp1_pipeline_frame_end(wpf->pipe); ret = IRQ_HANDLED; } @@ -220,7 +220,8 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) int ret; mdev->dev = vsp1->dev; - strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); + mdev->hw_revision = vsp1->version; + strlcpy(mdev->model, vsp1->info->model, sizeof(mdev->model)); snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", dev_name(mdev->dev)); media_device_init(mdev); @@ -559,6 +560,7 @@ static const struct dev_pm_ops vsp1_pm_ops = { static const struct vsp1_device_info vsp1_device_infos[] = { { .version = VI6_IP_VERSION_MODEL_VSPS_H2, + .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, @@ -569,6 +571,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPR_H2, + .model = "VSP1-R", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -578,6 +581,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, + .model = "VSP1-D", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, .rpf_count = 4, @@ -587,6 +591,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPS_M2, + .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, @@ -596,7 +601,30 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .num_bru_inputs = 4, .uapi = true, }, { + .version = VI6_IP_VERSION_MODEL_VSPS_V2H, + .model = "VSP1V-S", + .gen = 2, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .rpf_count = 4, + .uds_count = 1, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPD_V2H, + .model = "VSP1V-D", + .gen = 2, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_LIF, + .rpf_count = 4, + .uds_count = 1, + .wpf_count = 1, + .num_bru_inputs = 4, + .uapi = true, + }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, + .model = "VSP2-I", .gen = 3, .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, @@ -606,6 +634,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, + .model = "VSP2-BD", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -614,6 +643,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, + .model = "VSP2-BC", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_WPF_VFLIP, @@ -623,6 +653,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, + .model = "VSP2-D", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -638,7 +669,6 @@ static int vsp1_probe(struct platform_device *pdev) struct resource *irq; struct resource *io; unsigned int i; - u32 version; int ret; vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); @@ -689,11 +719,11 @@ static int vsp1_probe(struct platform_device *pdev) if (ret < 0) goto done; - version = vsp1_read(vsp1, VI6_IP_VERSION); + vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); pm_runtime_put_sync(&pdev->dev); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { - if ((version & VI6_IP_VERSION_MODEL_MASK) == + if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == vsp1_device_infos[i].version) { vsp1->info = &vsp1_device_infos[i]; break; @@ -701,12 +731,13 @@ static int vsp1_probe(struct platform_device *pdev) } if (!vsp1->info) { - dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); + dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", + vsp1->version); ret = -ENXIO; goto done; } - dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); + dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version); /* Instanciate entities */ ret = vsp1_create_entities(vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 4cf6cc719c00..da673495c222 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -51,6 +51,9 @@ void vsp1_entity_route_setup(struct vsp1_entity *source, * @cfg: the TRY pad configuration * @which: configuration selector (ACTIVE or TRY) * + * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold + * the entity lock to access the returned configuration. + * * Return the pad configuration requested by the which argument. The TRY * configuration is passed explicitly to the function through the cfg argument * and simply returned when requested. The ACTIVE configuration comes from the @@ -160,7 +163,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&entity->lock); fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); + mutex_unlock(&entity->lock); return 0; } @@ -204,8 +209,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&entity->lock); format = vsp1_entity_get_pad_format(entity, config, 0); code->code = format->code; + mutex_unlock(&entity->lock); } return 0; @@ -235,6 +242,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_entity *entity = to_vsp1_entity(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(entity, cfg, fse->which); if (!config) @@ -242,8 +250,12 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(entity, config, fse->pad); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&entity->lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == 0) { fse->min_width = min_width; @@ -260,7 +272,9 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, fse->max_height = format->height; } - return 0; +done: + mutex_unlock(&entity->lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -358,6 +372,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; + mutex_init(&entity->lock); + entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index b43457fd2c43..901146f807b9 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,7 +14,7 @@ #define __VSP1_ENTITY_H__ #include <linux/list.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> @@ -35,6 +35,18 @@ enum vsp1_entity_type { VSP1_ENTITY_WPF, }; +/** + * enum vsp1_entity_params - Entity configuration parameters class + * @VSP1_ENTITY_PARAMS_INIT - Initial parameters + * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters + * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters + */ +enum vsp1_entity_params { + VSP1_ENTITY_PARAMS_INIT, + VSP1_ENTITY_PARAMS_PARTITION, + VSP1_ENTITY_PARAMS_RUNTIME, +}; + #define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */ /* @@ -63,17 +75,16 @@ struct vsp1_route { /** * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. - * @set_memory: Setup memory buffer access. This operation applies the settings - * stored in the rwpf mem field to the display list. Valid for RPF - * and WPF only. * @configure: Setup the hardware based on the entity state (pipeline, formats, * selection rectangles, ...) + * @max_width: Return the max supported width of data that the entity can + * process in a single operation. */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); - void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl); void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, bool); + struct vsp1_dl_list *, enum vsp1_entity_params); + unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); }; struct vsp1_entity { @@ -96,6 +107,8 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_subdev_pad_config *config; + + struct mutex lock; /* Protects the pad config */ }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 6e5077beb38c..94316afc54ff 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -71,10 +71,15 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&hsit->entity.lock); config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad); @@ -83,7 +88,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, * modified. */ fmt->format = *format; - return 0; + goto done; } format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 @@ -104,7 +109,9 @@ static int hsit_set_format(struct v4l2_subdev *subdev, format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; - return 0; +done: + mutex_unlock(&hsit->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { @@ -125,11 +132,12 @@ static const struct v4l2_subdev_ops hsit_ops = { static void hsit_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; if (hsit->inverse) diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index a720063f38c5..e32acae1fc6e 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -66,10 +66,15 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct vsp1_lif *lif = to_lif(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&lif->entity.lock); config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -83,7 +88,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, * format. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -101,7 +106,9 @@ static int lif_set_format(struct v4l2_subdev *subdev, LIF_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&lif->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops lif_pad_ops = { @@ -122,7 +129,8 @@ static const struct v4l2_subdev_ops lif_ops = { static void lif_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -130,7 +138,7 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index dc31de9602ba..c67cc60db0db 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -124,10 +124,15 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct vsp1_lut *lut = to_lut(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&lut->entity.lock); config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -140,7 +145,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, if (fmt->pad == LUT_PAD_SOURCE) { /* The LUT output format can't be modified. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -158,7 +163,9 @@ static int lut_set_format(struct v4l2_subdev *subdev, LUT_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&lut->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -183,24 +190,31 @@ static const struct v4l2_subdev_ops lut_ops = { static void lut_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_lut *lut = to_lut(&entity->subdev); struct vsp1_dl_body *dlb; unsigned long flags; - if (full) { + switch (params) { + case VSP1_ENTITY_PARAMS_INIT: vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - return; - } + break; - spin_lock_irqsave(&lut->lock, flags); - dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + case VSP1_ENTITY_PARAMS_PARTITION: + break; + + case VSP1_ENTITY_PARAMS_RUNTIME: + spin_lock_irqsave(&lut->lock, flags); + dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); + break; + } } static const struct vsp1_entity_operations lut_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 3e75fb3fcace..756ca4ea7668 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -136,17 +136,23 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 3, { 8, 8, 8 }, false, true, 1, 1, false }, }; -/* +/** * vsp1_get_format_info - Retrieve format information for a 4CC + * @vsp1: the VSP1 device * @fourcc: the format 4CC * * Return a pointer to the format information structure corresponding to the * given V4L2 format 4CC, or NULL if no corresponding format can be found. */ -const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) +const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, + u32 fourcc) { unsigned int i; + /* Special case, the VYUY format is supported on Gen2 only. */ + if (vsp1->info->gen != 2 && fourcc == V4L2_PIX_FMT_VYUY) + return NULL; + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { const struct vsp1_format_info *info = &vsp1_video_formats[i]; @@ -365,6 +371,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1) void vsp1_pipelines_resume(struct vsp1_device *vsp1) { + unsigned long flags; unsigned int i; /* Resume all running pipelines. */ @@ -379,7 +386,9 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1) if (pipe == NULL) continue; + spin_lock_irqsave(&pipe->irqlock, flags); if (vsp1_pipeline_ready(pipe)) vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); } } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index d20d997b1fda..ac4ad2655551 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -77,6 +77,9 @@ enum vsp1_pipeline_state { * @uds_input: entity at the input of the UDS, if the UDS is present * @entities: list of entities in the pipeline * @dl: display list associated with the pipeline + * @div_size: The maximum allowed partition size for the pipeline + * @partitions: The number of partitions used to process one frame + * @current_partition: The partition number currently being configured */ struct vsp1_pipeline { struct media_pipeline pipe; @@ -104,6 +107,11 @@ struct vsp1_pipeline { struct list_head entities; struct vsp1_dl_list *dl; + + unsigned int div_size; + unsigned int partitions; + struct v4l2_rect partition; + unsigned int current_partition; }; void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); @@ -122,6 +130,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, void vsp1_pipelines_suspend(struct vsp1_device *vsp1); void vsp1_pipelines_resume(struct vsp1_device *vsp1); -const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc); +const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, + u32 fourcc); #endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 3b03007ba625..47b1dee044fb 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -660,6 +660,8 @@ #define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8) #define VI6_IP_VERSION_MODEL_VSPD_GEN2 (0x0b << 8) #define VI6_IP_VERSION_MODEL_VSPS_M2 (0x0c << 8) +#define VI6_IP_VERSION_MODEL_VSPS_V2H (0x12 << 8) +#define VI6_IP_VERSION_MODEL_VSPD_V2H (0x13 << 8) #define VI6_IP_VERSION_MODEL_VSPI_GEN3 (0x14 << 8) #define VI6_IP_VERSION_MODEL_VSPBD_GEN3 (0x15 << 8) #define VI6_IP_VERSION_MODEL_VSPBC_GEN3 (0x16 << 8) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 388838913205..b2e34a800ffa 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -46,34 +46,22 @@ static const struct v4l2_subdev_ops rpf_ops = { * VSP1 Entity Operations */ -static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) -{ - struct vsp1_rwpf *rpf = entity_to_rwpf(entity); - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, - rpf->mem.addr[0] + rpf->offsets[0]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, - rpf->mem.addr[1] + rpf->offsets[1]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, - rpf->mem.addr[2] + rpf->offsets[1]); -} - static void rpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop; unsigned int left = 0; unsigned int top = 0; u32 pstride; u32 infmt; - if (!full) { + if (params == VSP1_ENTITY_PARAMS_RUNTIME) { vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | @@ -83,34 +71,80 @@ static void rpf_configure(struct vsp1_entity *entity, return; } - /* Source size, stride and crop offsets. - * - * The crop offsets correspond to the location of the crop rectangle top - * left corner in the plane buffer. Only two offsets are needed, as - * planes 2 and 3 always have identical strides. - */ - crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + unsigned int offsets[2]; + struct v4l2_rect crop; + + /* + * Source size and crop offsets. + * + * The crop offsets correspond to the location of the crop + * rectangle top left corner in the plane buffer. Only two + * offsets are needed, as planes 2 and 3 always have identical + * strides. + */ + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple + * slices. We must scale our partition window based on the pipe + * configuration to match the destination partition window. + * To achieve this, we adjust our crop to provide a 'sub-crop' + * matching the expected partition window. Only 'left' and + * 'width' need to be adjusted. + */ + if (pipe->partitions > 1) { + const struct v4l2_mbus_framefmt *output; + struct vsp1_entity *wpf = &pipe->output->entity; + unsigned int input_width = crop.width; + + /* + * Scale the partition window based on the configuration + * of the pipeline. + */ + output = vsp1_entity_get_pad_format(wpf, wpf->config, + RWPF_PAD_SOURCE); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, - (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | - (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, - (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | - (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + crop.width = pipe->partition.width * input_width + / output->width; + crop.left += pipe->partition.left * input_width + / output->width; + } + + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, + (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, + (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + offsets[0] = crop.top * format->plane_fmt[0].bytesperline + + crop.left * fmtinfo->bpp[0] / 8; + + if (format->num_planes > 1) + offsets[1] = crop.top * format->plane_fmt[1].bytesperline + + crop.left / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + else + offsets[1] = 0; + + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, + rpf->mem.addr[0] + offsets[0]); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, + rpf->mem.addr[1] + offsets[1]); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, + rpf->mem.addr[2] + offsets[1]); + return; + } - rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline - + crop->left * fmtinfo->bpp[0] / 8; + /* Stride */ pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; - - if (format->num_planes > 1) { - rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline - + crop->left * fmtinfo->bpp[1] / 8; + if (format->num_planes > 1) pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; - } else { - rpf->offsets[1] = 0; - } vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -215,7 +249,6 @@ static void rpf_configure(struct vsp1_entity *entity, } static const struct vsp1_entity_operations rpf_entity_ops = { - .set_memory = rpf_set_memory, .configure = rpf_configure, }; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 8d461b375e91..66e4d7ea31d6 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -66,11 +66,15 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&rwpf->entity.lock); config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -85,7 +89,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, */ format->code = fmt->format.code; fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -98,19 +102,25 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; - /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, config); - crop->left = 0; - crop->top = 0; - crop->width = fmt->format.width; - crop->height = fmt->format.height; + if (rwpf->entity.type == VSP1_ENTITY_RPF) { + struct v4l2_rect *crop; + + /* Update the sink crop rectangle. */ + crop = vsp1_rwpf_get_crop(rwpf, config); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + } /* Propagate the format to the source pad. */ format = vsp1_entity_get_pad_format(&rwpf->entity, config, RWPF_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, @@ -120,14 +130,22 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; - /* Cropping is implemented on the sink pad. */ - if (sel->pad != RWPF_PAD_SINK) + /* + * Cropping is only supported on the RPF and is implemented on the sink + * pad. + */ + if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; + mutex_lock(&rwpf->entity.lock); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -144,10 +162,13 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, @@ -158,21 +179,27 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + int ret = 0; - /* Cropping is implemented on the sink pad. */ - if (sel->pad != RWPF_PAD_SINK) + /* + * Cropping is only supported on the RPF and is implemented on the sink + * pad. + */ + if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; + mutex_lock(&rwpf->entity.lock); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } - /* Make sure the crop rectangle is entirely contained in the image. The - * WPF top and left offsets are limited to 255. - */ + /* Make sure the crop rectangle is entirely contained in the image. */ format = vsp1_entity_get_pad_format(&rwpf->entity, config, RWPF_PAD_SINK); @@ -188,10 +215,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); - if (rwpf->entity.type == VSP1_ENTITY_WPF) { - sel->r.left = min_t(unsigned int, sel->r.left, 255); - sel->r.top = min_t(unsigned int, sel->r.top, 255); - } sel->r.width = min_t(unsigned int, sel->r.width, format->width - sel->r.left); sel->r.height = min_t(unsigned int, sel->r.height, @@ -206,7 +229,9 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format->width = crop->width; format->height = crop->height; - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index cb20484e80da..1c98aff3da5d 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -61,7 +61,6 @@ struct vsp1_rwpf { unsigned int active; } flip; - unsigned int offsets[2]; struct vsp1_rwpf_memory mem; struct vsp1_dl_manager *dlm; @@ -86,17 +85,5 @@ extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *config); -/** - * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF - * @rwpf: the [RW]PF instance - * @dl: the display list - * - * This function applies the cached memory buffer address to the display list. - */ -static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, - struct vsp1_dl_list *dl) -{ - rwpf->entity.ops->set_memory(&rwpf->entity, dl); -} #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 47f5e0cea2ce..b4e568a3b4ed 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -128,6 +128,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which); if (!config) @@ -135,8 +136,12 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&sru->entity.lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == SRU_PAD_SINK) { fse->min_width = SRU_MIN_SIZE; @@ -156,7 +161,9 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, } } - return 0; +done: + mutex_unlock(&sru->entity.lock); + return ret; } static void sru_try_format(struct vsp1_sru *sru, @@ -217,10 +224,15 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&sru->entity.lock); config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } sru_try_format(sru, config, fmt->pad, &fmt->format); @@ -236,7 +248,9 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, config, SRU_PAD_SOURCE, format); } - return 0; +done: + mutex_unlock(&sru->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops sru_pad_ops = { @@ -257,7 +271,8 @@ static const struct v4l2_subdev_ops sru_ops = { static void sru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -265,7 +280,7 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, @@ -291,8 +306,27 @@ static void sru_configure(struct vsp1_entity *entity, vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2); } +static unsigned int sru_max_width(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe) +{ + struct vsp1_sru *sru = to_sru(&entity->subdev); + struct v4l2_mbus_framefmt *input; + struct v4l2_mbus_framefmt *output; + + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SINK); + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SOURCE); + + if (input->width != output->width) + return 512; + else + return 256; +} + static const struct vsp1_entity_operations sru_entity_ops = { .configure = sru_configure, + .max_width = sru_max_width, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 652dcd895022..da8f89a31ea4 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -18,6 +18,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_pipe.h" #include "vsp1_uds.h" #define UDS_MIN_SIZE 4U @@ -133,6 +134,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which); if (!config) @@ -141,8 +143,12 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(&uds->entity, config, UDS_PAD_SINK); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&uds->entity.lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == UDS_PAD_SINK) { fse->min_width = UDS_MIN_SIZE; @@ -156,7 +162,9 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, &fse->max_height); } - return 0; +done: + mutex_unlock(&uds->entity.lock); + return ret; } static void uds_try_format(struct vsp1_uds *uds, @@ -202,10 +210,15 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&uds->entity.lock); config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } uds_try_format(uds, config, fmt->pad, &fmt->format); @@ -221,7 +234,9 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, config, UDS_PAD_SOURCE, format); } - return 0; +done: + mutex_unlock(&uds->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -246,7 +261,8 @@ static const struct v4l2_subdev_ops uds_ops = { static void uds_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -255,7 +271,16 @@ static void uds_configure(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - if (!full) + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + const struct v4l2_rect *clip = &pipe->partition; + + vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, + (clip->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (clip->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + return; + } + + if (params != VSP1_ENTITY_PARAMS_INIT) return; input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, @@ -287,17 +312,39 @@ static void uds_configure(struct vsp1_entity *entity, (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); - /* Set the scaling ratios and the output size. */ + /* Set the scaling ratios. */ vsp1_uds_write(uds, dl, VI6_UDS_SCALE, (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); - vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, - (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); +} + +static unsigned int uds_max_width(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe) +{ + struct vsp1_uds *uds = to_uds(&entity->subdev); + const struct v4l2_mbus_framefmt *output; + const struct v4l2_mbus_framefmt *input; + unsigned int hscale; + + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SINK); + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SOURCE); + hscale = output->width / input->width; + + if (hscale <= 2) + return 256; + else if (hscale <= 4) + return 512; + else if (hscale <= 8) + return 1024; + else + return 2048; } static const struct vsp1_entity_operations uds_entity_ops = { .configure = uds_configure, + .max_width = uds_max_width, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 9fb4fc26a359..d351b9c768d2 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -117,9 +117,9 @@ static int __vsp1_video_try_format(struct vsp1_video *video, /* Retrieve format information and select the default format if the * requested format isn't supported. */ - info = vsp1_get_format_info(pix->pixelformat); + info = vsp1_get_format_info(video->vsp1, pix->pixelformat); if (info == NULL) - info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT); pix->pixelformat = info->fourcc; pix->colorspace = V4L2_COLORSPACE_SRGB; @@ -169,6 +169,113 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + +static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + + format = vsp1_entity_get_pad_format(&pipe->output->entity, + pipe->output->entity.config, + RWPF_PAD_SOURCE); + div_size = format->width; + + /* Gen2 hardware doesn't require image partitioning. */ + if (vsp1->info->gen == 2) { + pipe->div_size = div_size; + pipe->partitions = 1; + return; + } + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max = VSP1_VIDEO_MAX_WIDTH; + + if (entity->ops->max_width) { + entity_max = entity->ops->max_width(entity, pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->div_size = div_size; + pipe->partitions = DIV_ROUND_UP(format->width, div_size); +} + +/** + * vsp1_video_partition - Calculate the active partition output window + * + * @div_size: pre-determined maximum partition division size + * @index: partition index + * + * Returns a v4l2_rect describing the partition window. + */ +static struct v4l2_rect vsp1_video_partition(struct vsp1_pipeline *pipe, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect partition; + unsigned int modulus; + + format = vsp1_entity_get_pad_format(&pipe->output->entity, + pipe->output->entity.config, + RWPF_PAD_SOURCE); + + /* A single partition simply processes the output size in full. */ + if (pipe->partitions <= 1) { + partition.left = 0; + partition.top = 0; + partition.width = format->width; + partition.height = format->height; + return partition; + } + + /* Initialise the partition with sane starting conditions. */ + partition.left = index * div_size; + partition.top = 0; + partition.width = div_size; + partition.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevents this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + partition.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + partition.width = (div_size / 2) + modulus; + partition.left -= div_size / 2; + } + } else if (index == partitions) { + partition.width = modulus; + } + } + + return partition; +} + +/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -234,44 +341,81 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, { struct vsp1_video *video = rwpf->video; struct vsp1_vb2_buffer *buf; - unsigned long flags; buf = vsp1_video_complete_buffer(video); if (buf == NULL) return; - spin_lock_irqsave(&pipe->irqlock, flags); - video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; +} - spin_unlock_irqrestore(&pipe->irqlock, flags); +static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) +{ + struct vsp1_entity *entity; + + pipe->partition = vsp1_video_partition(pipe, pipe->div_size, + pipe->current_partition); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->ops->configure) + entity->ops->configure(entity, pipe, dl, + VSP1_ENTITY_PARAMS_PARTITION); + } } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; - unsigned int i; if (!pipe->dl) pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + /* + * Start with the runtime parameters as the configure operation can + * compute/cache information needed when configuring partitions. This + * is the case with flipping in the WPF. + */ list_for_each_entry(entity, &pipe->entities, list_pipe) { if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, false); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_RUNTIME); } - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *rwpf = pipe->inputs[i]; + /* Run the first partition */ + pipe->current_partition = 0; + vsp1_video_pipeline_run_partition(pipe, pipe->dl); - if (rwpf) - vsp1_rwpf_set_memory(rwpf, pipe->dl); - } + /* Process consecutive partitions as necessary */ + for (pipe->current_partition = 1; + pipe->current_partition < pipe->partitions; + pipe->current_partition++) { + struct vsp1_dl_list *dl; - if (!pipe->lif) - vsp1_rwpf_set_memory(pipe->output, pipe->dl); + /* + * Partition configuration operations will utilise + * the pipe->current_partition variable to determine + * the work they should complete. + */ + dl = vsp1_dl_list_get(pipe->output->dlm); + + /* + * An incomplete chain will still function, but output only + * the partitions that had a dl available. The frame end + * interrupt will be marked on the last dl in the chain. + */ + if (!dl) { + dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); + break; + } + + vsp1_video_pipeline_run_partition(pipe, dl); + vsp1_dl_list_add_chain(pipe->dl, dl); + } + /* Complete, and commit the head display list. */ vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; @@ -285,6 +429,8 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) unsigned long flags; unsigned int i; + spin_lock_irqsave(&pipe->irqlock, flags); + /* Complete buffers on all video nodes. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { if (!pipe->inputs[i]) @@ -295,8 +441,6 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) vsp1_video_frame_end(pipe, pipe->output); - spin_lock_irqsave(&pipe->irqlock, flags); - state = pipe->state; pipe->state = VSP1_PIPELINE_STOPPED; @@ -607,6 +751,9 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; + /* Determine this pipelines sizes for image partitioning support. */ + vsp1_video_pipeline_setup_partitions(pipe); + /* Prepare the display list. */ pipe->dl = vsp1_dl_list_get(pipe->output->dlm); if (!pipe->dl) @@ -634,7 +781,8 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, true); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_INIT); } return 0; @@ -675,6 +823,14 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; + /* + * Clear the buffers ready flag to make sure the device won't be started + * by a QBUF on the video node on the other side of the pipeline. + */ + spin_lock_irqsave(&video->irqlock, flags); + pipe->buffers_ready &= ~(1 << video->pipe_index); + spin_unlock_irqrestore(&video->irqlock, flags); + mutex_lock(&pipe->lock); if (--pipe->stream_count == pipe->num_inputs) { /* Stop the pipeline. */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 31983169c24a..7c48f81cd5c1 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -173,58 +173,28 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } -static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) -{ - struct vsp1_rwpf *wpf = entity_to_rwpf(entity); - const struct v4l2_pix_format_mplane *format = &wpf->format; - struct vsp1_rwpf_memory mem = wpf->mem; - unsigned int flip = wpf->flip.active; - unsigned int offset; - - /* Update the memory offsets based on flipping configuration. The - * destination addresses point to the locations where the VSP starts - * writing to memory, which can be different corners of the image - * depending on vertical flipping. Horizontal flipping is handled - * through a line buffer and doesn't modify the start address. - */ - if (flip & BIT(WPF_CTRL_VFLIP)) { - mem.addr[0] += (format->height - 1) - * format->plane_fmt[0].bytesperline; - - if (format->num_planes > 1) { - offset = (format->height / wpf->fmtinfo->vsub - 1) - * format->plane_fmt[1].bytesperline; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - } - - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); -} - static void wpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop; unsigned int i; u32 outfmt = 0; u32 srcrpf = 0; - if (!full) { + if (params == VSP1_ENTITY_PARAMS_RUNTIME) { const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); + unsigned long flags; - spin_lock(&wpf->flip.lock); + spin_lock_irqsave(&wpf->flip.lock, flags); wpf->flip.active = (wpf->flip.active & ~mask) | (wpf->flip.pending & mask); - spin_unlock(&wpf->flip.lock); + spin_unlock_irqrestore(&wpf->flip.lock, flags); outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; @@ -237,17 +207,6 @@ static void wpf_configure(struct vsp1_entity *entity, return; } - /* Cropping */ - crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); - - vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | - (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | - (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); - vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | - (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | - (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - - /* Format */ sink_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SINK); @@ -255,6 +214,80 @@ static void wpf_configure(struct vsp1_entity *entity, wpf->entity.config, RWPF_PAD_SOURCE); + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + const struct v4l2_pix_format_mplane *format = &wpf->format; + struct vsp1_rwpf_memory mem = wpf->mem; + unsigned int flip = wpf->flip.active; + unsigned int width = source_format->width; + unsigned int height = source_format->height; + unsigned int offset; + + /* + * Cropping. The partition algorithm can split the image into + * multiple slices. + */ + if (pipe->partitions > 1) + width = pipe->partition.width; + + vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); + + if (pipe->lif) + return; + + /* + * Update the memory offsets based on flipping configuration. + * The destination addresses point to the locations where the + * VSP starts writing to memory, which can be different corners + * of the image depending on vertical flipping. + */ + if (pipe->partitions > 1) { + const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; + + /* + * Horizontal flipping is handled through a line buffer + * and doesn't modify the start address, but still needs + * to be handled when image partitioning is in effect to + * order the partitions correctly. + */ + if (flip & BIT(WPF_CTRL_HFLIP)) + offset = format->width - pipe->partition.left + - pipe->partition.width; + else + offset = pipe->partition.left; + + mem.addr[0] += offset * fmtinfo->bpp[0] / 8; + if (format->num_planes > 1) { + mem.addr[1] += offset / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + mem.addr[2] += offset / fmtinfo->hsub + * fmtinfo->bpp[2] / 8; + } + } + + if (flip & BIT(WPF_CTRL_VFLIP)) { + mem.addr[0] += (format->height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (format->height / wpf->fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } + + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); + return; + } + + /* Format */ if (!pipe->lif) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -318,12 +351,11 @@ static void wpf_configure(struct vsp1_entity *entity, /* Enable interrupts */ vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), - VI6_WFP_IRQ_ENB_FREE); + VI6_WFP_IRQ_ENB_DFEE); } static const struct vsp1_entity_operations wpf_entity_ops = { .destroy = vsp1_wpf_destroy, - .set_memory = wpf_set_memory, .configure = wpf_configure, }; @@ -360,7 +392,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) return ERR_PTR(ret); /* Initialize the display list manager. */ - wpf->dlm = vsp1_dlm_create(vsp1, index, 4); + wpf->dlm = vsp1_dlm_create(vsp1, index, 64); if (!wpf->dlm) { ret = -ENOMEM; goto error; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 7ae1a134b1ff..1d5836c3fb7a 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -474,7 +474,7 @@ static void xvip_dma_stop_streaming(struct vb2_queue *vq) spin_unlock_irq(&dma->queued_lock); } -static struct vb2_ops xvip_dma_queue_qops = { +static const struct vb2_ops xvip_dma_queue_qops = { .queue_setup = xvip_dma_queue_setup, .buf_prepare = xvip_dma_buffer_prepare, .buf_queue = xvip_dma_buffer_queue, diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 471d6a8ae8a4..ee0470a3196b 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -509,7 +509,6 @@ static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); static struct i2c_driver si470x_i2c_driver = { .driver = { .name = "si470x", - .owner = THIS_MODULE, #ifdef CONFIG_PM_SLEEP .pm = &si470x_i2c_pm, #endif diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 5146be2a1a50..e5e5a1672bdb 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -402,7 +402,7 @@ static u32 si4713_functionality(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm si4713_algo = { +static const struct i2c_algorithm si4713_algo = { .master_xfer = si4713_transfer, .functionality = si4713_functionality, }; diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index e0c531fa01da..5cf983be07a2 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -203,7 +203,8 @@ static int igorplugusb_probe(struct usb_interface *intf, * This device can only store 36 pulses + spaces, which is not enough * for the NEC protocol and many others. */ - rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_RC6_6A_20 | + rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_NECX | + RC_BIT_NEC32 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SONY20 | RC_BIT_MCE_KBD | RC_BIT_SANYO); diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index 27a7ea8f1260..09314933ea08 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -34,19 +34,21 @@ static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols, bitrev8(addr_inv) << 16 | bitrev8(data) << 8 | bitrev8(data_inv); + request->protocol = RC_TYPE_NEC32; } else if ((addr_inv ^ addr) != 0xff) { /* Extended NEC */ /* scan encoding: AAaaDD */ request->scancode = addr << 16 | addr_inv << 8 | data; + request->protocol = RC_TYPE_NECX; } else { /* Normal NEC */ /* scan encoding: AADD */ request->scancode = addr << 8 | data; + request->protocol = RC_TYPE_NEC; } - request->protocol = RC_TYPE_NEC; return IMG_IR_SCANCODE; } @@ -109,7 +111,7 @@ static int img_ir_nec_filter(const struct rc_scancode_filter *in, * http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol */ struct img_ir_decoder img_ir_nec = { - .type = RC_BIT_NEC, + .type = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32, .control = { .decoden = 1, .code_type = IMG_IR_CODETYPE_PULSEDIST, diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index bea0d1eedee0..2a9d155548ab 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -49,6 +49,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct nec_dec *data = &dev->raw->nec; u32 scancode; + enum rc_type rc_type; u8 address, not_address, command, not_command; bool send_32bits = false; @@ -171,22 +172,25 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) * least Apple and TiVo remotes */ scancode = data->bits; IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode); + rc_type = RC_TYPE_NEC32; } else if ((address ^ not_address) != 0xff) { /* Extended NEC */ scancode = address << 16 | not_address << 8 | command; IR_dprintk(1, "NEC (Ext) scancode 0x%06x\n", scancode); + rc_type = RC_TYPE_NECX; } else { /* Normal NEC */ scancode = address << 8 | command; IR_dprintk(1, "NEC scancode 0x%04x\n", scancode); + rc_type = RC_TYPE_NEC; } if (data->is_nec_x) data->necx_repeat = true; - rc_keydown(dev, RC_TYPE_NEC, scancode, 0); + rc_keydown(dev, rc_type, scancode, 0); data->state = STATE_INACTIVE; return 0; } @@ -198,7 +202,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) } static struct ir_raw_handler nec_handler = { - .protocols = RC_BIT_NEC, + .protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32, .decode = ir_nec_decode, }; diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index e0e2edefa651..5cc54c967a80 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -248,7 +248,7 @@ again: toggle = 0; break; case 24: - protocol = RC_BIT_RC6_6A_24; + protocol = RC_TYPE_RC6_6A_24; toggle = 0; break; case 32: @@ -257,7 +257,7 @@ again: toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); scancode &= ~RC6_6A_MCE_TOGGLE_MASK; } else { - protocol = RC_BIT_RC6_6A_32; + protocol = RC_TYPE_RC6_6A_32; toggle = 0; } break; diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 00215f343819..04fedaa75612 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -769,21 +769,11 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt) rawir.pulse ? "pulse" : "space", rawir.duration); ir_raw_event_store_with_filter(nvt->rdev, &rawir); - - /* - * BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE - * indicates end of IR signal, but new data incoming. In both - * cases, it means we're ready to call ir_raw_event_handle - */ - if ((sample == BUF_PULSE_BIT) && (i + 1 < nvt->pkts)) { - nvt_dbg("Calling ir_raw_event_handle (signal end)\n"); - ir_raw_event_handle(nvt->rdev); - } } nvt->pkts = 0; - nvt_dbg("Calling ir_raw_event_handle (buffer empty)\n"); + nvt_dbg("Calling ir_raw_event_handle\n"); ir_raw_event_handle(nvt->rdev); nvt_dbg_verbose("%s done", __func__); @@ -801,8 +791,7 @@ static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt) /* copy data from hardware rx fifo into driver buffer */ static void nvt_get_rx_ir_data(struct nvt_dev *nvt) { - u8 fifocount, val; - unsigned int b_idx; + u8 fifocount; int i; /* Get count of how many bytes to read from RX FIFO */ @@ -810,21 +799,11 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt) nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount); - b_idx = nvt->pkts; - - /* This should never happen, but lets check anyway... */ - if (b_idx + fifocount > RX_BUF_LEN) { - nvt_process_rx_ir_data(nvt); - b_idx = 0; - } - /* Read fifocount bytes from CIR Sample RX FIFO register */ - for (i = 0; i < fifocount; i++) { - val = nvt_cir_reg_read(nvt, CIR_SRXFIFO); - nvt->buf[b_idx + i] = val; - } + for (i = 0; i < fifocount; i++) + nvt->buf[i] = nvt_cir_reg_read(nvt, CIR_SRXFIFO); - nvt->pkts += fifocount; + nvt->pkts = fifocount; nvt_dbg("%s: pkts now %d", __func__, nvt->pkts); nvt_process_rx_ir_data(nvt); @@ -886,6 +865,15 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) status = nvt_cir_reg_read(nvt, CIR_IRSTS); iren = nvt_cir_reg_read(nvt, CIR_IREN); + /* At least NCT6779D creates a spurious interrupt when the + * logical device is being disabled. + */ + if (status == 0xff && iren == 0xff) { + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + nvt_dbg_verbose("Spurious interrupt detected"); + return IRQ_HANDLED; + } + /* IRQ may be shared with CIR WAKE, therefore check for each * status bit whether the related interrupt source is enabled */ diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 144304c94606..205ecc602e34 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -26,6 +26,7 @@ static LIST_HEAD(ir_raw_client_list); /* Used to handle IR raw handler extensions */ static DEFINE_MUTEX(ir_raw_handler_lock); static LIST_HEAD(ir_raw_handler_list); +static DEFINE_MUTEX(available_protocols_lock); static u64 available_protocols; static int ir_raw_event_thread(void *data) @@ -234,9 +235,9 @@ u64 ir_raw_get_allowed_protocols(void) { u64 protocols; - mutex_lock(&ir_raw_handler_lock); + mutex_lock(&available_protocols_lock); protocols = available_protocols; - mutex_unlock(&ir_raw_handler_lock); + mutex_unlock(&available_protocols_lock); return protocols; } @@ -330,7 +331,9 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) if (ir_raw_handler->raw_register) list_for_each_entry(raw, &ir_raw_client_list, list) ir_raw_handler->raw_register(raw->dev); + mutex_lock(&available_protocols_lock); available_protocols |= ir_raw_handler->protocols; + mutex_unlock(&available_protocols_lock); mutex_unlock(&ir_raw_handler_lock); return 0; @@ -349,7 +352,9 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) if (ir_raw_handler->raw_unregister) ir_raw_handler->raw_unregister(raw->dev); } + mutex_lock(&available_protocols_lock); available_protocols &= ~protocols; + mutex_unlock(&available_protocols_lock); mutex_unlock(&ir_raw_handler_lock); } EXPORT_SYMBOL(ir_raw_handler_unregister); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 8e7f2929fa6f..d9c1f2ff7119 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -795,7 +795,9 @@ static const struct { { RC_BIT_UNKNOWN, "unknown", NULL }, { RC_BIT_RC5 | RC_BIT_RC5X, "rc-5", "ir-rc5-decoder" }, - { RC_BIT_NEC, "nec", "ir-nec-decoder" }, + { RC_BIT_NEC | + RC_BIT_NECX | + RC_BIT_NEC32, "nec", "ir-nec-decoder" }, { RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | @@ -1460,6 +1462,10 @@ int rc_register_device(struct rc_dev *dev) dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; + rc = input_register_device(dev->input_dev); + if (rc) + goto out_table; + /* * Default delay of 250ms is too short for some protocols, especially * since the timeout is currently set to 250ms. Increase it to 500ms, @@ -1475,11 +1481,6 @@ int rc_register_device(struct rc_dev *dev) */ dev->input_dev->rep[REP_PERIOD] = 125; - /* rc_open will be called here */ - rc = input_register_device(dev->input_dev); - if (rc) - goto out_table; - path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); dev_info(&dev->dev, "%s as %s\n", dev->input_name ?: "Unspecified device", path ?: "N/A"); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index ec8016d9b009..05ba47bc0b61 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -124,6 +124,41 @@ #define USB_RR3USB_PRODUCT_ID 0x0001 #define USB_RR3IIUSB_PRODUCT_ID 0x0005 + +/* + * The redrat3 encodes an IR signal as set of different lengths and a set + * of indices into those lengths. This sets how much two lengths must + * differ before they are considered distinct, the value is specified + * in microseconds. + * Default 5, value 0 to 127. + */ +static int length_fuzz = 5; +module_param(length_fuzz, uint, 0644); +MODULE_PARM_DESC(length_fuzz, "Length Fuzz (0-127)"); + +/* + * When receiving a continuous ir stream (for example when a user is + * holding a button down on a remote), this specifies the minimum size + * of a space when the redrat3 sends a irdata packet to the host. Specified + * in miliseconds. Default value 18ms. + * The value can be between 2 and 30 inclusive. + */ +static int minimum_pause = 18; +module_param(minimum_pause, uint, 0644); +MODULE_PARM_DESC(minimum_pause, "Minimum Pause in ms (2-30)"); + +/* + * The carrier frequency is measured during the first pulse of the IR + * signal. The larger the number of periods used To measure, the more + * accurate the result is likely to be, however some signals have short + * initial pulses, so in some case it may be necessary to reduce this value. + * Default 8, value 1 to 255. + */ +static int periods_measure_carrier = 8; +module_param(periods_measure_carrier, uint, 0644); +MODULE_PARM_DESC(periods_measure_carrier, "Number of Periods to Measure Carrier (1-255)"); + + struct redrat3_header { __be16 length; __be16 transfer_type; @@ -188,9 +223,6 @@ struct redrat3_dev { /* usb dma */ dma_addr_t dma_in; - /* rx signal timeout */ - u32 hw_timeout; - /* Is the device currently transmitting?*/ bool transmitting; @@ -372,7 +404,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) /* add a trailing space */ rawir.pulse = false; rawir.timeout = true; - rawir.duration = US_TO_NS(rr3->hw_timeout); + rawir.duration = rr3->rc->timeout; dev_dbg(dev, "storing trailing timeout with duration %d\n", rawir.duration); ir_raw_event_store_with_filter(rr3->rc, &rawir); @@ -480,7 +512,7 @@ static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutns) struct redrat3_dev *rr3 = rc_dev->priv; struct usb_device *udev = rr3->udev; struct device *dev = rr3->dev; - u32 *timeout; + __be32 *timeout; int ret; timeout = kmalloc(sizeof(*timeout), GFP_KERNEL); @@ -495,10 +527,9 @@ static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutns) dev_dbg(dev, "set ir parm timeout %d ret 0x%02x\n", be32_to_cpu(*timeout), ret); - if (ret == sizeof(*timeout)) { - rr3->hw_timeout = timeoutns / 1000; + if (ret == sizeof(*timeout)) ret = 0; - } else if (ret >= 0) + else if (ret >= 0) ret = -EIO; kfree(timeout); @@ -529,12 +560,25 @@ static void redrat3_reset(struct redrat3_dev *rr3) RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25); dev_dbg(dev, "reset returned 0x%02x\n", rc); - *val = 5; + *val = length_fuzz; rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25); dev_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc); + *val = (65536 - (minimum_pause * 2000)) / 256; + rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + RR3_IR_IO_MIN_PAUSE, 0, val, len, HZ * 25); + dev_dbg(dev, "set ir parm min pause %d rc 0x%02x\n", *val, rc); + + *val = periods_measure_carrier; + rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + RR3_IR_IO_PERIODS_MF, 0, val, len, HZ * 25); + dev_dbg(dev, "set ir parm periods measure carrier %d rc 0x%02x", *val, + rc); + *val = RR3_DRIVER_MAXLENS; rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, @@ -889,7 +933,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->allowed_protocols = RC_BIT_ALL; rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT); rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT); - rc->timeout = US_TO_NS(rr3->hw_timeout); + rc->timeout = US_TO_NS(redrat3_get_timeout(rr3)); rc->s_timeout = redrat3_set_timeout; rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; @@ -998,9 +1042,6 @@ static int redrat3_dev_probe(struct usb_interface *intf, if (retval < 0) goto error; - /* store current hardware timeout, in µs */ - rr3->hw_timeout = redrat3_get_timeout(rr3); - /* default.. will get overridden by any sends with a freq defined */ rr3->carrier = 38000; diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 815243c65bc3..4004260a7c69 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -499,7 +499,7 @@ static int streamzap_resume(struct usb_interface *intf) struct streamzap_ir *sz = usb_get_intfdata(intf); if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { - dev_err(sz->dev, "Error sumbiting urb\n"); + dev_err(sz->dev, "Error submitting urb\n"); return -EIO; } diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig new file mode 100644 index 000000000000..a21f5a39a440 --- /dev/null +++ b/drivers/media/spi/Kconfig @@ -0,0 +1,14 @@ +if VIDEO_V4L2 + +menu "SPI helper chips" + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST + +config VIDEO_GS1662 + tristate "Gennum Serializers video" + depends on SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + Enable the GS1662 driver which serializes video streams. + +endmenu + +endif diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile new file mode 100644 index 000000000000..ea64013d16cc --- /dev/null +++ b/drivers/media/spi/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_GS1662) += gs1662.o diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c new file mode 100644 index 000000000000..d76f36233f43 --- /dev/null +++ b/drivers/media/spi/gs1662.c @@ -0,0 +1,478 @@ +/* + * GS1662 device registration. + * + * Copyright (C) 2015-2016 Nexvision + * Author: Charles-Antoine Couret <charles-antoine.couret@nexvision.fr> + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/spi/spi.h> +#include <linux/platform_device.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/module.h> + +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-dv-timings.h> +#include <linux/v4l2-dv-timings.h> + +#define REG_STATUS 0x04 +#define REG_FORCE_FMT 0x06 +#define REG_LINES_PER_FRAME 0x12 +#define REG_WORDS_PER_LINE 0x13 +#define REG_WORDS_PER_ACT_LINE 0x14 +#define REG_ACT_LINES_PER_FRAME 0x15 + +#define MASK_H_LOCK 0x001 +#define MASK_V_LOCK 0x002 +#define MASK_STD_LOCK 0x004 +#define MASK_FORCE_STD 0x020 +#define MASK_STD_STATUS 0x3E0 + +#define GS_WIDTH_MIN 720 +#define GS_WIDTH_MAX 2048 +#define GS_HEIGHT_MIN 487 +#define GS_HEIGHT_MAX 1080 +#define GS_PIXELCLOCK_MIN 10519200 +#define GS_PIXELCLOCK_MAX 74250000 + +struct gs { + struct spi_device *pdev; + struct v4l2_subdev sd; + struct v4l2_dv_timings current_timings; + int enabled; +}; + +struct gs_reg_fmt { + u16 reg_value; + struct v4l2_dv_timings format; +}; + +struct gs_reg_fmt_custom { + u16 reg_value; + __u32 width; + __u32 height; + __u64 pixelclock; + __u32 interlaced; +}; + +static const struct spi_device_id gs_id[] = { + { "gs1662", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, gs_id); + +static const struct v4l2_dv_timings fmt_cap[] = { + V4L2_DV_BT_SDI_720X487I60, + V4L2_DV_BT_CEA_720X576P50, + V4L2_DV_BT_CEA_1280X720P24, + V4L2_DV_BT_CEA_1280X720P25, + V4L2_DV_BT_CEA_1280X720P30, + V4L2_DV_BT_CEA_1280X720P50, + V4L2_DV_BT_CEA_1280X720P60, + V4L2_DV_BT_CEA_1920X1080P24, + V4L2_DV_BT_CEA_1920X1080P25, + V4L2_DV_BT_CEA_1920X1080P30, + V4L2_DV_BT_CEA_1920X1080I50, + V4L2_DV_BT_CEA_1920X1080I60, +}; + +static const struct gs_reg_fmt reg_fmt[] = { + { 0x00, V4L2_DV_BT_CEA_1280X720P60 }, + { 0x01, V4L2_DV_BT_CEA_1280X720P60 }, + { 0x02, V4L2_DV_BT_CEA_1280X720P30 }, + { 0x03, V4L2_DV_BT_CEA_1280X720P30 }, + { 0x04, V4L2_DV_BT_CEA_1280X720P50 }, + { 0x05, V4L2_DV_BT_CEA_1280X720P50 }, + { 0x06, V4L2_DV_BT_CEA_1280X720P25 }, + { 0x07, V4L2_DV_BT_CEA_1280X720P25 }, + { 0x08, V4L2_DV_BT_CEA_1280X720P24 }, + { 0x09, V4L2_DV_BT_CEA_1280X720P24 }, + { 0x0A, V4L2_DV_BT_CEA_1920X1080I60 }, + { 0x0B, V4L2_DV_BT_CEA_1920X1080P30 }, + + /* Default value: keep this field before 0xC */ + { 0x14, V4L2_DV_BT_CEA_1920X1080I50 }, + { 0x0C, V4L2_DV_BT_CEA_1920X1080I50 }, + { 0x0D, V4L2_DV_BT_CEA_1920X1080P25 }, + { 0x0E, V4L2_DV_BT_CEA_1920X1080P25 }, + { 0x10, V4L2_DV_BT_CEA_1920X1080P24 }, + { 0x12, V4L2_DV_BT_CEA_1920X1080P24 }, + { 0x16, V4L2_DV_BT_SDI_720X487I60 }, + { 0x19, V4L2_DV_BT_SDI_720X487I60 }, + { 0x18, V4L2_DV_BT_CEA_720X576P50 }, + { 0x1A, V4L2_DV_BT_CEA_720X576P50 }, + + /* Implement following timings before enable it. + * Because of we don't have access to these theoretical timings yet. + * Workaround: use functions to get and set registers for these formats. + */ +#if 0 + { 0x0F, V4L2_DV_BT_XXX_1920X1080I25 }, /* SMPTE 274M */ + { 0x11, V4L2_DV_BT_XXX_1920X1080I24 }, /* SMPTE 274M */ + { 0x13, V4L2_DV_BT_XXX_1920X1080I25 }, /* SMPTE 274M */ + { 0x15, V4L2_DV_BT_XXX_1920X1035I60 }, /* SMPTE 260M */ + { 0x17, V4L2_DV_BT_SDI_720X507I60 }, /* SMPTE 125M */ + { 0x1B, V4L2_DV_BT_SDI_720X507I60 }, /* SMPTE 125M */ + { 0x1C, V4L2_DV_BT_XXX_2048X1080P25 }, /* SMPTE 428.1M */ +#endif +}; + +static const struct v4l2_dv_timings_cap gs_timings_cap = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with GCC < 4.4.6 */ + .reserved = { 0 }, + V4L2_INIT_BT_TIMINGS(GS_WIDTH_MIN, GS_WIDTH_MAX, GS_HEIGHT_MIN, + GS_HEIGHT_MAX, GS_PIXELCLOCK_MIN, + GS_PIXELCLOCK_MAX, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_SDI, + V4L2_DV_BT_CAP_PROGRESSIVE + | V4L2_DV_BT_CAP_INTERLACED) +}; + +static int gs_read_register(struct spi_device *spi, u16 addr, u16 *value) +{ + int ret; + u16 buf_addr = (0x8000 | (0x0FFF & addr)); + u16 buf_value = 0; + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_addr, + .len = 2, + .delay_usecs = 1, + }, { + .rx_buf = &buf_value, + .len = 2, + .delay_usecs = 1, + }, + }; + + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + + *value = buf_value; + + return ret; +} + +static int gs_write_register(struct spi_device *spi, u16 addr, u16 value) +{ + int ret; + u16 buf_addr = addr; + u16 buf_value = value; + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_addr, + .len = 2, + .delay_usecs = 1, + }, { + .tx_buf = &buf_value, + .len = 2, + .delay_usecs = 1, + }, + }; + + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int gs_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct spi_device *spi = v4l2_get_subdevdata(sd); + u16 val; + int ret; + + ret = gs_read_register(spi, reg->reg & 0xFFFF, &val); + reg->val = val; + reg->size = 2; + return ret; +} + +static int gs_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct spi_device *spi = v4l2_get_subdevdata(sd); + + return gs_write_register(spi, reg->reg & 0xFFFF, reg->val & 0xFFFF); +} +#endif + +static int gs_status_format(u16 status, struct v4l2_dv_timings *timings) +{ + int std = (status & MASK_STD_STATUS) >> 5; + int i; + + for (i = 0; i < ARRAY_SIZE(reg_fmt); i++) { + if (reg_fmt[i].reg_value == std) { + *timings = reg_fmt[i].format; + return 0; + } + } + + return -ERANGE; +} + +static u16 get_register_timings(struct v4l2_dv_timings *timings) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_fmt); i++) { + if (v4l2_match_dv_timings(timings, ®_fmt[i].format, 0, + false)) + return reg_fmt[i].reg_value | MASK_FORCE_STD; + } + + return 0x0; +} + +static inline struct gs *to_gs(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gs, sd); +} + +static int gs_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct gs *gs = to_gs(sd); + int reg_value; + + reg_value = get_register_timings(timings); + if (reg_value == 0x0) + return -EINVAL; + + gs->current_timings = *timings; + return 0; +} + +static int gs_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct gs *gs = to_gs(sd); + + *timings = gs->current_timings; + return 0; +} + +static int gs_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct gs *gs = to_gs(sd); + struct v4l2_dv_timings fmt; + u16 reg_value, i; + int ret; + + if (gs->enabled) + return -EBUSY; + + /* + * Check if the component detect a line, a frame or something else + * which looks like a video signal activity. + */ + for (i = 0; i < 4; i++) { + gs_read_register(gs->pdev, REG_LINES_PER_FRAME + i, ®_value); + if (reg_value) + break; + } + + /* If no register reports a video signal */ + if (i >= 4) + return -ENOLINK; + + gs_read_register(gs->pdev, REG_STATUS, ®_value); + if (!(reg_value & MASK_H_LOCK) || !(reg_value & MASK_V_LOCK)) + return -ENOLCK; + if (!(reg_value & MASK_STD_LOCK)) + return -ERANGE; + + ret = gs_status_format(reg_value, &fmt); + + if (ret < 0) + return ret; + + *timings = fmt; + return 0; +} + +static int gs_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + if (timings->index >= ARRAY_SIZE(fmt_cap)) + return -EINVAL; + + if (timings->pad != 0) + return -EINVAL; + + timings->timings = fmt_cap[timings->index]; + return 0; +} + +static int gs_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct gs *gs = to_gs(sd); + int reg_value; + + if (gs->enabled == enable) + return 0; + + gs->enabled = enable; + + if (enable) { + /* To force the specific format */ + reg_value = get_register_timings(&gs->current_timings); + return gs_write_register(gs->pdev, REG_FORCE_FMT, reg_value); + } + + /* To renable auto-detection mode */ + return gs_write_register(gs->pdev, REG_FORCE_FMT, 0x0); +} + +static int gs_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct gs *gs = to_gs(sd); + u16 reg_value, i; + int ret; + + /* + * Check if the component detect a line, a frame or something else + * which looks like a video signal activity. + */ + for (i = 0; i < 4; i++) { + ret = gs_read_register(gs->pdev, + REG_LINES_PER_FRAME + i, ®_value); + if (reg_value) + break; + if (ret) { + *status = V4L2_IN_ST_NO_POWER; + return ret; + } + } + + /* If no register reports a video signal */ + if (i >= 4) + *status |= V4L2_IN_ST_NO_SIGNAL; + + ret = gs_read_register(gs->pdev, REG_STATUS, ®_value); + if (!(reg_value & MASK_H_LOCK)) + *status |= V4L2_IN_ST_NO_H_LOCK; + if (!(reg_value & MASK_V_LOCK)) + *status |= V4L2_IN_ST_NO_V_LOCK; + if (!(reg_value & MASK_STD_LOCK)) + *status |= V4L2_IN_ST_NO_STD_LOCK; + + return ret; +} + +static int gs_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + if (cap->pad != 0) + return -EINVAL; + + *cap = gs_timings_cap; + return 0; +} + +/* V4L2 core operation handlers */ +static const struct v4l2_subdev_core_ops gs_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = gs_g_register, + .s_register = gs_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops gs_video_ops = { + .s_dv_timings = gs_s_dv_timings, + .g_dv_timings = gs_g_dv_timings, + .s_stream = gs_s_stream, + .g_input_status = gs_g_input_status, + .query_dv_timings = gs_query_dv_timings, +}; + +static const struct v4l2_subdev_pad_ops gs_pad_ops = { + .enum_dv_timings = gs_enum_dv_timings, + .dv_timings_cap = gs_dv_timings_cap, +}; + +/* V4L2 top level operation handlers */ +static const struct v4l2_subdev_ops gs_ops = { + .core = &gs_core_ops, + .video = &gs_video_ops, + .pad = &gs_pad_ops, +}; + +static int gs_probe(struct spi_device *spi) +{ + int ret; + struct gs *gs; + struct v4l2_subdev *sd; + + gs = devm_kzalloc(&spi->dev, sizeof(struct gs), GFP_KERNEL); + if (!gs) + return -ENOMEM; + + gs->pdev = spi; + sd = &gs->sd; + + spi->mode = SPI_MODE_0; + spi->irq = -1; + spi->max_speed_hz = 10000000; + spi->bits_per_word = 16; + ret = spi_setup(spi); + v4l2_spi_subdev_init(sd, spi, &gs_ops); + + gs->current_timings = reg_fmt[0].format; + gs->enabled = 0; + + /* Set H_CONFIG to SMPTE timings */ + gs_write_register(spi, 0x0, 0x300); + + return ret; +} + +static int gs_remove(struct spi_device *spi) +{ + struct v4l2_subdev *sd = spi_get_drvdata(spi); + struct gs *gs = to_gs(sd); + + v4l2_device_unregister_subdev(sd); + kfree(gs); + return 0; +} + +static struct spi_driver gs_driver = { + .driver = { + .name = "gs1662", + .owner = THIS_MODULE, + }, + + .probe = gs_probe, + .remove = gs_remove, + .id_table = gs_id, +}; + +module_spi_driver(gs_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@nexvision.fr>"); +MODULE_DESCRIPTION("Gennum GS1662 HD/SD-SDI Serializer driver"); diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 7f0b9d5940db..dfec23743afe 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -2201,7 +2201,7 @@ static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) return 0; } -static struct dvb_tuner_ops mt2063_ops = { +static const struct dvb_tuner_ops mt2063_ops = { .info = { .name = "MT2063 Silicon Tuner", .frequency_min = 45000000, diff --git a/drivers/media/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c index 9e031040c13f..52da4671b0e0 100644 --- a/drivers/media/tuners/mt20xx.c +++ b/drivers/media/tuners/mt20xx.c @@ -363,7 +363,7 @@ static int mt2032_set_params(struct dvb_frontend *fe, return ret; } -static struct dvb_tuner_ops mt2032_tuner_ops = { +static const struct dvb_tuner_ops mt2032_tuner_ops = { .set_analog_params = mt2032_set_params, .release = microtune_release, .get_frequency = microtune_get_frequency, @@ -563,7 +563,7 @@ static int mt2050_set_params(struct dvb_frontend *fe, return ret; } -static struct dvb_tuner_ops mt2050_tuner_ops = { +static const struct dvb_tuner_ops mt2050_tuner_ops = { .set_analog_params = mt2050_set_params, .release = microtune_release, .get_frequency = microtune_get_frequency, diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index f4ae04c3328a..42569c6811e6 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -794,7 +794,7 @@ static int mxl5007t_release(struct dvb_frontend *fe) /* ------------------------------------------------------------------------- */ -static struct dvb_tuner_ops mxl5007t_tuner_ops = { +static const struct dvb_tuner_ops mxl5007t_tuner_ops = { .info = { .name = "MaxLinear MxL5007T", }, diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index f8620741bb5f..2d50e8b1dce1 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -18,11 +18,12 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/delay.h> -#include <linux/videodev2.h> #include "tda18271-priv.h" #include "tda8290.h" +#include <linux/delay.h> +#include <linux/videodev2.h> + int tda18271_debug; module_param_named(debug, tda18271_debug, int, 0644); MODULE_PARM_DESC(debug, "set debug level " @@ -646,7 +647,7 @@ static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe) unsigned int i; int ret; - tda_info("tda18271: performing RF tracking filter calibration\n"); + tda_info("performing RF tracking filter calibration\n"); /* wait for die temperature stabilization */ msleep(200); @@ -692,12 +693,12 @@ static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) if (tda_fail(ret)) goto fail; - tda_info("tda18271: RF tracking filter calibration complete\n"); + tda_info("RF tracking filter calibration complete\n"); priv->cal_initialized = true; goto end; fail: - tda_info("tda18271: RF tracking filter calibration failed!\n"); + tda_info("RF tracking filter calibration failed!\n"); end: return ret; } diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h index cc80f544af34..0bcc735a0427 100644 --- a/drivers/media/tuners/tda18271-priv.h +++ b/drivers/media/tuners/tda18271-priv.h @@ -21,6 +21,8 @@ #ifndef __TDA18271_PRIV_H__ #define __TDA18271_PRIV_H__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/types.h> #include <linux/mutex.h> diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index edcb4a723aa1..5050ce9be423 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -818,7 +818,7 @@ static int tda827x_initial_sleep(struct dvb_frontend *fe) return fe->ops.tuner_ops.sleep(fe); } -static struct dvb_tuner_ops tda827xo_tuner_ops = { +static const struct dvb_tuner_ops tda827xo_tuner_ops = { .info = { .name = "Philips TDA827X", .frequency_min = 55000000, @@ -834,7 +834,7 @@ static struct dvb_tuner_ops tda827xo_tuner_ops = { .get_bandwidth = tda827x_get_bandwidth, }; -static struct dvb_tuner_ops tda827xa_tuner_ops = { +static const struct dvb_tuner_ops tda827xa_tuner_ops = { .info = { .name = "Philips TDA827XA", .frequency_min = 44000000, diff --git a/drivers/media/tuners/tea5761.c b/drivers/media/tuners/tea5761.c index bf78cb9fc52c..36b0b1e1d05b 100644 --- a/drivers/media/tuners/tea5761.c +++ b/drivers/media/tuners/tea5761.c @@ -301,7 +301,7 @@ static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static struct dvb_tuner_ops tea5761_tuner_ops = { +static const struct dvb_tuner_ops tea5761_tuner_ops = { .info = { .name = "tea5761", // Philips TEA5761HN FM Radio }, diff --git a/drivers/media/tuners/tea5767.c b/drivers/media/tuners/tea5767.c index 36e85d81acb2..d62a6d6b1f42 100644 --- a/drivers/media/tuners/tea5767.c +++ b/drivers/media/tuners/tea5767.c @@ -10,6 +10,8 @@ * from their contributions on DScaler. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> @@ -370,17 +372,18 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) { struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int rc; if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { - printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); + pr_warn("It is not a TEA5767. Received %i bytes.\n", rc); return -EINVAL; } /* If all bytes are the same then it's a TV tuner and not a tea5767 */ if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && buffer[0] == buffer[3] && buffer[0] == buffer[4]) { - printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); + pr_warn("All bytes are equal. It is not a TEA5767\n"); return -EINVAL; } @@ -390,7 +393,7 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) * Byte 5: bit 7:0 : == 0 */ if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { - printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); + pr_warn("Chip ID is not zero. It is not a TEA5767\n"); return -EINVAL; } @@ -423,7 +426,7 @@ static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) return 0; } -static struct dvb_tuner_ops tea5767_tuner_ops = { +static const struct dvb_tuner_ops tea5767_tuner_ops = { .info = { .name = "tea5767", // Philips TEA5767HN FM Radio }, diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index 8e9ce144da9a..9ba9582e7765 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -1035,7 +1035,7 @@ static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } -static struct dvb_tuner_ops simple_tuner_ops = { +static const struct dvb_tuner_ops simple_tuner_ops = { .init = simple_init, .sleep = simple_sleep, .set_analog_params = simple_set_params, diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 3c556ee306cd..8251942bcd12 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -605,7 +605,7 @@ static void airspy_stop_streaming(struct vb2_queue *vq) mutex_unlock(&s->v4l2_lock); } -static struct vb2_ops airspy_vb2_ops = { +static const struct vb2_ops airspy_vb2_ops = { .queue_setup = airspy_queue_setup, .buf_queue = airspy_buf_queue, .start_streaming = airspy_start_streaming, diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index 3d6687f0407d..1e66e7828d8f 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -344,7 +344,8 @@ int au0828_rc_register(struct au0828_dev *dev) rc->dev.parent = &dev->usbdev->dev; rc->driver_name = "au0828-input"; rc->driver_type = RC_DRIVER_IR_RAW; - rc->allowed_protocols = RC_BIT_NEC | RC_BIT_RC5; + rc->allowed_protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32 | + RC_BIT_RC5; /* all done */ err = rc_register_device(rc); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 13b8387082f2..85dd9a8e83ff 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -928,7 +928,7 @@ void au0828_stop_vbi_streaming(struct vb2_queue *vq) del_timer_sync(&dev->vbi_timeout); } -static struct vb2_ops au0828_video_qops = { +static const struct vb2_ops au0828_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 4cd5fa91612f..8263c4b0610b 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -635,7 +635,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, return vmalloc_to_page(pageptr); } -static struct snd_pcm_ops snd_cx231xx_pcm_capture = { +static const struct snd_pcm_ops snd_cx231xx_pcm_capture = { .open = snd_cx231xx_capture_open, .close = snd_cx231xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 491913778bcc..2f52d66b4dae 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -1264,7 +1264,10 @@ int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, dev->board.agc_analog_digital_select_gpio, analog_or_digital); - return status; + if (status < 0) + return status; + + return 0; } int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index c63248a18823..36bc25494319 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -486,7 +486,7 @@ struct cx231xx_board cx231xx_boards[] = { .output_mode = OUT_MODE_VIP11, .demod_xfer_mode = 0, .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */ + .agc_analog_digital_select_gpio = 0x1c, .tuner_sif_gpio = -1, .tuner_scl_gpio = -1, .tuner_sda_gpio = -1, @@ -1186,12 +1186,12 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev) */ void cx231xx_release_resources(struct cx231xx *dev) { + cx231xx_ir_exit(dev); + cx231xx_release_analog_resources(dev); cx231xx_remove_from_devlist(dev); - cx231xx_ir_exit(dev); - /* Release I2C buses */ cx231xx_dev_uninit(dev); diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 8ec05cb306d8..8b099fe1d592 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -712,6 +712,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) break; case CX231XX_BOARD_CNXT_RDE_253S: case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); break; case CX231XX_BOARD_HAUPPAUGE_EXETER: @@ -738,14 +739,21 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); break; default: break; } } - return errCode ? -EINVAL : 0; + if (errCode < 0) { + dev_err(dev->dev, "Failed to set devmode to %s: error: %i", + dev->mode == CX231XX_DIGITAL_MODE ? "digital" : "analog", + errCode); + return errCode; + } + + return 0; } EXPORT_SYMBOL_GPL(cx231xx_set_mode); @@ -799,7 +807,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - cx231xx_isocdbg("urb completition error %d.\n", urb->status); + cx231xx_isocdbg("urb completion error %d.\n", urb->status); break; } @@ -842,8 +850,11 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) case -ENOENT: case -ESHUTDOWN: return; + case -EPIPE: /* stall */ + cx231xx_isocdbg("urb completion error - device is stalled.\n"); + return; default: /* error */ - cx231xx_isocdbg("urb completition error %d.\n", urb->status); + cx231xx_isocdbg("urb completion error %d.\n", urb->status); break; } @@ -867,6 +878,7 @@ void cx231xx_uninit_isoc(struct cx231xx *dev) struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; struct urb *urb; int i; + bool broken_pipe = false; cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n"); @@ -886,12 +898,19 @@ void cx231xx_uninit_isoc(struct cx231xx *dev) transfer_buffer[i], urb->transfer_dma); } + if (urb->status == -EPIPE) { + broken_pipe = true; + } usb_free_urb(urb); dev->video_mode.isoc_ctl.urb[i] = NULL; } dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL; } + if (broken_pipe) { + cx231xx_isocdbg("Reset endpoint to recover broken pipe."); + usb_reset_endpoint(dev->udev, dev->video_mode.end_point_addr); + } kfree(dev->video_mode.isoc_ctl.urb); kfree(dev->video_mode.isoc_ctl.transfer_buffer); kfree(dma_q->p_left_data); @@ -918,6 +937,7 @@ void cx231xx_uninit_bulk(struct cx231xx *dev) struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; struct urb *urb; int i; + bool broken_pipe = false; cx231xx_isocdbg("cx231xx: called cx231xx_uninit_bulk\n"); @@ -937,12 +957,19 @@ void cx231xx_uninit_bulk(struct cx231xx *dev) transfer_buffer[i], urb->transfer_dma); } + if (urb->status == -EPIPE) { + broken_pipe = true; + } usb_free_urb(urb); dev->video_mode.bulk_ctl.urb[i] = NULL; } dev->video_mode.bulk_ctl.transfer_buffer[i] = NULL; } + if (broken_pipe) { + cx231xx_isocdbg("Reset endpoint to recover broken pipe."); + usb_reset_endpoint(dev->udev, dev->video_mode.end_point_addr); + } kfree(dev->video_mode.bulk_ctl.urb); kfree(dev->video_mode.bulk_ctl.transfer_buffer); kfree(dma_q->p_left_data); @@ -1297,15 +1324,29 @@ int cx231xx_dev_init(struct cx231xx *dev) dev->i2c_bus[2].i2c_reserve = 0; /* register I2C buses */ - cx231xx_i2c_register(&dev->i2c_bus[0]); - cx231xx_i2c_register(&dev->i2c_bus[1]); - cx231xx_i2c_register(&dev->i2c_bus[2]); + errCode = cx231xx_i2c_register(&dev->i2c_bus[0]); + if (errCode < 0) + return errCode; + errCode = cx231xx_i2c_register(&dev->i2c_bus[1]); + if (errCode < 0) + return errCode; + errCode = cx231xx_i2c_register(&dev->i2c_bus[2]); + if (errCode < 0) + return errCode; errCode = cx231xx_i2c_mux_create(dev); + if (errCode < 0) { + dev_err(dev->dev, + "%s: Failed to create I2C mux\n", __func__); + return errCode; + } + errCode = cx231xx_i2c_mux_register(dev, 0); + if (errCode < 0) + return errCode; + + errCode = cx231xx_i2c_mux_register(dev, 1); if (errCode < 0) return errCode; - cx231xx_i2c_mux_register(dev, 0); - cx231xx_i2c_mux_register(dev, 1); /* scan the real bus segments in the order of physical port numbers */ cx231xx_do_i2c_scan(dev, I2C_0); @@ -1448,14 +1489,14 @@ int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, /* set request */ if (!request) { if (direction) - ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */ + ven_req.bRequest = VRT_GET_GPIO; /* 0x9 gpio */ else - ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */ + ven_req.bRequest = VRT_SET_GPIO; /* 0x8 gpio */ } else { if (direction) - ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */ + ven_req.bRequest = VRT_GET_GPIE; /* 0xb gpie */ else - ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */ + ven_req.bRequest = VRT_SET_GPIE; /* 0xa gpie */ } /* set index value */ diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index ab2fb9fa0cd1..1417515d30eb 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -65,6 +65,7 @@ struct cx231xx_dvb { struct dmx_frontend fe_hw; struct dmx_frontend fe_mem; struct dvb_net net; + struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; }; @@ -150,18 +151,6 @@ static struct tda18271_config pv_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; -static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_XTAL, - .ref_freq_Hz = 16000000, -}; - -static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = { - .i2c_addr = 0x64, - .chip_mode = SI2165_MODE_PLL_EXT, - .ref_freq_Hz = 24000000, -}; - static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { .i2c_addr = 0x59, .qam_if_khz = 4000, @@ -586,8 +575,14 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); dvb_dmxdev_release(&dvb->dmxdev); dvb_dmx_release(&dvb->demux); - client = dvb->i2c_client_tuner; /* remove I2C tuner */ + client = dvb->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + /* remove I2C demod */ + client = dvb->i2c_client_demod; if (client) { module_put(client->dev.driver->owner); i2c_unregister_device(client); @@ -749,19 +744,38 @@ static int dvb_init(struct cx231xx *dev) break; case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + { + struct i2c_client *client; + struct i2c_board_info info; + struct si2165_platform_data si2165_pdata; - dev->dvb->frontend = dvb_attach(si2165_attach, - &hauppauge_930C_HD_1113xx_si2165_config, - demod_i2c - ); + /* attach demod */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL, + si2165_pdata.ref_freq_Hz = 16000000, - if (dev->dvb->frontend == NULL) { + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; goto out_free; } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ @@ -774,27 +788,43 @@ static int dvb_init(struct cx231xx *dev) dev->cx231xx_reset_analog_tuner = NULL; break; - + } case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: { struct i2c_client *client; struct i2c_board_info info; + struct si2165_platform_data si2165_pdata; struct si2157_config si2157_config; - memset(&info, 0, sizeof(struct i2c_board_info)); + /* attach demod */ + memset(&si2165_pdata, 0, sizeof(si2165_pdata)); + si2165_pdata.fe = &dev->dvb->frontend; + si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT, + si2165_pdata.ref_freq_Hz = 24000000, - dev->dvb->frontend = dvb_attach(si2165_attach, - &pctv_quatro_stick_1114xx_si2165_config, - demod_i2c - ); - - if (dev->dvb->frontend == NULL) { + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2165", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2165_pdata; + request_module(info.type); + client = i2c_new_device(demod_i2c, &info); + if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) { dev_err(dev->dev, "Failed to attach SI2165 front end\n"); result = -EINVAL; goto out_free; } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + memset(&info, 0, sizeof(struct i2c_board_info)); + dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 473cd3433fe5..35e9acfe63d3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -454,7 +454,7 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -static struct i2c_algorithm cx231xx_algo = { +static const struct i2c_algorithm cx231xx_algo = { .master_xfer = cx231xx_i2c_xfer, .functionality = functionality, }; @@ -608,7 +608,7 @@ struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port) case I2C_1_MUX_3: return dev->muxc->adapter[1]; default: - return NULL; + BUG(); } } EXPORT_SYMBOL_GPL(cx231xx_get_i2c_adap); diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 09e0f58f6bb7..941ceff9b268 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1222,6 +1222,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* Only process key if canary killed */ if (buf[16] != 0xff && buf[0] != 0x01) { + enum rc_type proto; dev_dbg(&d->udev->dev, "%s: key pressed %*ph\n", __func__, 4, buf + 12); @@ -1237,11 +1238,13 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* NEC */ state->rc_keycode = RC_SCANCODE_NEC(buf[12], buf[14]); + proto = RC_TYPE_NEC; } else { /* NEC extended*/ state->rc_keycode = RC_SCANCODE_NECX(buf[12] << 8 | buf[13], buf[14]); + proto = RC_TYPE_NECX; } } else { /* 32 bit NEC */ @@ -1249,8 +1252,9 @@ static int af9015_rc_query(struct dvb_usb_device *d) buf[13] << 16 | buf[14] << 8 | buf[15]); + proto = RC_TYPE_NEC32; } - rc_keydown(d->rc_dev, RC_TYPE_NEC, state->rc_keycode, 0); + rc_keydown(d->rc_dev, proto, state->rc_keycode, 0); } else { dev_dbg(&d->udev->dev, "%s: no key press\n", __func__); /* Invalidate last keypress */ @@ -1317,7 +1321,7 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) if (!rc->map_name) rc->map_name = RC_MAP_EMPTY; - rc->allowed_protos = RC_BIT_NEC; + rc->allowed_protos = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32; rc->query = af9015_rc_query; rc->interval = 500; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index ca018cd3fcd4..8961dd732522 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1828,6 +1828,7 @@ static int af9035_rc_query(struct dvb_usb_device *d) { struct usb_interface *intf = d->intf; int ret; + enum rc_type proto; u32 key; u8 buf[4]; struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, buf }; @@ -1842,19 +1843,22 @@ static int af9035_rc_query(struct dvb_usb_device *d) if ((buf[0] + buf[1]) == 0xff) { /* NEC standard 16bit */ key = RC_SCANCODE_NEC(buf[0], buf[2]); + proto = RC_TYPE_NEC; } else { /* NEC extended 24bit */ key = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], buf[2]); + proto = RC_TYPE_NECX; } } else { /* NEC full code 32bit */ key = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]); + proto = RC_TYPE_NEC32; } dev_dbg(&intf->dev, "%*ph\n", 4, buf); - rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0); + rc_keydown(d->rc_dev, proto, key, 0); return 0; @@ -1889,7 +1893,8 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) switch (tmp) { case 0: /* NEC */ default: - rc->allowed_protos = RC_BIT_NEC; + rc->allowed_protos = RC_BIT_NEC | RC_BIT_NECX | + RC_BIT_NEC32; break; case 1: /* RC6 */ rc->allowed_protos = RC_BIT_RC6_MCE; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 935dbaa80ef0..50c07fe7dacb 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -208,6 +208,7 @@ static int az6007_rc_query(struct dvb_usb_device *d) { struct az6007_device_state *st = d_to_priv(d); unsigned code; + enum rc_type proto; az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); @@ -215,19 +216,23 @@ static int az6007_rc_query(struct dvb_usb_device *d) return 0; if ((st->data[3] ^ st->data[4]) == 0xff) { - if ((st->data[1] ^ st->data[2]) == 0xff) + if ((st->data[1] ^ st->data[2]) == 0xff) { code = RC_SCANCODE_NEC(st->data[1], st->data[3]); - else + proto = RC_TYPE_NEC; + } else { code = RC_SCANCODE_NECX(st->data[1] << 8 | st->data[2], st->data[3]); + proto = RC_TYPE_NECX; + } } else { code = RC_SCANCODE_NEC32(st->data[1] << 24 | st->data[2] << 16 | st->data[3] << 8 | st->data[4]); + proto = RC_TYPE_NEC32; } - rc_keydown(d->rc_dev, RC_TYPE_NEC, code, st->data[5]); + rc_keydown(d->rc_dev, proto, code, st->data[5]); return 0; } @@ -236,7 +241,7 @@ static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { pr_debug("Getting az6007 Remote Control properties\n"); - rc->allowed_protos = RC_BIT_NEC; + rc->allowed_protos = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32; rc->query = az6007_rc_query; rc->interval = 400; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 3fbb2cd19f5e..a8e6624fbe83 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -82,8 +82,6 @@ static int dvb_usbv2_i2c_init(struct dvb_usb_device *d) ret = i2c_add_adapter(&d->i2c_adap); if (ret < 0) { d->i2c_adap.algo = NULL; - dev_err(&d->udev->dev, "%s: i2c_add_adapter() failed=%d\n", - KBUILD_MODNAME, ret); goto err; } diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 3721ee63b8fb..0e8fb89896c4 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -357,7 +357,8 @@ static void lme2510_int_response(struct urb *lme_urb) ibuf[5]); deb_info(1, "INT Key = 0x%08x", key); - rc_keydown(adap_to_d(adap)->rc_dev, RC_TYPE_NEC, key, 0); + rc_keydown(adap_to_d(adap)->rc_dev, RC_TYPE_NEC32, key, + 0); break; case 0xbb: switch (st->tuner_config) { @@ -1242,7 +1243,7 @@ static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, static int lme2510_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { - rc->allowed_protos = RC_BIT_NEC; + rc->allowed_protos = RC_BIT_NEC32; return 0; } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index 7d16252dbb71..f141dcc55cc9 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -466,7 +466,7 @@ static int mxl111sf_tuner_release(struct dvb_frontend *fe) /* ------------------------------------------------------------------------- */ -static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { +static const struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { .info = { .name = "MaxLinear MxL111SF", #if 0 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 6643762a9ff7..c583c638e468 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1631,22 +1631,27 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) goto err; if (buf[4] & 0x01) { + enum rc_type proto; + if (buf[2] == (u8) ~buf[3]) { if (buf[0] == (u8) ~buf[1]) { /* NEC standard (16 bit) */ rc_code = RC_SCANCODE_NEC(buf[0], buf[2]); + proto = RC_TYPE_NEC; } else { /* NEC extended (24 bit) */ rc_code = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], buf[2]); + proto = RC_TYPE_NECX; } } else { /* NEC full (32 bit) */ rc_code = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]); + proto = RC_TYPE_NEC32; } - rc_keydown(d->rc_dev, RC_TYPE_NEC, rc_code, 0); + rc_keydown(d->rc_dev, proto, rc_code, 0); ret = rtl28xxu_wr_reg(d, SYS_IRRC_SR, 1); if (ret) @@ -1668,7 +1673,7 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) { rc->map_name = RC_MAP_EMPTY; - rc->allowed_protos = RC_BIT_NEC; + rc->allowed_protos = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32; rc->query = rtl2831u_rc_query; rc->interval = 400; diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index f03b0b70c901..959fa09dfd92 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -20,10 +20,20 @@ config DVB_USB_DEBUG Say Y if you want to enable debugging. See modinfo dvb-usb (and the appropriate drivers) for debug levels. +config DVB_USB_DIB3000MC + tristate + depends on DVB_USB + select DVB_DIB3000MC + help + This is a module with helper functions for accessing the + DIB3000MC from USB DVB devices. It must be a separate module + in case DVB_USB is built-in and DVB_DIB3000MC is a module, + and gets selected automatically when needed. + config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB - select DVB_DIB3000MC + select DVB_USB_DIB3000MC select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help @@ -34,6 +44,7 @@ config DVB_USB_DIBUSB_MB depends on DVB_USB select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB3000MB + depends on DVB_DIB3000MC || !DVB_DIB3000MC select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by @@ -54,7 +65,7 @@ config DVB_USB_DIBUSB_MB_FAULTY config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB - select DVB_DIB3000MC + select DVB_USB_DIB3000MC select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help Support for USB2.0 DVB-T receivers based on reference designs made by @@ -72,7 +83,7 @@ config DVB_USB_DIB0700 select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT - select DVB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT + select DVB_USB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT @@ -99,7 +110,7 @@ config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - select DVB_DIB3000MC + select DVB_USB_DIB3000MC select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT help @@ -192,7 +203,7 @@ config DVB_USB_GP8PSK config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB - select DVB_DIB3000MC + select DVB_USB_DIB3000MC select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT help diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index acdd1efd4e74..2a7b5a963acf 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -16,20 +16,23 @@ obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o dvb-usb-dibusb-common-objs := dibusb-common.o +dvb-usb-dibusb-mc-common-objs := dibusb-mc-common.o +obj-$(CONFIG_DVB_USB_DIB3000MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc-common.o + dvb-usb-a800-objs := a800.o -obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o +obj-$(CONFIG_DVB_USB_A800) += dvb-usb-a800.o dvb-usb-dibusb-mb-objs := dibusb-mb.o obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o dvb-usb-dibusb-mc-objs := dibusb-mc.o -obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o +obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-mc.o dvb-usb-nova-t-usb2-objs := nova-t-usb2.o -obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o +obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-nova-t-usb2.o dvb-usb-umt-010-objs := umt-010.o -obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o +obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-umt-010.o dvb-usb-m920x-objs := m920x.o obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 26797979ebce..f3196658fb70 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -710,7 +710,6 @@ static void dib0700_rc_urb_completion(struct urb *purb) switch (d->props.rc.core.protocol) { case RC_BIT_NEC: - protocol = RC_TYPE_NEC; toggle = 0; /* NEC protocol sends repeat code as 0 0 0 FF */ @@ -728,16 +727,19 @@ static void dib0700_rc_urb_completion(struct urb *purb) poll_reply->nec.not_system << 16 | poll_reply->nec.data << 8 | poll_reply->nec.not_data); + protocol = RC_TYPE_NEC32; } else if ((poll_reply->nec.system ^ poll_reply->nec.not_system) != 0xff) { deb_data("NEC extended protocol\n"); keycode = RC_SCANCODE_NECX(poll_reply->nec.system << 8 | poll_reply->nec.not_system, poll_reply->nec.data); + protocol = RC_TYPE_NECX; } else { deb_data("NEC normal protocol\n"); keycode = RC_SCANCODE_NEC(poll_reply->nec.system, poll_reply->nec.data); + protocol = RC_TYPE_NEC; } break; diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index 9e94b17e1336..18ed3bfbb5e2 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -183,164 +183,6 @@ int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) } EXPORT_SYMBOL(dibusb_read_eeprom_byte); -#if IS_ENABLED(CONFIG_DVB_DIB3000MC) - -/* 3000MC/P stuff */ -// Config Adjacent channels Perf -cal22 -static struct dibx000_agc_config dib3000p_mt2060_agc_config = { - .band_caps = BAND_VHF | BAND_UHF, - .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), - - .agc1_max = 48497, - .agc1_min = 23593, - .agc2_max = 46531, - .agc2_min = 24904, - - .agc1_pt1 = 0x65, - .agc1_pt2 = 0x69, - - .agc1_slope1 = 0x51, - .agc1_slope2 = 0x27, - - .agc2_pt1 = 0, - .agc2_pt2 = 0x33, - - .agc2_slope1 = 0x35, - .agc2_slope2 = 0x37, -}; - -static struct dib3000mc_config stk3000p_dib3000p_config = { - &dib3000p_mt2060_agc_config, - - .max_time = 0x196, - .ln_adc_level = 0x1cc7, - - .output_mpeg2_in_188_bytes = 1, - - .agc_command1 = 1, - .agc_command2 = 1, -}; - -static struct dibx000_agc_config dib3000p_panasonic_agc_config = { - .band_caps = BAND_VHF | BAND_UHF, - .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), - - .agc1_max = 56361, - .agc1_min = 22282, - .agc2_max = 47841, - .agc2_min = 36045, - - .agc1_pt1 = 0x3b, - .agc1_pt2 = 0x6b, - - .agc1_slope1 = 0x55, - .agc1_slope2 = 0x1d, - - .agc2_pt1 = 0, - .agc2_pt2 = 0x0a, - - .agc2_slope1 = 0x95, - .agc2_slope2 = 0x1e, -}; - -static struct dib3000mc_config mod3000p_dib3000p_config = { - &dib3000p_panasonic_agc_config, - - .max_time = 0x51, - .ln_adc_level = 0x1cc7, - - .output_mpeg2_in_188_bytes = 1, - - .agc_command1 = 1, - .agc_command2 = 1, -}; - -int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) -{ - if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && - le16_to_cpu(adap->dev->udev->descriptor.idProduct) == - USB_PID_LITEON_DVB_T_WARM) { - msleep(1000); - } - - adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, - &adap->dev->i2c_adap, - DEFAULT_DIB3000P_I2C_ADDRESS, - &mod3000p_dib3000p_config); - if ((adap->fe_adap[0].fe) == NULL) - adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, - &adap->dev->i2c_adap, - DEFAULT_DIB3000MC_I2C_ADDRESS, - &mod3000p_dib3000p_config); - if ((adap->fe_adap[0].fe) != NULL) { - if (adap->priv != NULL) { - struct dibusb_state *st = adap->priv; - st->ops.pid_parse = dib3000mc_pid_parse; - st->ops.pid_ctrl = dib3000mc_pid_control; - } - return 0; - } - return -ENODEV; -} -EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); - -static struct mt2060_config stk3000p_mt2060_config = { - 0x60 -}; - -int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) -{ - struct dibusb_state *st = adap->priv; - u8 a,b; - u16 if1 = 1220; - struct i2c_adapter *tun_i2c; - - // First IF calibration for Liteon Sticks - if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && - le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_LITEON_DVB_T_WARM) { - - dibusb_read_eeprom_byte(adap->dev,0x7E,&a); - dibusb_read_eeprom_byte(adap->dev,0x7F,&b); - - if (a == 0x00) - if1 += b; - else if (a == 0x80) - if1 -= b; - else - warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); - - } else if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_DIBCOM && - le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_DIBCOM_MOD3001_WARM) { - u8 desc; - dibusb_read_eeprom_byte(adap->dev, 7, &desc); - if (desc == 2) { - a = 127; - do { - dibusb_read_eeprom_byte(adap->dev, a, &desc); - a--; - } while (a > 7 && (desc == 0xff || desc == 0x00)); - if (desc & 0x80) - if1 -= (0xff - desc); - else - if1 += desc; - } - } - - tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); - if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { - /* not found - use panasonic pll parameters */ - if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) - return -ENOMEM; - } else { - st->mt2060_present = 1; - /* set the correct parameters for the dib3000p */ - dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config); - } - return 0; -} -EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); -#endif - /* * common remote control stuff */ diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c new file mode 100644 index 000000000000..d66f56cc46a5 --- /dev/null +++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c @@ -0,0 +1,168 @@ +/* Common methods for dibusb-based-receivers. + * + * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include <linux/kconfig.h> +#include "dibusb.h" + +/* 3000MC/P stuff */ +// Config Adjacent channels Perf -cal22 +static struct dibx000_agc_config dib3000p_mt2060_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), + + .agc1_max = 48497, + .agc1_min = 23593, + .agc2_max = 46531, + .agc2_min = 24904, + + .agc1_pt1 = 0x65, + .agc1_pt2 = 0x69, + + .agc1_slope1 = 0x51, + .agc1_slope2 = 0x27, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x33, + + .agc2_slope1 = 0x35, + .agc2_slope2 = 0x37, +}; + +static struct dib3000mc_config stk3000p_dib3000p_config = { + &dib3000p_mt2060_agc_config, + + .max_time = 0x196, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, + + .agc_command1 = 1, + .agc_command2 = 1, +}; + +static struct dibx000_agc_config dib3000p_panasonic_agc_config = { + .band_caps = BAND_VHF | BAND_UHF, + .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), + + .agc1_max = 56361, + .agc1_min = 22282, + .agc2_max = 47841, + .agc2_min = 36045, + + .agc1_pt1 = 0x3b, + .agc1_pt2 = 0x6b, + + .agc1_slope1 = 0x55, + .agc1_slope2 = 0x1d, + + .agc2_pt1 = 0, + .agc2_pt2 = 0x0a, + + .agc2_slope1 = 0x95, + .agc2_slope2 = 0x1e, +}; + +static struct dib3000mc_config mod3000p_dib3000p_config = { + &dib3000p_panasonic_agc_config, + + .max_time = 0x51, + .ln_adc_level = 0x1cc7, + + .output_mpeg2_in_188_bytes = 1, + + .agc_command1 = 1, + .agc_command2 = 1, +}; + +int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == + USB_PID_LITEON_DVB_T_WARM) { + msleep(1000); + } + + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000P_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) == NULL) + adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, + &adap->dev->i2c_adap, + DEFAULT_DIB3000MC_I2C_ADDRESS, + &mod3000p_dib3000p_config); + if ((adap->fe_adap[0].fe) != NULL) { + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + st->ops.pid_parse = dib3000mc_pid_parse; + st->ops.pid_ctrl = dib3000mc_pid_control; + } + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); + +static struct mt2060_config stk3000p_mt2060_config = { + 0x60 +}; + +int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dibusb_state *st = adap->priv; + u8 a,b; + u16 if1 = 1220; + struct i2c_adapter *tun_i2c; + + // First IF calibration for Liteon Sticks + if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_LITEON_DVB_T_WARM) { + + dibusb_read_eeprom_byte(adap->dev,0x7E,&a); + dibusb_read_eeprom_byte(adap->dev,0x7F,&b); + + if (a == 0x00) + if1 += b; + else if (a == 0x80) + if1 -= b; + else + warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); + + } else if (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_DIBCOM && + le16_to_cpu(adap->dev->udev->descriptor.idProduct) == USB_PID_DIBCOM_MOD3001_WARM) { + u8 desc; + dibusb_read_eeprom_byte(adap->dev, 7, &desc); + if (desc == 2) { + a = 127; + do { + dibusb_read_eeprom_byte(adap->dev, a, &desc); + a--; + } while (a > 7 && (desc == 0xff || desc == 0x00)); + if (desc & 0x80) + if1 -= (0xff - desc); + else + if1 += desc; + } + } + + tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { + /* not found - use panasonic pll parameters */ + if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) + return -ENOMEM; + } else { + st->mt2060_present = 1; + /* set the correct parameters for the dib3000p */ + dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config); + } + return 0; +} +EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c index be633ece4194..d2a01b50af0d 100644 --- a/drivers/media/usb/dvb-usb/dtt200u.c +++ b/drivers/media/usb/dvb-usb/dtt200u.c @@ -62,18 +62,21 @@ static int dtt200u_rc_query(struct dvb_usb_device *d) dvb_usb_generic_rw(d,&cmd,1,key,5,0); if (key[0] == 1) { + enum rc_type proto = RC_TYPE_NEC; + scancode = key[1]; if ((u8) ~key[1] != key[2]) { /* Extended NEC */ scancode = scancode << 8; scancode |= key[2]; + proto = RC_TYPE_NECX; } scancode = scancode << 8; scancode |= key[3]; /* Check command checksum is ok */ if ((u8) ~key[3] == key[4]) - rc_keydown(d->rc_dev, RC_TYPE_NEC, scancode, 0); + rc_keydown(d->rc_dev, proto, scancode, 0); else rc_keyup(d->rc_dev); } else if (key[0] == 2) { diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 78f3687772bf..e11fe46a547c 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -695,7 +695,7 @@ static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, /* * register/unregister code and data */ -static struct snd_pcm_ops snd_em28xx_pcm_capture = { +static const struct snd_pcm_ops snd_em28xx_pcm_capture = { .open = snd_em28xx_capture_open, .close = snd_em28xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 1a9e1e556706..8b690ac908a4 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -855,7 +855,7 @@ static u32 functionality(struct i2c_adapter *i2c_adap) return 0; } -static struct i2c_algorithm em28xx_algo = { +static const struct i2c_algorithm em28xx_algo = { .master_xfer = em28xx_i2c_xfer, .functionality = functionality, }; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 7968695217f3..1f7fa059eb34 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1204,7 +1204,7 @@ buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&dev->slock, flags); } -static struct vb2_ops em28xx_video_qops = { +static const struct vb2_ops em28xx_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, diff --git a/drivers/media/usb/go7007/go7007-i2c.c b/drivers/media/usb/go7007/go7007-i2c.c index 55addfa855d4..c084bf794b56 100644 --- a/drivers/media/usb/go7007/go7007-i2c.c +++ b/drivers/media/usb/go7007/go7007-i2c.c @@ -191,7 +191,7 @@ static u32 go7007_functionality(struct i2c_adapter *adapter) return I2C_FUNC_SMBUS_BYTE_DATA; } -static struct i2c_algorithm go7007_algo = { +static const struct i2c_algorithm go7007_algo = { .smbus_xfer = go7007_smbus_xfer, .master_xfer = go7007_i2c_master_xfer, .functionality = go7007_functionality, diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index 14d3f8c1ce4a..ed9bcaf08d5e 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1032,7 +1032,7 @@ static u32 go7007_usb_functionality(struct i2c_adapter *adapter) return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; } -static struct i2c_algorithm go7007_usb_algo = { +static const struct i2c_algorithm go7007_usb_algo = { .master_xfer = go7007_usb_i2c_master_xfer, .functionality = go7007_usb_functionality, }; diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index af8458996d91..4eaba0c24629 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -477,7 +477,7 @@ static void go7007_stop_streaming(struct vb2_queue *q) go7007_write_addr(go, 0x3c82, 0x000d); } -static struct vb2_ops go7007_video_qops = { +static const struct vb2_ops go7007_video_qops = { .queue_setup = go7007_queue_setup, .buf_queue = go7007_buf_queue, .buf_prepare = go7007_buf_prepare, diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index d22d7d574672..070871fb1fc4 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -198,7 +198,7 @@ static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, return vmalloc_to_page(substream->runtime->dma_area + offset); } -static struct snd_pcm_ops go7007_snd_capture_ops = { +static const struct snd_pcm_ops go7007_snd_capture_ops = { .open = go7007_snd_capture_open, .close = go7007_snd_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c index 52bdb569760b..ae9a55d7bbbb 100644 --- a/drivers/media/usb/gspca/finepix.c +++ b/drivers/media/usb/gspca/finepix.c @@ -41,7 +41,6 @@ struct usb_fpix { struct gspca_dev gspca_dev; /* !! must be the first item */ struct work_struct work_struct; - struct workqueue_struct *work_thread; }; /* Delay after which claim the next frame. If the delay is too small, @@ -226,9 +225,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* Again, reset bulk in endpoint */ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - /* Start the workqueue function to do the streaming */ - dev->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(dev->work_thread, &dev->work_struct); + schedule_work(&dev->work_struct); return 0; } @@ -241,9 +238,8 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* wait for the work queue to terminate */ mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(dev->work_thread); + flush_work(&dev->work_struct); mutex_lock(&gspca_dev->usb_lock); - dev->work_thread = NULL; } /* Table of supported USB devices */ diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 5b481fa43099..ac295f04bd18 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -45,7 +45,6 @@ struct sd { const struct v4l2_pix_format *cap_mode; /* Driver stuff */ struct work_struct work_struct; - struct workqueue_struct *work_thread; u8 frame_brightness; int block_size; /* block size of camera */ int vga; /* 1 if vga cam, 0 if cif cam */ @@ -477,9 +476,7 @@ static int sd_start(struct gspca_dev *gspca_dev) return -1; } - /* Start the workqueue function to do the streaming */ - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(sd->work_thread, &sd->work_struct); + schedule_work(&sd->work_struct); return 0; } @@ -493,8 +490,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* wait for the work queue to terminate */ mutex_unlock(&gspca_dev->usb_lock); /* This waits for sq905c_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; + flush_work(&dev->work_struct); mutex_lock(&gspca_dev->usb_lock); } diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index fd1c8706d86a..d49d76ec1421 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -54,7 +54,6 @@ struct sd { u32 exposure; struct work_struct work; - struct workqueue_struct *work_thread; u32 pktsz; /* (used by pkt_scan) */ u16 npkt; @@ -2485,7 +2484,6 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->pktsz = sd->npkt = 0; sd->nchg = sd->short_mark = 0; - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); return gspca_dev->usb_err; } @@ -2569,12 +2567,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->work_thread != NULL) { - mutex_unlock(&gspca_dev->usb_lock); - destroy_workqueue(sd->work_thread); - mutex_lock(&gspca_dev->usb_lock); - sd->work_thread = NULL; - } + mutex_unlock(&gspca_dev->usb_lock); + flush_work(&sd->work); + mutex_lock(&gspca_dev->usb_lock); } static void do_autogain(struct gspca_dev *gspca_dev) @@ -2785,7 +2780,7 @@ marker_found: new_qual = QUALITY_MAX; if (new_qual != sd->quality) { sd->quality = new_qual; - queue_work(sd->work_thread, &sd->work); + schedule_work(&sd->work); } } } else { diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c index 103f6c4236b0..8860510c2f9c 100644 --- a/drivers/media/usb/gspca/vicam.c +++ b/drivers/media/usb/gspca/vicam.c @@ -47,7 +47,6 @@ MODULE_FIRMWARE(VICAM_FIRMWARE); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ struct work_struct work_struct; - struct workqueue_struct *work_thread; }; /* The vicam sensor has a resolution of 512 x 244, with I believe square @@ -278,9 +277,7 @@ static int sd_start(struct gspca_dev *gspca_dev) if (ret < 0) return ret; - /* Start the workqueue function to do the streaming */ - sd->work_thread = create_singlethread_workqueue(MODULE_NAME); - queue_work(sd->work_thread, &sd->work_struct); + schedule_work(&sd->work_struct); return 0; } @@ -294,8 +291,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* wait for the work queue to terminate */ mutex_unlock(&gspca_dev->usb_lock); /* This waits for vicam_dostream to finish */ - destroy_workqueue(dev->work_thread); - dev->work_thread = NULL; + flush_work(&dev->work_struct); mutex_lock(&gspca_dev->usb_lock); if (gspca_dev->present) diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index c2c8d12e9498..d9a525260511 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -129,7 +129,7 @@ struct hackrf_dev { struct list_head rx_buffer_list; struct list_head tx_buffer_list; spinlock_t buffer_list_lock; /* Protects buffer_list */ - unsigned sequence; /* Buffer sequence counter */ + unsigned int sequence; /* Buffer sequence counter */ unsigned int vb_full; /* vb is full and packets dropped */ unsigned int vb_empty; /* vb is empty and packets dropped */ @@ -891,7 +891,7 @@ static void hackrf_stop_streaming(struct vb2_queue *vq) mutex_unlock(&dev->v4l2_lock); } -static struct vb2_ops hackrf_vb2_ops = { +static const struct vb2_ops hackrf_vb2_ops = { .queue_setup = hackrf_queue_setup, .buf_queue = hackrf_buf_queue, .start_streaming = hackrf_start_streaming, diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index a38f58c4c6bf..9b641c4d4431 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -55,7 +55,7 @@ struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) /* Our default information for ir-kbd-i2c.c to use */ init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_BIT_RC5; + init_data->type = RC_BIT_RC5 | RC_BIT_RC6_MCE | RC_BIT_RC6_6A_32; init_data->name = "HD-PVR"; init_data->polling_interval = 405; /* ms, duplicated from Windows */ hdpvr_ir_rx_i2c_board_info.platform_data = init_data; @@ -180,7 +180,7 @@ static u32 hdpvr_functionality(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm hdpvr_algo = { +static const struct i2c_algorithm hdpvr_algo = { .master_xfer = hdpvr_transfer, .functionality = hdpvr_functionality, }; diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 367eb7e2a31d..bb3d31e2a0b5 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -897,7 +897,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) mutex_unlock(&dev->v4l2_lock); } -static struct vb2_ops msi2500_vb2_ops = { +static const struct vb2_ops msi2500_vb2_ops = { .queue_setup = msi2500_queue_setup, .buf_queue = msi2500_buf_queue, .start_streaming = msi2500_start_streaming, diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h index 60141b16d731..23473a21319c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h @@ -170,7 +170,6 @@ struct pvr2_hdw { const struct pvr2_device_desc *hdw_desc; /* Kernel worker thread handling */ - struct workqueue_struct *workqueue; struct work_struct workpoll; /* Update driver state */ /* Video spigot */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index fe20fe4f2330..1eb4f7ba2967 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2624,7 +2624,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; hdw->name[cnt1] = 0; - hdw->workqueue = create_singlethread_workqueue(hdw->name); INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", @@ -2651,11 +2650,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, del_timer_sync(&hdw->decoder_stabilization_timer); del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); - if (hdw->workqueue) { - flush_workqueue(hdw->workqueue); - destroy_workqueue(hdw->workqueue); - hdw->workqueue = NULL; - } + flush_work(&hdw->workpoll); usb_free_urb(hdw->ctl_read_urb); usb_free_urb(hdw->ctl_write_urb); kfree(hdw->ctl_read_buffer); @@ -2712,11 +2707,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) { if (!hdw) return; pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); - if (hdw->workqueue) { - flush_workqueue(hdw->workqueue); - destroy_workqueue(hdw->workqueue); - hdw->workqueue = NULL; - } + flush_work(&hdw->workpoll); del_timer_sync(&hdw->quiescent_timer); del_timer_sync(&hdw->decoder_stabilization_timer); del_timer_sync(&hdw->encoder_run_timer); @@ -4443,7 +4434,7 @@ static void pvr2_hdw_quiescent_timeout(unsigned long data) hdw->state_decoder_quiescent = !0; trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); + schedule_work(&hdw->workpoll); } @@ -4454,7 +4445,7 @@ static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data) hdw->state_decoder_ready = !0; trace_stbit("state_decoder_ready", hdw->state_decoder_ready); hdw->state_stale = !0; - queue_work(hdw->workqueue, &hdw->workpoll); + schedule_work(&hdw->workpoll); } @@ -4465,7 +4456,7 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) hdw->state_encoder_waitok = !0; trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); + schedule_work(&hdw->workpoll); } @@ -4477,7 +4468,7 @@ static void pvr2_hdw_encoder_run_timeout(unsigned long data) hdw->state_encoder_runok = !0; trace_stbit("state_encoder_runok",hdw->state_encoder_runok); hdw->state_stale = !0; - queue_work(hdw->workqueue,&hdw->workpoll); + schedule_work(&hdw->workpoll); } } @@ -4991,7 +4982,7 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) if (hdw->state_stale) return; hdw->state_stale = !0; trace_stbit("state_stale",hdw->state_stale); - queue_work(hdw->workqueue,&hdw->workpoll); + schedule_work(&hdw->workpoll); } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 14321d0a1833..6da5fb544817 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -596,7 +596,8 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ init_data->ir_codes = RC_MAP_HAUPPAUGE; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_BIT_RC5; + init_data->type = RC_BIT_RC5 | RC_BIT_RC6_MCE | + RC_BIT_RC6_6A_32; init_data->name = hdw->hdw_desc->description; /* IR Receiver */ info.addr = 0x71; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 81f788b7b242..2cc4d2b6f810 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -719,64 +719,85 @@ static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) return ret; } -static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int pvr2_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_cropcap cap; int val = 0; int ret; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) - return -EINVAL; - crop->c.left = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) - return -EINVAL; - crop->c.top = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) - return -EINVAL; - crop->c.width = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) + + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); + if (ret != 0) + return -EINVAL; + sel->r.left = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); + if (ret != 0) + return -EINVAL; + sel->r.top = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); + if (ret != 0) + return -EINVAL; + sel->r.width = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); + if (ret != 0) + return -EINVAL; + sel->r.height = val; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + ret = pvr2_hdw_get_cropcap(hdw, &cap); + sel->r = cap.defrect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + ret = pvr2_hdw_get_cropcap(hdw, &cap); + sel->r = cap.bounds; + break; + default: return -EINVAL; - crop->c.height = val; - return 0; + } + return ret; } -static int pvr2_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) +static int pvr2_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; int ret; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), - crop->c.left); + sel->r.left); if (ret != 0) return -EINVAL; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), - crop->c.top); + sel->r.top); if (ret != 0) return -EINVAL; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), - crop->c.width); + sel->r.width); if (ret != 0) return -EINVAL; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), - crop->c.height); + sel->r.height); if (ret != 0) return -EINVAL; return 0; @@ -798,8 +819,8 @@ static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { .vidioc_enumaudio = pvr2_enumaudio, .vidioc_enum_input = pvr2_enum_input, .vidioc_cropcap = pvr2_cropcap, - .vidioc_s_crop = pvr2_s_crop, - .vidioc_g_crop = pvr2_g_crop, + .vidioc_s_selection = pvr2_s_selection, + .vidioc_g_selection = pvr2_g_selection, .vidioc_g_input = pvr2_g_input, .vidioc_s_input = pvr2_s_input, .vidioc_g_frequency = pvr2_g_frequency, diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index c4454c928776..ff657644b6b3 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -707,7 +707,7 @@ static void stop_streaming(struct vb2_queue *vq) mutex_unlock(&pdev->v4l2_lock); } -static struct vb2_ops pwc_vb_queue_ops = { +static const struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, .buf_prepare = buffer_prepare, diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 9458eb0ef66f..c3a0e87066eb 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -717,7 +717,7 @@ static void buffer_queue(struct vb2_buffer *vb) static int start_streaming(struct vb2_queue *vq, unsigned int count); static void stop_streaming(struct vb2_queue *vq); -static struct vb2_ops s2255_video_qops = { +static const struct vb2_ops s2255_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, diff --git a/drivers/media/usb/stk1160/stk1160-i2c.c b/drivers/media/usb/stk1160/stk1160-i2c.c index 850cf285ada8..3f2517be02bb 100644 --- a/drivers/media/usb/stk1160/stk1160-i2c.c +++ b/drivers/media/usb/stk1160/stk1160-i2c.c @@ -235,7 +235,7 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm algo = { +static const struct i2c_algorithm algo = { .master_xfer = stk1160_i2c_xfer, .functionality = functionality, }; diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 5fab3bee8c74..a005d262392a 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -742,7 +742,7 @@ static void stop_streaming(struct vb2_queue *vq) stk1160_stop_streaming(dev); } -static struct vb2_ops stk1160_video_qops = { +static const struct vb2_ops stk1160_video_qops = { .queue_setup = queue_setup, .buf_queue = buffer_queue, .start_streaming = start_streaming, diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c index e21c7aacecb6..f16fbd1f9f51 100644 --- a/drivers/media/usb/tm6000/tm6000-alsa.c +++ b/drivers/media/usb/tm6000/tm6000-alsa.c @@ -388,7 +388,7 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, /* * operators */ -static struct snd_pcm_ops snd_tm6000_pcm_ops = { +static const struct snd_pcm_ops snd_tm6000_pcm_ops = { .open = snd_tm6000_pcm_open, .close = snd_tm6000_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 4e36e24cb3a6..4e7671a3a1e4 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -206,7 +206,7 @@ static void ttusb_dec_set_model(struct ttusb_dec *dec, static void ttusb_dec_handle_irq( struct urb *urb) { - struct ttusb_dec * dec = urb->context; + struct ttusb_dec *dec = urb->context; char *buffer = dec->irq_buffer; int retval; @@ -227,25 +227,31 @@ static void ttusb_dec_handle_irq( struct urb *urb) goto exit; } - if( (buffer[0] == 0x1) && (buffer[2] == 0x15) ) { - /* IR - Event */ - /* this is an fact a bit too simple implementation; + if ((buffer[0] == 0x1) && (buffer[2] == 0x15)) { + /* + * IR - Event + * + * this is an fact a bit too simple implementation; * the box also reports a keyrepeat signal * (with buffer[3] == 0x40) in an intervall of ~100ms. * But to handle this correctly we had to imlemenent some * kind of timer which signals a 'key up' event if no * keyrepeat signal is received for lets say 200ms. * this should/could be added later ... - * for now lets report each signal as a key down and up*/ - dprintk("%s:rc signal:%d\n", __func__, buffer[4]); - input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); - input_sync(dec->rc_input_dev); - input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); - input_sync(dec->rc_input_dev); + * for now lets report each signal as a key down and up + */ + if (buffer[4] - 1 < ARRAY_SIZE(rc_keys)) { + dprintk("%s:rc signal:%d\n", __func__, buffer[4]); + input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); + input_sync(dec->rc_input_dev); + input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); + input_sync(dec->rc_input_dev); + } } -exit: retval = usb_submit_urb(urb, GFP_ATOMIC); - if(retval) +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) printk("%s - usb_commit_urb failed with result: %d\n", __func__, retval); } diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c index 1965ff1b1f12..9db31db7d9ac 100644 --- a/drivers/media/usb/usbtv/usbtv-audio.c +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -332,7 +332,7 @@ static snd_pcm_uframes_t snd_usbtv_pointer(struct snd_pcm_substream *substream) return chip->snd_buffer_pos; } -static struct snd_pcm_ops snd_usbtv_pcm_ops = { +static const struct snd_pcm_ops snd_usbtv_pcm_ops = { .open = snd_usbtv_pcm_open, .close = snd_usbtv_pcm_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 2a089756c988..6cbe4a245c9f 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -689,7 +689,7 @@ static void usbtv_stop_streaming(struct vb2_queue *vq) usbtv_stop(usbtv); } -static struct vb2_ops usbtv_vb2_ops = { +static const struct vb2_ops usbtv_vb2_ops = { .queue_setup = usbtv_queue_setup, .buf_queue = usbtv_buf_queue, .start_streaming = usbtv_start_streaming, diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 773fefb52d7a..77edd206d345 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -177,7 +177,7 @@ static void uvc_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&queue->irqlock, flags); } -static struct vb2_ops uvc_queue_qops = { +static const struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index a4b224d92572..5bada202b2d3 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -119,13 +119,6 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return ret; } - ret = v4l2_subdev_call(sd, core, registered_async); - if (ret < 0 && ret != -ENOIOCTLCMD) { - if (notifier->unbind) - notifier->unbind(notifier, sd, asd); - return ret; - } - if (list_empty(¬ifier->waiting) && notifier->complete) return notifier->complete(notifier); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 5b808500e7e7..57cfe26a393f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -291,7 +291,7 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ - if (spi && sd == NULL) + if (!sd) spi_unregister_device(spi); return sd; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f7abfad9ad23..adc2147fcff7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -361,6 +361,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Scalable Baseline", "Scalable High", "Scalable High Intra", + "Stereo High", "Multiview High", NULL, }; diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index e6da353b39bc..8be561ab2615 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -527,6 +527,7 @@ static void determine_valid_ioctls(struct video_device *vdev) bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI; bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO; bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR; + bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; @@ -573,7 +574,7 @@ static void determine_valid_ioctls(struct video_device *vdev) if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); - if (is_vid) { + if (is_vid || is_tch) { /* video specific ioctls */ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap || ops->vidioc_enum_fmt_vid_cap_mplane || @@ -662,7 +663,7 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); } - if (is_vid || is_vbi || is_sdr) { + if (is_vid || is_vbi || is_sdr || is_tch) { /* ioctls valid for video, vbi or sdr */ SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); @@ -675,7 +676,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); } - if (is_vid || is_vbi) { + if (is_vid || is_vbi || is_tch) { /* ioctls valid for video or vbi */ if (ops->vidioc_s_std) set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); @@ -751,6 +752,10 @@ static int video_register_media_controller(struct video_device *vdev, int type) intf_type = MEDIA_INTF_T_V4L_SWRADIO; vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO; break; + case VFL_TYPE_TOUCH: + intf_type = MEDIA_INTF_T_V4L_TOUCH; + vdev->entity.function = MEDIA_ENT_F_IO_V4L; + break; case VFL_TYPE_RADIO: intf_type = MEDIA_INTF_T_V4L_RADIO; /* @@ -854,6 +859,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Use device name 'swradio' because 'sdr' was already taken. */ name_base = "swradio"; break; + case VFL_TYPE_TOUCH: + name_base = "v4l-touch"; + break; default: printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 06fa5f1b2cff..62bbed76dbbc 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -160,12 +160,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, int err; /* Check for valid input */ - if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) + if (!v4l2_dev || !sd || sd->v4l2_dev || !sd->name[0]) return -EINVAL; - /* Warn if we apparently re-register a subdev */ - WARN_ON(sd->v4l2_dev != NULL); - /* * The reason to acquire the module here is to avoid unloading * a module of sub-device which is registered to a media diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 889de0a32152..730a7c392c1d 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -306,7 +306,7 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", bt->il_vsync, bt->il_vbackporch); pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); - pr_info("%s: flags (0x%x):%s%s%s%s%s%s\n", dev_prefix, bt->flags, + pr_info("%s: flags (0x%x):%s%s%s%s%s%s%s\n", dev_prefix, bt->flags, (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? " REDUCED_BLANKING" : "", ((bt->flags & V4L2_DV_FL_REDUCED_BLANKING) && @@ -318,12 +318,15 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, (bt->flags & V4L2_DV_FL_HALF_LINE) ? " HALF_LINE" : "", (bt->flags & V4L2_DV_FL_IS_CE_VIDEO) ? - " CE_VIDEO" : ""); - pr_info("%s: standards (0x%x):%s%s%s%s\n", dev_prefix, bt->standards, + " CE_VIDEO" : "", + (bt->flags & V4L2_DV_FL_FIRST_FIELD_EXTRA_LINE) ? + " FIRST_FIELD_EXTRA_LINE" : ""); + pr_info("%s: standards (0x%x):%s%s%s%s%s\n", dev_prefix, bt->standards, (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA" : "", (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT" : "", (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", - (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); + (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : "", + (bt->standards & V4L2_DV_BT_STD_SDI) ? " SDI" : ""); } EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 51a0fa144392..c52d94c018bb 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -924,6 +924,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; + bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; @@ -932,7 +933,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (is_vid && is_rx && + if ((is_vid || is_tch) && is_rx && (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane)) return 0; break; @@ -1243,6 +1244,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_SDR_FMT_CS8: descr = "Complex S8"; break; case V4L2_SDR_FMT_CS14LE: descr = "Complex S14LE"; break; case V4L2_SDR_FMT_RU12LE: descr = "Real U12LE"; break; + case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit signed deltas"; break; + case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break; + case V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break; + case V4L2_TCH_FMT_TU08: descr = "8-bit unsigned touch data"; break; default: /* Compressed formats */ @@ -1309,13 +1314,14 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; + bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret = -EINVAL; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap)) + if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap)) break; ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg); break; @@ -1362,6 +1368,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; + bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; @@ -1392,7 +1399,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) + if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap)) break; p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); @@ -1451,6 +1458,21 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, return -EINVAL; } +static void v4l_pix_format_touch(struct v4l2_pix_format *p) +{ + /* + * The v4l2_pix_format structure contains fields that make no sense for + * touch. Set them to default values in this case. + */ + + p->field = V4L2_FIELD_NONE; + p->colorspace = V4L2_COLORSPACE_RAW; + p->flags = 0; + p->ycbcr_enc = 0; + p->quantization = 0; + p->xfer_func = 0; +} + static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1458,6 +1480,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; + bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; @@ -1469,17 +1492,19 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) + if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg); /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + if (is_tch) + v4l_pix_format_touch(&p->fmt.pix); return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane)) break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay)) @@ -1507,7 +1532,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane)) break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay)) @@ -1545,6 +1570,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; + bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; @@ -1553,7 +1579,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) + if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg); @@ -1563,7 +1589,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane)) break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay)) @@ -1591,7 +1617,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane)) break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); + CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay)) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 61d56c940f80..6bc27e7b2a33 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -76,9 +76,6 @@ static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, return &m2m_ctx->cap_q_ctx; } -/** - * v4l2_m2m_get_vq() - return vb2_queue for the given type - */ struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { @@ -92,9 +89,6 @@ struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_get_vq); -/** - * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers - */ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; @@ -113,10 +107,6 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); -/** - * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and - * return it - */ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; @@ -140,10 +130,6 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); * Scheduling handlers */ -/** - * v4l2_m2m_get_curr_priv() - return driver private data for the currently - * running instance or NULL if no instance is running - */ void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev) { unsigned long flags; @@ -188,26 +174,6 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); } -/** - * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to - * the pending job queue and add it if so. - * @m2m_ctx: m2m context assigned to the instance to be checked - * - * There are three basic requirements an instance has to meet to be able to run: - * 1) at least one source buffer has to be queued, - * 2) at least one destination buffer has to be queued, - * 3) streaming has to be on. - * - * If a queue is buffered (for example a decoder hardware ringbuffer that has - * to be drained before doing streamoff), allow scheduling without v4l2 buffers - * on that queue. - * - * There may also be additional, custom requirements. In such case the driver - * should supply a custom callback (job_ready in v4l2_m2m_ops) that should - * return 1 if the instance is ready. - * An example of the above could be an instance that requires more than one - * src/dst buffer per transaction. - */ void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) { struct v4l2_m2m_dev *m2m_dev; @@ -311,18 +277,6 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) } } -/** - * v4l2_m2m_job_finish() - inform the framework that a job has been finished - * and have it clean up - * - * Called by a driver to yield back the device after it has finished with it. - * Should be called as soon as possible after reaching a state which allows - * other instances to take control of the device. - * - * This function has to be called only after device_run() callback has been - * called on the driver. To prevent recursion, it should not be called directly - * from the device_run() callback though. - */ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, struct v4l2_m2m_ctx *m2m_ctx) { @@ -350,9 +304,6 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, } EXPORT_SYMBOL(v4l2_m2m_job_finish); -/** - * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer - */ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { @@ -370,11 +321,6 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); -/** - * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer - * - * See v4l2_m2m_mmap() documentation for details. - */ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -400,10 +346,6 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); -/** - * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on - * the type - */ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -419,10 +361,6 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); -/** - * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on - * the type - */ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -433,10 +371,6 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); -/** - * v4l2_m2m_prepare_buf() - prepare a source or destination buffer, depending on - * the type - */ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -452,10 +386,6 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf); -/** - * v4l2_m2m_create_bufs() - create a source or destination buffer, depending - * on the type - */ int v4l2_m2m_create_bufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_create_buffers *create) { @@ -466,10 +396,6 @@ int v4l2_m2m_create_bufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_create_bufs); -/** - * v4l2_m2m_expbuf() - export a source or destination buffer, depending on - * the type - */ int v4l2_m2m_expbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_exportbuffer *eb) { @@ -479,9 +405,7 @@ int v4l2_m2m_expbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, return vb2_expbuf(vq, eb); } EXPORT_SYMBOL_GPL(v4l2_m2m_expbuf); -/** - * v4l2_m2m_streamon() - turn on streaming for a video queue - */ + int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { @@ -497,9 +421,6 @@ int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); -/** - * v4l2_m2m_streamoff() - turn off streaming for a video queue - */ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) { @@ -540,14 +461,6 @@ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); -/** - * v4l2_m2m_poll() - poll replacement, for destination buffers only - * - * Call from the driver's poll() function. Will poll both queues. If a buffer - * is available to dequeue (with dqbuf) from the source queue, this will - * indicate that a non-blocking write can be performed, while read will be - * returned in case of the destination queue. - */ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct poll_table_struct *wait) { @@ -626,16 +539,6 @@ end: } EXPORT_SYMBOL_GPL(v4l2_m2m_poll); -/** - * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer - * - * Call from driver's mmap() function. Will handle mmap() for both queues - * seamlessly for videobuffer, which will receive normal per-queue offsets and - * proper videobuf queue pointers. The differentiation is made outside videobuf - * by adding a predefined offset to buffers from one of the queues and - * subtracting it before passing it back to videobuf. Only drivers (and - * thus applications) receive modified offsets. - */ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct vm_area_struct *vma) { @@ -653,11 +556,6 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_mmap); -/** - * v4l2_m2m_init() - initialize per-driver m2m data - * - * Usually called from driver's probe() function. - */ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; @@ -679,26 +577,12 @@ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) } EXPORT_SYMBOL_GPL(v4l2_m2m_init); -/** - * v4l2_m2m_release() - cleans up and frees a m2m_dev structure - * - * Usually called from driver's remove() function. - */ void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev) { kfree(m2m_dev); } EXPORT_SYMBOL_GPL(v4l2_m2m_release); -/** - * v4l2_m2m_ctx_init() - allocate and initialize a m2m context - * @priv - driver's instance private data - * @m2m_dev - a previously initialized m2m_dev struct - * @vq_init - a callback for queue type-specific initialization function to be - * used for initializing videobuf_queues - * - * Usually called from driver's open() function. - */ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, void *drv_priv, int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)) @@ -744,11 +628,6 @@ err: } EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); -/** - * v4l2_m2m_ctx_release() - release m2m context - * - * Usually called from driver's release() function. - */ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) { /* wait until the current context is dequeued from job_queue */ @@ -761,11 +640,6 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); -/** - * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list. - * - * Call from buf_queue(), videobuf_queue_ops callback. - */ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_v4l2_buffer *vbuf) { diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index ca8ffeb56d72..21900202ff83 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -198,6 +198,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; void *mem_priv; int plane; + int ret = -ENOMEM; /* * Allocate memory for all planes in this buffer @@ -209,8 +210,11 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) mem_priv = call_ptr_memop(vb, alloc, q->alloc_devs[plane] ? : q->dev, q->dma_attrs, size, dma_dir, q->gfp_flags); - if (IS_ERR_OR_NULL(mem_priv)) + if (IS_ERR(mem_priv)) { + if (mem_priv) + ret = PTR_ERR(mem_priv); goto free; + } /* Associate allocator private data with this plane */ vb->planes[plane].mem_priv = mem_priv; @@ -224,7 +228,7 @@ free: vb->planes[plane - 1].mem_priv = NULL; } - return -ENOMEM; + return ret; } /** @@ -524,10 +528,6 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) return 0; } -/** - * vb2_buffer_in_use() - return true if the buffer is in use and - * the queue cannot be freed (by the means of REQBUFS(0)) call - */ bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) { unsigned int plane; @@ -560,16 +560,6 @@ static bool __buffers_in_use(struct vb2_queue *q) return false; } -/** - * vb2_core_querybuf() - query video buffer information - * @q: videobuf queue - * @index: id number of the buffer - * @pb: buffer struct passed from userspace - * - * Should be called from vidioc_querybuf ioctl handler in driver. - * The passed buffer should have been verified. - * This function fills the relevant information for the userspace. - */ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb) { call_void_bufop(q, fill_user_buffer, q->bufs[index], pb); @@ -616,10 +606,6 @@ static int __verify_dmabuf_ops(struct vb2_queue *q) return 0; } -/** - * vb2_verify_memory_type() - Check whether the memory type and buffer type - * passed to a buffer operation are compatible with the queue. - */ int vb2_verify_memory_type(struct vb2_queue *q, enum vb2_memory memory, unsigned int type) { @@ -666,30 +652,6 @@ int vb2_verify_memory_type(struct vb2_queue *q, } EXPORT_SYMBOL(vb2_verify_memory_type); -/** - * vb2_core_reqbufs() - Initiate streaming - * @q: videobuf2 queue - * @memory: memory type - * @count: requested buffer count - * - * Should be called from vidioc_reqbufs ioctl handler of a driver. - * This function: - * 1) verifies streaming parameters passed from the userspace, - * 2) sets up the queue, - * 3) negotiates number of buffers and planes per buffer with the driver - * to be used during streaming, - * 4) allocates internal buffer structures (struct vb2_buffer), according to - * the agreed parameters, - * 5) for MMAP memory type, allocates actual video memory, using the - * memory handling/allocation routines provided during queue initialization - * - * If req->count is 0, all the memory will be freed instead. - * If the queue has been allocated previously (by a previous vb2_reqbufs) call - * and the queue is not busy, memory will be reallocated. - * - * The return values from this function are intended to be directly returned - * from vidioc_reqbufs handler in driver. - */ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count) { @@ -815,22 +777,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, } EXPORT_SYMBOL_GPL(vb2_core_reqbufs); -/** - * vb2_core_create_bufs() - Allocate buffers and any required auxiliary structs - * @q: videobuf2 queue - * @memory: memory type - * @count: requested buffer count - * @parg: parameter passed to device driver - * - * Should be called from vidioc_create_bufs ioctl handler of a driver. - * This function: - * 1) verifies parameter sanity - * 2) calls the .queue_setup() queue operation - * 3) performs any necessary memory allocations - * - * The return values from this function are intended to be directly returned - * from vidioc_create_bufs handler in driver. - */ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count, unsigned requested_planes, const unsigned requested_sizes[]) @@ -920,14 +866,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, } EXPORT_SYMBOL_GPL(vb2_core_create_bufs); -/** - * vb2_plane_vaddr() - Return a kernel virtual address of a given plane - * @vb: vb2_buffer to which the plane in question belongs to - * @plane_no: plane number for which the address is to be returned - * - * This function returns a kernel virtual address of a given plane if - * such a mapping exist, NULL otherwise. - */ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) { if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) @@ -938,17 +876,6 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) } EXPORT_SYMBOL_GPL(vb2_plane_vaddr); -/** - * vb2_plane_cookie() - Return allocator specific cookie for the given plane - * @vb: vb2_buffer to which the plane in question belongs to - * @plane_no: plane number for which the cookie is to be returned - * - * This function returns an allocator specific cookie for a given plane if - * available, NULL otherwise. The allocator should provide some simple static - * inline function, which would convert this cookie to the allocator specific - * type that can be used directly by the driver to access the buffer. This can - * be for example physical address, pointer to scatter list or IOMMU mapping. - */ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no) { if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv) @@ -958,26 +885,6 @@ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no) } EXPORT_SYMBOL_GPL(vb2_plane_cookie); -/** - * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished - * @vb: vb2_buffer returned from the driver - * @state: either VB2_BUF_STATE_DONE if the operation finished successfully, - * VB2_BUF_STATE_ERROR if the operation finished with an error or - * VB2_BUF_STATE_QUEUED if the driver wants to requeue buffers. - * If start_streaming fails then it should return buffers with state - * VB2_BUF_STATE_QUEUED to put them back into the queue. - * - * This function should be called by the driver after a hardware operation on - * a buffer is finished and the buffer may be returned to userspace. The driver - * cannot use this buffer anymore until it is queued back to it by videobuf - * by the means of buf_queue callback. Only buffers previously queued to the - * driver by buf_queue can be passed to this function. - * - * While streaming a buffer can only be returned in state DONE or ERROR. - * The start_streaming op can also return them in case the DMA engine cannot - * be started for some reason. In that case the buffers should be returned with - * state QUEUED. - */ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; @@ -1036,18 +943,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) } EXPORT_SYMBOL_GPL(vb2_buffer_done); -/** - * vb2_discard_done() - discard all buffers marked as DONE - * @q: videobuf2 queue - * - * This function is intended to be used with suspend/resume operations. It - * discards all 'done' buffers as they would be too old to be requested after - * resume. - * - * Drivers must stop the hardware and synchronize with interrupt handlers and/or - * delayed works before calling this function to make sure no buffer will be - * touched by the driver and/or hardware. - */ void vb2_discard_done(struct vb2_queue *q) { struct vb2_buffer *vb; @@ -1136,10 +1031,10 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb) q->alloc_devs[plane] ? : q->dev, planes[plane].m.userptr, planes[plane].length, dma_dir); - if (IS_ERR_OR_NULL(mem_priv)) { + if (IS_ERR(mem_priv)) { dprintk(1, "failed acquiring userspace " "memory for plane %d\n", plane); - ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL; + ret = PTR_ERR(mem_priv); goto err; } vb->planes[plane].mem_priv = mem_priv; @@ -1228,8 +1123,10 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb) planes[plane].length = dbuf->size; if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "invalid dmabuf length for plane %d\n", - plane); + dprintk(1, "invalid dmabuf length %u for plane %d, " + "minimum length %u\n", + planes[plane].length, plane, + vb->planes[plane].min_length); dma_buf_put(dbuf); ret = -EINVAL; goto err; @@ -1271,9 +1168,10 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb) vb->planes[plane].mem_priv = mem_priv; } - /* TODO: This pins the buffer(s) with dma_buf_map_attachment()).. but - * really we want to do this just before the DMA, not while queueing - * the buffer(s).. + /* + * This pins the buffer(s) with dma_buf_map_attachment()). It's done + * here instead just before the DMA, while queueing the buffer(s) so + * userspace knows sooner rather than later if the dma-buf map fails. */ for (plane = 0; plane < vb->num_planes; ++plane) { ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); @@ -1377,22 +1275,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb) return ret; } -/** - * vb2_core_prepare_buf() - Pass ownership of a buffer from userspace - * to the kernel - * @q: videobuf2 queue - * @index: id number of the buffer - * @pb: buffer structure passed from userspace to vidioc_prepare_buf - * handler in driver - * - * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * The passed buffer should have been verified. - * This function calls buf_prepare callback in the driver (if provided), - * in which driver-specific buffer initialization can be performed, - * - * The return values from this function are intended to be directly returned - * from vidioc_prepare_buf handler in driver. - */ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) { struct vb2_buffer *vb; @@ -1481,24 +1363,6 @@ static int vb2_start_streaming(struct vb2_queue *q) return ret; } -/** - * vb2_core_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @index: id number of the buffer - * @pb: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * The passed buffer should have been verified. - * This function: - * 1) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 2) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb) { struct vb2_buffer *vb; @@ -1679,15 +1543,6 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, return ret; } -/** - * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2 - * @q: videobuf2 queue - * - * This function will wait until all buffers that have been given to the driver - * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call - * wait_prepare, wait_finish pair. It is intended to be called with all locks - * taken, for example from stop_streaming() callback. - */ int vb2_wait_for_all_buffers(struct vb2_queue *q) { if (!q->streaming) { @@ -1725,27 +1580,6 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) } } -/** - * vb2_dqbuf() - Dequeue a buffer to the userspace - * @q: videobuf2 queue - * @pb: buffer structure passed from userspace to vidioc_dqbuf handler - * in driver - * @nonblocking: if true, this call will not sleep waiting for a buffer if no - * buffers ready for dequeuing are present. Normally the driver - * would be passing (file->f_flags & O_NONBLOCK) here - * - * Should be called from vidioc_dqbuf ioctl handler of a driver. - * The passed buffer should have been verified. - * This function: - * 1) calls buf_finish callback in the driver (if provided), in which - * driver can perform any additional operations that may be required before - * returning the buffer to userspace, such as cache sync, - * 2) the buffer struct members are filled with relevant information for - * the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_dqbuf handler in driver. - */ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, bool nonblocking) { @@ -1909,19 +1743,6 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) } EXPORT_SYMBOL_GPL(vb2_core_streamon); -/** - * vb2_queue_error() - signal a fatal error on the queue - * @q: videobuf2 queue - * - * Flag that a fatal unrecoverable error has occurred and wake up all processes - * waiting on the queue. Polling will now set POLLERR and queuing and dequeuing - * buffers will return -EIO. - * - * The error flag will be cleared when cancelling the queue, either from - * vb2_streamoff or vb2_queue_release. Drivers should thus not call this - * function before starting the stream, otherwise the error flag will remain set - * until the queue is released when closing the device node. - */ void vb2_queue_error(struct vb2_queue *q) { q->error = 1; @@ -1984,19 +1805,6 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, return -EINVAL; } -/** - * vb2_core_expbuf() - Export a buffer as a file descriptor - * @q: videobuf2 queue - * @fd: file descriptor associated with DMABUF (set by driver) * - * @type: buffer type - * @index: id number of the buffer - * @plane: index of the plane to be exported, 0 for single plane queues - * @flags: flags for newly created file, currently only O_CLOEXEC is - * supported, refer to manual of open syscall for more details - * - * The return values from this function are intended to be directly returned - * from vidioc_expbuf handler in driver. - */ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, unsigned int index, unsigned int plane, unsigned int flags) { @@ -2068,25 +1876,6 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, } EXPORT_SYMBOL_GPL(vb2_core_expbuf); -/** - * vb2_mmap() - map video buffers into application address space - * @q: videobuf2 queue - * @vma: vma passed to the mmap file operation handler in the driver - * - * Should be called from mmap file operation handler of a driver. - * This function maps one plane of one of the available video buffers to - * userspace. To map whole video memory allocated on reqbufs, this function - * has to be called once per each plane per each buffer previously allocated. - * - * When the userspace application calls mmap, it passes to it an offset returned - * to it earlier by the means of vidioc_querybuf handler. That offset acts as - * a "cookie", which is then used to identify the plane to be mapped. - * This function finds a plane with a matching offset and a mapping is performed - * by the means of a provided memory operation. - * - * The return values from this function are intended to be directly returned - * from the mmap handler in driver. - */ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) { unsigned long off = vma->vm_pgoff << PAGE_SHIFT; @@ -2188,17 +1977,6 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); #endif -/** - * vb2_core_queue_init() - initialize a videobuf2 queue - * @q: videobuf2 queue; this structure should be allocated in driver - * - * The vb2_queue structure should be allocated by the driver. The driver is - * responsible of clearing it's content and setting initial values for some - * required entries before calling this function. - * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer - * to the struct vb2_queue description in include/media/videobuf2-core.h - * for more information. - */ int vb2_core_queue_init(struct vb2_queue *q) { /* @@ -2228,14 +2006,6 @@ EXPORT_SYMBOL_GPL(vb2_core_queue_init); static int __vb2_init_fileio(struct vb2_queue *q, int read); static int __vb2_cleanup_fileio(struct vb2_queue *q); -/** - * vb2_core_queue_release() - stop streaming, release the queue and free memory - * @q: videobuf2 queue - * - * This function stops streaming and performs necessary clean ups, including - * freeing video buffer memory. The driver is responsible for freeing - * the vb2_queue structure itself. - */ void vb2_core_queue_release(struct vb2_queue *q) { __vb2_cleanup_fileio(q); @@ -2246,22 +2016,6 @@ void vb2_core_queue_release(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(vb2_core_queue_release); -/** - * vb2_core_poll() - implements poll userspace operation - * @q: videobuf2 queue - * @file: file argument passed to the poll file operation handler - * @wait: wait argument passed to the poll file operation handler - * - * This function implements poll file operation handler for a driver. - * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will - * be informed that the file descriptor of a video device is available for - * reading. - * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor - * will be reported as available for writing. - * - * The return values from this function are intended to be directly returned - * from poll handler in driver. - */ unsigned int vb2_core_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 59fa204b15f3..fb6a177be461 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -141,6 +141,9 @@ static void *vb2_dc_alloc(struct device *dev, unsigned long attrs, { struct vb2_dc_buf *buf; + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -493,6 +496,9 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, return ERR_PTR(-EINVAL); } + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -673,6 +679,9 @@ static void *vb2_dc_attach_dmabuf(struct device *dev, struct dma_buf *dbuf, if (dbuf->size < size) return ERR_PTR(-EFAULT); + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index bd82d709ee82..ecff8f492c4f 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -104,11 +104,12 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, int ret; int num_pages; - if (WARN_ON(dev == NULL)) - return NULL; + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) - return NULL; + return ERR_PTR(-ENOMEM); buf->vaddr = NULL; buf->dma_dir = dma_dir; @@ -166,7 +167,7 @@ fail_pages_alloc: kfree(buf->pages); fail_pages_array_alloc: kfree(buf); - return NULL; + return ERR_PTR(-ENOMEM); } static void vb2_dma_sg_put(void *buf_priv) @@ -224,9 +225,12 @@ static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr, struct sg_table *sgt; struct frame_vector *vec; + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) - return NULL; + return ERR_PTR(-ENOMEM); buf->vaddr = NULL; buf->dev = dev; @@ -266,7 +270,7 @@ userptr_fail_sgtable: vb2_destroy_framevec(vec); userptr_fail_pfnvec: kfree(buf); - return NULL; + return ERR_PTR(-ENOMEM); } /* @@ -606,6 +610,9 @@ static void *vb2_dma_sg_attach_dmabuf(struct device *dev, struct dma_buf *dbuf, struct vb2_dma_sg_buf *buf; struct dma_buf_attachment *dba; + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + if (dbuf->size < size) return ERR_PTR(-EFAULT); diff --git a/drivers/media/v4l2-core/videobuf2-v4l2.c b/drivers/media/v4l2-core/videobuf2-v4l2.c index 9cfbb6e4bc28..52ef8833f6b6 100644 --- a/drivers/media/v4l2-core/videobuf2-v4l2.c +++ b/drivers/media/v4l2-core/videobuf2-v4l2.c @@ -483,13 +483,6 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL(vb2_querybuf); -/** - * vb2_reqbufs() - Wrapper for vb2_core_reqbufs() that also verifies - * the memory and type values. - * @q: videobuf2 queue - * @req: struct passed from userspace to vidioc_reqbufs handler - * in driver - */ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = vb2_verify_memory_type(q, req->memory, req->type); @@ -498,21 +491,6 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) } EXPORT_SYMBOL_GPL(vb2_reqbufs); -/** - * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_prepare_buf - * handler in driver - * - * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, - * - * The return values from this function are intended to be directly returned - * from vidioc_prepare_buf handler in driver. - */ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { int ret; @@ -528,13 +506,6 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL_GPL(vb2_prepare_buf); -/** - * vb2_create_bufs() - Wrapper for vb2_core_create_bufs() that also verifies - * the memory and type values. - * @q: videobuf2 queue - * @create: creation parameters, passed from userspace to vidioc_create_bufs - * handler in driver - */ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) { unsigned requested_planes = 1; @@ -586,23 +557,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) } EXPORT_SYMBOL_GPL(vb2_create_bufs); -/** - * vb2_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { int ret; @@ -617,27 +571,6 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL_GPL(vb2_qbuf); -/** - * vb2_dqbuf() - Dequeue a buffer to the userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_dqbuf handler - * in driver - * @nonblocking: if true, this call will not sleep waiting for a buffer if no - * buffers ready for dequeuing are present. Normally the driver - * would be passing (file->f_flags & O_NONBLOCK) here - * - * Should be called from vidioc_dqbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_finish callback in the driver (if provided), in which - * driver can perform any additional operations that may be required before - * returning the buffer to userspace, such as cache sync, - * 3) the buffer struct members are filled with relevant information for - * the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_dqbuf handler in driver. - */ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { int ret; @@ -664,19 +597,6 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) } EXPORT_SYMBOL_GPL(vb2_dqbuf); -/** - * vb2_streamon - start streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamon handler - * - * Should be called from vidioc_streamon handler of a driver. - * This function: - * 1) verifies current state - * 2) passes any previously queued buffers to the driver and starts streaming - * - * The return values from this function are intended to be directly returned - * from vidioc_streamon handler in the driver. - */ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { @@ -687,21 +607,6 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } EXPORT_SYMBOL_GPL(vb2_streamon); -/** - * vb2_streamoff - stop streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamoff handler - * - * Should be called from vidioc_streamoff handler of a driver. - * This function: - * 1) verifies current state, - * 2) stop streaming and dequeues any queued buffers, including those previously - * passed to the driver (after waiting for the driver to finish). - * - * This call can be used for pausing playback. - * The return values from this function are intended to be directly returned - * from vidioc_streamoff handler in the driver - */ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { @@ -712,15 +617,6 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) } EXPORT_SYMBOL_GPL(vb2_streamoff); -/** - * vb2_expbuf() - Export a buffer as a file descriptor - * @q: videobuf2 queue - * @eb: export buffer structure passed from userspace to vidioc_expbuf - * handler in driver - * - * The return values from this function are intended to be directly returned - * from vidioc_expbuf handler in driver. - */ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) { return vb2_core_expbuf(q, &eb->fd, eb->type, eb->index, @@ -728,17 +624,6 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) } EXPORT_SYMBOL_GPL(vb2_expbuf); -/** - * vb2_queue_init() - initialize a videobuf2 queue - * @q: videobuf2 queue; this structure should be allocated in driver - * - * The vb2_queue structure should be allocated by the driver. The driver is - * responsible of clearing it's content and setting initial values for some - * required entries before calling this function. - * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer - * to the struct vb2_queue description in include/media/videobuf2-core.h - * for more information. - */ int vb2_queue_init(struct vb2_queue *q) { /* @@ -779,39 +664,12 @@ int vb2_queue_init(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(vb2_queue_init); -/** - * vb2_queue_release() - stop streaming, release the queue and free memory - * @q: videobuf2 queue - * - * This function stops streaming and performs necessary clean ups, including - * freeing video buffer memory. The driver is responsible for freeing - * the vb2_queue structure itself. - */ void vb2_queue_release(struct vb2_queue *q) { vb2_core_queue_release(q); } EXPORT_SYMBOL_GPL(vb2_queue_release); -/** - * vb2_poll() - implements poll userspace operation - * @q: videobuf2 queue - * @file: file argument passed to the poll file operation handler - * @wait: wait argument passed to the poll file operation handler - * - * This function implements poll file operation handler for a driver. - * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will - * be informed that the file descriptor of a video device is available for - * reading. - * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor - * will be reported as available for writing. - * - * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any - * pending events. - * - * The return values from this function are intended to be directly returned - * from poll handler in driver. - */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { struct video_device *vfd = video_devdata(file); diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index c2820a6e164d..ab3227b75c84 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -41,7 +41,7 @@ static void *vb2_vmalloc_alloc(struct device *dev, unsigned long attrs, buf = kzalloc(sizeof(*buf), GFP_KERNEL | gfp_flags); if (!buf) - return NULL; + return ERR_PTR(-ENOMEM); buf->size = size; buf->vaddr = vmalloc_user(buf->size); @@ -53,7 +53,7 @@ static void *vb2_vmalloc_alloc(struct device *dev, unsigned long attrs, if (!buf->vaddr) { pr_debug("vmalloc of size %ld failed\n", buf->size); kfree(buf); - return NULL; + return ERR_PTR(-ENOMEM); } atomic_inc(&buf->refcount); @@ -77,17 +77,20 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr, struct vb2_vmalloc_buf *buf; struct frame_vector *vec; int n_pages, offset, i; + int ret = -ENOMEM; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) - return NULL; + return ERR_PTR(-ENOMEM); buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); - if (IS_ERR(vec)) + if (IS_ERR(vec)) { + ret = PTR_ERR(vec); goto fail_pfnvec_create; + } buf->vec = vec; n_pages = frame_vector_count(vec); if (frame_vector_to_pages(vec) < 0) { @@ -117,7 +120,7 @@ fail_map: fail_pfnvec_create: kfree(buf); - return NULL; + return ERR_PTR(ret); } static void vb2_vmalloc_put_userptr(void *buf_priv) diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index 903becd31410..93ceea4f27d5 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -91,9 +91,132 @@ static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai); -/* Temporary variables used during scanning */ -static struct ubi_ec_hdr *ech; -static struct ubi_vid_hdr *vidh; +#define AV_FIND BIT(0) +#define AV_ADD BIT(1) +#define AV_FIND_OR_ADD (AV_FIND | AV_ADD) + +/** + * find_or_add_av - internal function to find a volume, add a volume or do + * both (find and add if missing). + * @ai: attaching information + * @vol_id: the requested volume ID + * @flags: a combination of the %AV_FIND and %AV_ADD flags describing the + * expected operation. If only %AV_ADD is set, -EEXIST is returned + * if the volume already exists. If only %AV_FIND is set, NULL is + * returned if the volume does not exist. And if both flags are + * set, the helper first tries to find an existing volume, and if + * it does not exist it creates a new one. + * @created: in value used to inform the caller whether it"s a newly created + * volume or not. + * + * This function returns a pointer to a volume description or an ERR_PTR if + * the operation failed. It can also return NULL if only %AV_FIND is set and + * the volume does not exist. + */ +static struct ubi_ainf_volume *find_or_add_av(struct ubi_attach_info *ai, + int vol_id, unsigned int flags, + bool *created) +{ + struct ubi_ainf_volume *av; + struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + + /* Walk the volume RB-tree to look if this volume is already present */ + while (*p) { + parent = *p; + av = rb_entry(parent, struct ubi_ainf_volume, rb); + + if (vol_id == av->vol_id) { + *created = false; + + if (!(flags & AV_FIND)) + return ERR_PTR(-EEXIST); + + return av; + } + + if (vol_id > av->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + if (!(flags & AV_ADD)) + return NULL; + + /* The volume is absent - add it */ + av = kzalloc(sizeof(*av), GFP_KERNEL); + if (!av) + return ERR_PTR(-ENOMEM); + + av->vol_id = vol_id; + + if (vol_id > ai->highest_vol_id) + ai->highest_vol_id = vol_id; + + rb_link_node(&av->rb, parent, p); + rb_insert_color(&av->rb, &ai->volumes); + ai->vols_found += 1; + *created = true; + dbg_bld("added volume %d", vol_id); + return av; +} + +/** + * ubi_find_or_add_av - search for a volume in the attaching information and + * add one if it does not exist. + * @ai: attaching information + * @vol_id: the requested volume ID + * @created: whether the volume has been created or not + * + * This function returns a pointer to the new volume description or an + * ERR_PTR if the operation failed. + */ +static struct ubi_ainf_volume *ubi_find_or_add_av(struct ubi_attach_info *ai, + int vol_id, bool *created) +{ + return find_or_add_av(ai, vol_id, AV_FIND_OR_ADD, created); +} + +/** + * ubi_alloc_aeb - allocate an aeb element + * @ai: attaching information + * @pnum: physical eraseblock number + * @ec: erase counter of the physical eraseblock + * + * Allocate an aeb object and initialize the pnum and ec information. + * vol_id and lnum are set to UBI_UNKNOWN, and the other fields are + * initialized to zero. + * Note that the element is not added in any list or RB tree. + */ +struct ubi_ainf_peb *ubi_alloc_aeb(struct ubi_attach_info *ai, int pnum, + int ec) +{ + struct ubi_ainf_peb *aeb; + + aeb = kmem_cache_zalloc(ai->aeb_slab_cache, GFP_KERNEL); + if (!aeb) + return NULL; + + aeb->pnum = pnum; + aeb->ec = ec; + aeb->vol_id = UBI_UNKNOWN; + aeb->lnum = UBI_UNKNOWN; + + return aeb; +} + +/** + * ubi_free_aeb - free an aeb element + * @ai: attaching information + * @aeb: the element to free + * + * Free an aeb object. The caller must have removed the element from any list + * or RB tree. + */ +void ubi_free_aeb(struct ubi_attach_info *ai, struct ubi_ainf_peb *aeb) +{ + kmem_cache_free(ai->aeb_slab_cache, aeb); +} /** * add_to_list - add physical eraseblock to a list. @@ -131,14 +254,12 @@ static int add_to_list(struct ubi_attach_info *ai, int pnum, int vol_id, } else BUG(); - aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + aeb = ubi_alloc_aeb(ai, pnum, ec); if (!aeb) return -ENOMEM; - aeb->pnum = pnum; aeb->vol_id = vol_id; aeb->lnum = lnum; - aeb->ec = ec; if (to_head) list_add(&aeb->u.list, list); else @@ -163,13 +284,11 @@ static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec) dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); - aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + aeb = ubi_alloc_aeb(ai, pnum, ec); if (!aeb) return -ENOMEM; ai->corr_peb_count += 1; - aeb->pnum = pnum; - aeb->ec = ec; list_add(&aeb->u.list, &ai->corr); return 0; } @@ -192,14 +311,12 @@ static int add_fastmap(struct ubi_attach_info *ai, int pnum, { struct ubi_ainf_peb *aeb; - aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + aeb = ubi_alloc_aeb(ai, pnum, ec); if (!aeb) return -ENOMEM; - aeb->pnum = pnum; - aeb->vol_id = be32_to_cpu(vidh->vol_id); - aeb->sqnum = be64_to_cpu(vidh->sqnum); - aeb->ec = ec; + aeb->vol_id = be32_to_cpu(vid_hdr->vol_id); + aeb->sqnum = be64_to_cpu(vid_hdr->sqnum); list_add(&aeb->u.list, &ai->fastmap); dbg_bld("add to fastmap list: PEB %d, vol_id %d, sqnum: %llu", pnum, @@ -294,44 +411,20 @@ static struct ubi_ainf_volume *add_volume(struct ubi_attach_info *ai, const struct ubi_vid_hdr *vid_hdr) { struct ubi_ainf_volume *av; - struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + bool created; ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); - /* Walk the volume RB-tree to look if this volume is already present */ - while (*p) { - parent = *p; - av = rb_entry(parent, struct ubi_ainf_volume, rb); - - if (vol_id == av->vol_id) - return av; - - if (vol_id > av->vol_id) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - /* The volume is absent - add it */ - av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL); - if (!av) - return ERR_PTR(-ENOMEM); + av = ubi_find_or_add_av(ai, vol_id, &created); + if (IS_ERR(av) || !created) + return av; - av->highest_lnum = av->leb_count = 0; - av->vol_id = vol_id; - av->root = RB_ROOT; av->used_ebs = be32_to_cpu(vid_hdr->used_ebs); av->data_pad = be32_to_cpu(vid_hdr->data_pad); av->compat = vid_hdr->compat; av->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; - if (vol_id > ai->highest_vol_id) - ai->highest_vol_id = vol_id; - rb_link_node(&av->rb, parent, p); - rb_insert_color(&av->rb, &ai->volumes); - ai->vols_found += 1; - dbg_bld("added volume %d", vol_id); return av; } @@ -360,7 +453,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, { int len, err, second_is_newer, bitflips = 0, corrupted = 0; uint32_t data_crc, crc; - struct ubi_vid_hdr *vh = NULL; + struct ubi_vid_io_buf *vidb = NULL; unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); if (sqnum2 == aeb->sqnum) { @@ -403,12 +496,12 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, return bitflips << 1; } - vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vh) + vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!vidb) return -ENOMEM; pnum = aeb->pnum; - err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 0); if (err) { if (err == UBI_IO_BITFLIPS) bitflips = 1; @@ -422,7 +515,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, } } - vid_hdr = vh; + vid_hdr = ubi_get_vid_hdr(vidb); } /* Read the data of the copy and check the CRC */ @@ -448,7 +541,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, } mutex_unlock(&ubi->buf_mutex); - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vidb); if (second_is_newer) dbg_bld("second PEB %d is newer, copy_flag is set", pnum); @@ -460,7 +553,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, out_unlock: mutex_unlock(&ubi->buf_mutex); out_free_vidh: - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vidb); return err; } @@ -605,12 +698,10 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, if (err) return err; - aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + aeb = ubi_alloc_aeb(ai, pnum, ec); if (!aeb) return -ENOMEM; - aeb->ec = ec; - aeb->pnum = pnum; aeb->vol_id = vol_id; aeb->lnum = lnum; aeb->scrub = bitflips; @@ -629,6 +720,21 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, } /** + * ubi_add_av - add volume to the attaching information. + * @ai: attaching information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the new volume description or an + * ERR_PTR if the operation failed. + */ +struct ubi_ainf_volume *ubi_add_av(struct ubi_attach_info *ai, int vol_id) +{ + bool created; + + return find_or_add_av(ai, vol_id, AV_ADD, &created); +} + +/** * ubi_find_av - find volume in the attaching information. * @ai: attaching information * @vol_id: the requested volume ID @@ -639,24 +745,15 @@ int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, int vol_id) { - struct ubi_ainf_volume *av; - struct rb_node *p = ai->volumes.rb_node; - - while (p) { - av = rb_entry(p, struct ubi_ainf_volume, rb); - - if (vol_id == av->vol_id) - return av; - - if (vol_id > av->vol_id) - p = p->rb_left; - else - p = p->rb_right; - } + bool created; - return NULL; + return find_or_add_av((struct ubi_attach_info *)ai, vol_id, AV_FIND, + &created); } +static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av, + struct list_head *list); + /** * ubi_remove_av - delete attaching information about a volume. * @ai: attaching information @@ -664,19 +761,10 @@ struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, */ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av) { - struct rb_node *rb; - struct ubi_ainf_peb *aeb; - dbg_bld("remove attaching information about volume %d", av->vol_id); - while ((rb = rb_first(&av->root))) { - aeb = rb_entry(rb, struct ubi_ainf_peb, u.rb); - rb_erase(&aeb->u.rb, &av->root); - list_add_tail(&aeb->u.list, &ai->erase); - } - rb_erase(&av->rb, &ai->volumes); - kfree(av); + destroy_av(ai, av, &ai->erase); ai->vols_found -= 1; } @@ -866,6 +954,9 @@ static bool vol_ignored(int vol_id) static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, bool fast) { + struct ubi_ec_hdr *ech = ai->ech; + struct ubi_vid_io_buf *vidb = ai->vidb; + struct ubi_vid_hdr *vidh = ubi_get_vid_hdr(vidb); long long ec; int err, bitflips = 0, vol_id = -1, ec_err = 0; @@ -963,7 +1054,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, /* OK, we've done with the EC header, let's look at the VID header */ - err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 0); if (err < 0) return err; switch (err) { @@ -1191,10 +1282,12 @@ static int late_analysis(struct ubi_device *ubi, struct ubi_attach_info *ai) * destroy_av - free volume attaching information. * @av: volume attaching information * @ai: attaching information + * @list: put the aeb elements in there if !NULL, otherwise free them * * This function destroys the volume attaching information. */ -static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av) +static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av, + struct list_head *list) { struct ubi_ainf_peb *aeb; struct rb_node *this = av->root.rb_node; @@ -1214,7 +1307,10 @@ static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av) this->rb_right = NULL; } - kmem_cache_free(ai->aeb_slab_cache, aeb); + if (list) + list_add_tail(&aeb->u.list, list); + else + ubi_free_aeb(ai, aeb); } } kfree(av); @@ -1232,23 +1328,23 @@ static void destroy_ai(struct ubi_attach_info *ai) list_for_each_entry_safe(aeb, aeb_tmp, &ai->alien, u.list) { list_del(&aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); } list_for_each_entry_safe(aeb, aeb_tmp, &ai->erase, u.list) { list_del(&aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); } list_for_each_entry_safe(aeb, aeb_tmp, &ai->corr, u.list) { list_del(&aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); } list_for_each_entry_safe(aeb, aeb_tmp, &ai->free, u.list) { list_del(&aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); } list_for_each_entry_safe(aeb, aeb_tmp, &ai->fastmap, u.list) { list_del(&aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); } /* Destroy the volume RB-tree */ @@ -1269,7 +1365,7 @@ static void destroy_ai(struct ubi_attach_info *ai) rb->rb_right = NULL; } - destroy_av(ai, av); + destroy_av(ai, av, NULL); } } @@ -1297,12 +1393,12 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai, err = -ENOMEM; - ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ech) + ai->ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ai->ech) return err; - vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vidh) + ai->vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!ai->vidb) goto out_ech; for (pnum = start; pnum < ubi->peb_count; pnum++) { @@ -1351,15 +1447,15 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai, if (err) goto out_vidh; - ubi_free_vid_hdr(ubi, vidh); - kfree(ech); + ubi_free_vid_buf(ai->vidb); + kfree(ai->ech); return 0; out_vidh: - ubi_free_vid_hdr(ubi, vidh); + ubi_free_vid_buf(ai->vidb); out_ech: - kfree(ech); + kfree(ai->ech); return err; } @@ -1411,12 +1507,12 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai) if (!scan_ai) goto out; - ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ech) + scan_ai->ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!scan_ai->ech) goto out_ai; - vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vidh) + scan_ai->vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!scan_ai->vidb) goto out_ech; for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) { @@ -1428,8 +1524,8 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai) goto out_vidh; } - ubi_free_vid_hdr(ubi, vidh); - kfree(ech); + ubi_free_vid_buf(scan_ai->vidb); + kfree(scan_ai->ech); if (scan_ai->force_full_scan) err = UBI_NO_FASTMAP; @@ -1449,9 +1545,9 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai) return err; out_vidh: - ubi_free_vid_hdr(ubi, vidh); + ubi_free_vid_buf(scan_ai->vidb); out_ech: - kfree(ech); + kfree(scan_ai->ech); out_ai: destroy_ai(scan_ai); out: @@ -1573,6 +1669,8 @@ out_ai: */ static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai) { + struct ubi_vid_io_buf *vidb = ai->vidb; + struct ubi_vid_hdr *vidh = ubi_get_vid_hdr(vidb); int pnum, err, vols_found = 0; struct rb_node *rb1, *rb2; struct ubi_ainf_volume *av; @@ -1708,7 +1806,7 @@ static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai) last_aeb = aeb; - err = ubi_io_read_vid_hdr(ubi, aeb->pnum, vidh, 1); + err = ubi_io_read_vid_hdr(ubi, aeb->pnum, vidb, 1); if (err && err != UBI_IO_BITFLIPS) { ubi_err(ubi, "VID header is not OK (%d)", err); diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 0680516bb472..85d54f37e28f 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -574,7 +574,7 @@ void ubi_free_internal_volumes(struct ubi_device *ubi) for (i = ubi->vtbl_slots; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { - kfree(ubi->volumes[i]->eba_tbl); + ubi_eba_replace_table(ubi->volumes[i], NULL); kfree(ubi->volumes[i]); } } diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index ee2b74d1d1b5..45c329694a5e 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -416,7 +416,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, } rsvd_bytes = (long long)vol->reserved_pebs * - ubi->leb_size-vol->data_pad; + vol->usable_leb_size; if (bytes < 0 || bytes > rsvd_bytes) { err = -EINVAL; break; @@ -454,7 +454,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, /* Validate the request */ err = -EINVAL; - if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || + if (!ubi_leb_valid(vol, req.lnum) || req.bytes < 0 || req.bytes > vol->usable_leb_size) break; @@ -485,7 +485,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, break; } - if (lnum < 0 || lnum >= vol->reserved_pebs) { + if (!ubi_leb_valid(vol, lnum)) { err = -EINVAL; break; } diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index ebf517271d29..95c4048a371e 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -50,6 +50,30 @@ #define EBA_RESERVED_PEBS 1 /** + * struct ubi_eba_entry - structure encoding a single LEB -> PEB association + * @pnum: the physical eraseblock number attached to the LEB + * + * This structure is encoding a LEB -> PEB association. Note that the LEB + * number is not stored here, because it is the index used to access the + * entries table. + */ +struct ubi_eba_entry { + int pnum; +}; + +/** + * struct ubi_eba_table - LEB -> PEB association information + * @entries: the LEB to PEB mapping (one entry per LEB). + * + * This structure is private to the EBA logic and should be kept here. + * It is encoding the LEB to PEB association table, and is subject to + * changes. + */ +struct ubi_eba_table { + struct ubi_eba_entry *entries; +}; + +/** * next_sqnum - get next sequence number. * @ubi: UBI device description object * @@ -84,6 +108,110 @@ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) } /** + * ubi_eba_get_ldesc - get information about a LEB + * @vol: volume description object + * @lnum: logical eraseblock number + * @ldesc: the LEB descriptor to fill + * + * Used to query information about a specific LEB. + * It is currently only returning the physical position of the LEB, but will be + * extended to provide more information. + */ +void ubi_eba_get_ldesc(struct ubi_volume *vol, int lnum, + struct ubi_eba_leb_desc *ldesc) +{ + ldesc->lnum = lnum; + ldesc->pnum = vol->eba_tbl->entries[lnum].pnum; +} + +/** + * ubi_eba_create_table - allocate a new EBA table and initialize it with all + * LEBs unmapped + * @vol: volume containing the EBA table to copy + * @nentries: number of entries in the table + * + * Allocate a new EBA table and initialize it with all LEBs unmapped. + * Returns a valid pointer if it succeed, an ERR_PTR() otherwise. + */ +struct ubi_eba_table *ubi_eba_create_table(struct ubi_volume *vol, + int nentries) +{ + struct ubi_eba_table *tbl; + int err = -ENOMEM; + int i; + + tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); + if (!tbl) + return ERR_PTR(-ENOMEM); + + tbl->entries = kmalloc_array(nentries, sizeof(*tbl->entries), + GFP_KERNEL); + if (!tbl->entries) + goto err; + + for (i = 0; i < nentries; i++) + tbl->entries[i].pnum = UBI_LEB_UNMAPPED; + + return tbl; + +err: + kfree(tbl->entries); + kfree(tbl); + + return ERR_PTR(err); +} + +/** + * ubi_eba_destroy_table - destroy an EBA table + * @tbl: the table to destroy + * + * Destroy an EBA table. + */ +void ubi_eba_destroy_table(struct ubi_eba_table *tbl) +{ + if (!tbl) + return; + + kfree(tbl->entries); + kfree(tbl); +} + +/** + * ubi_eba_copy_table - copy the EBA table attached to vol into another table + * @vol: volume containing the EBA table to copy + * @dst: destination + * @nentries: number of entries to copy + * + * Copy the EBA table stored in vol into the one pointed by dst. + */ +void ubi_eba_copy_table(struct ubi_volume *vol, struct ubi_eba_table *dst, + int nentries) +{ + struct ubi_eba_table *src; + int i; + + ubi_assert(dst && vol && vol->eba_tbl); + + src = vol->eba_tbl; + + for (i = 0; i < nentries; i++) + dst->entries[i].pnum = src->entries[i].pnum; +} + +/** + * ubi_eba_replace_table - assign a new EBA table to a volume + * @vol: volume containing the EBA table to copy + * @tbl: new EBA table + * + * Assign a new EBA table to the volume and release the old one. + */ +void ubi_eba_replace_table(struct ubi_volume *vol, struct ubi_eba_table *tbl) +{ + ubi_eba_destroy_table(vol->eba_tbl); + vol->eba_tbl = tbl; +} + +/** * ltree_lookup - look up the lock tree. * @ubi: UBI device description object * @vol_id: volume ID @@ -312,6 +440,18 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) } /** + * ubi_eba_is_mapped - check if a LEB is mapped. + * @vol: volume description object + * @lnum: logical eraseblock number + * + * This function returns true if the LEB is mapped, false otherwise. + */ +bool ubi_eba_is_mapped(struct ubi_volume *vol, int lnum) +{ + return vol->eba_tbl->entries[lnum].pnum >= 0; +} + +/** * ubi_eba_unmap_leb - un-map logical eraseblock. * @ubi: UBI device description object * @vol: volume description object @@ -333,7 +473,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum < 0) /* This logical eraseblock is already unmapped */ goto out_unlock; @@ -341,7 +481,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); down_read(&ubi->fm_eba_sem); - vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + vol->eba_tbl->entries[lnum].pnum = UBI_LEB_UNMAPPED; up_read(&ubi->fm_eba_sem); err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0); @@ -373,6 +513,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int offset, int len, int check) { int err, pnum, scrub = 0, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t uninitialized_var(crc); @@ -380,7 +521,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum < 0) { /* * The logical eraseblock is not mapped, fill the whole buffer @@ -403,13 +544,15 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, retry: if (check) { - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) { err = -ENOMEM; goto out_unlock; } - err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + vid_hdr = ubi_get_vid_hdr(vidb); + + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 1); if (err && err != UBI_IO_BITFLIPS) { if (err > 0) { /* @@ -455,7 +598,7 @@ retry: ubi_assert(len == be32_to_cpu(vid_hdr->data_size)); crc = be32_to_cpu(vid_hdr->data_crc); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); } err = ubi_io_read_data(ubi, buf, pnum, offset, len); @@ -492,7 +635,7 @@ retry: return err; out_free: - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); out_unlock: leb_read_unlock(ubi, vol_id, lnum); return err; @@ -554,49 +697,47 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, } /** - * recover_peb - recover from write failure. - * @ubi: UBI device description object + * try_recover_peb - try to recover from write failure. + * @vol: volume description object * @pnum: the physical eraseblock to recover - * @vol_id: volume ID * @lnum: logical eraseblock number * @buf: data which was not written because of the write failure * @offset: offset of the failed write * @len: how many bytes should have been written + * @vidb: VID buffer + * @retry: whether the caller should retry in case of failure * * This function is called in case of a write failure and moves all good data * from the potentially bad physical eraseblock to a good physical eraseblock. * This function also writes the data which was not written due to the failure. - * Returns new physical eraseblock number in case of success, and a negative - * error code in case of failure. + * Returns 0 in case of success, and a negative error code in case of failure. + * In case of failure, the %retry parameter is set to false if this is a fatal + * error (retrying won't help), and true otherwise. */ -static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, - const void *buf, int offset, int len) +static int try_recover_peb(struct ubi_volume *vol, int pnum, int lnum, + const void *buf, int offset, int len, + struct ubi_vid_io_buf *vidb, bool *retry) { - int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; - struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_device *ubi = vol->ubi; struct ubi_vid_hdr *vid_hdr; + int new_pnum, err, vol_id = vol->vol_id, data_size; uint32_t crc; - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) - return -ENOMEM; + *retry = false; -retry: new_pnum = ubi_wl_get_peb(ubi); if (new_pnum < 0) { - ubi_free_vid_hdr(ubi, vid_hdr); - up_read(&ubi->fm_eba_sem); - return new_pnum; + err = new_pnum; + goto out_put; } ubi_msg(ubi, "recover PEB %d, move data to PEB %d", pnum, new_pnum); - err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + err = ubi_io_read_vid_hdr(ubi, pnum, vidb, 1); if (err && err != UBI_IO_BITFLIPS) { if (err > 0) err = -EIO; - up_read(&ubi->fm_eba_sem); goto out_put; } @@ -608,12 +749,12 @@ retry: /* Read everything before the area where the write failure happened */ if (offset > 0) { err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); - if (err && err != UBI_IO_BITFLIPS) { - up_read(&ubi->fm_eba_sem); + if (err && err != UBI_IO_BITFLIPS) goto out_unlock; - } } + *retry = true; + memcpy(ubi->peb_buf + offset, buf, len); data_size = offset + len; @@ -622,50 +763,140 @@ retry: vid_hdr->copy_flag = 1; vid_hdr->data_size = cpu_to_be32(data_size); vid_hdr->data_crc = cpu_to_be32(crc); - err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); - if (err) { - mutex_unlock(&ubi->buf_mutex); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + err = ubi_io_write_vid_hdr(ubi, new_pnum, vidb); + if (err) + goto out_unlock; err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); - if (err) { - mutex_unlock(&ubi->buf_mutex); - up_read(&ubi->fm_eba_sem); - goto write_error; - } +out_unlock: mutex_unlock(&ubi->buf_mutex); - ubi_free_vid_hdr(ubi, vid_hdr); - vol->eba_tbl[lnum] = new_pnum; + if (!err) + vol->eba_tbl->entries[lnum].pnum = new_pnum; + +out_put: up_read(&ubi->fm_eba_sem); - ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - ubi_msg(ubi, "data was successfully recovered"); - return 0; + if (!err) { + ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); + ubi_msg(ubi, "data was successfully recovered"); + } else if (new_pnum >= 0) { + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's + * try to get another one. + */ + ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); + ubi_warn(ubi, "failed to write to PEB %d", new_pnum); + } -out_unlock: - mutex_unlock(&ubi->buf_mutex); -out_put: - ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); - ubi_free_vid_hdr(ubi, vid_hdr); return err; +} -write_error: - /* - * Bad luck? This physical eraseblock is bad too? Crud. Let's try to - * get another one. - */ - ubi_warn(ubi, "failed to write to PEB %d", new_pnum); - ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); - if (++tries > UBI_IO_RETRIES) { - ubi_free_vid_hdr(ubi, vid_hdr); - return err; +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data which was not written because of the write failure + * @offset: offset of the failed write + * @len: how many bytes should have been written + * + * This function is called in case of a write failure and moves all good data + * from the potentially bad physical eraseblock to a good physical eraseblock. + * This function also writes the data which was not written due to the failure. + * Returns 0 in case of success, and a negative error code in case of failure. + * This function tries %UBI_IO_RETRIES before giving up. + */ +static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, + const void *buf, int offset, int len) +{ + int err, idx = vol_id2idx(ubi, vol_id), tries; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_io_buf *vidb; + + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) + return -ENOMEM; + + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + bool retry; + + err = try_recover_peb(vol, pnum, lnum, buf, offset, len, vidb, + &retry); + if (!err || !retry) + break; + + ubi_msg(ubi, "try again"); } - ubi_msg(ubi, "try again"); - goto retry; + + ubi_free_vid_buf(vidb); + + return err; +} + +/** + * try_write_vid_and_data - try to write VID header and data to a new PEB. + * @vol: volume description object + * @lnum: logical eraseblock number + * @vidb: the VID buffer to write + * @buf: buffer containing the data + * @offset: where to start writing data + * @len: how many bytes should be written + * + * This function tries to write VID header and data belonging to logical + * eraseblock @lnum of volume @vol to a new physical eraseblock. Returns zero + * in case of success and a negative error code in case of failure. + * In case of error, it is possible that something was still written to the + * flash media, but may be some garbage. + */ +static int try_write_vid_and_data(struct ubi_volume *vol, int lnum, + struct ubi_vid_io_buf *vidb, const void *buf, + int offset, int len) +{ + struct ubi_device *ubi = vol->ubi; + int pnum, opnum, err, vol_id = vol->vol_id; + + pnum = ubi_wl_get_peb(ubi); + if (pnum < 0) { + err = pnum; + goto out_put; + } + + opnum = vol->eba_tbl->entries[lnum].pnum; + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vidb); + if (err) { + ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto out_put; + } + + if (len) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn(ubi, + "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + goto out_put; + } + } + + vol->eba_tbl->entries[lnum].pnum = pnum; + +out_put: + up_read(&ubi->fm_eba_sem); + + if (err && pnum >= 0) + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); + else if (!err && opnum >= 0) + err = ubi_wl_put_peb(ubi, vol_id, lnum, opnum, 0); + + return err; } /** @@ -681,11 +912,13 @@ write_error: * @vol. Returns zero in case of success and a negative error code in case * of failure. In case of error, it is possible that something was still * written to the flash media, but may be some garbage. + * This function retries %UBI_IO_RETRIES times before giving up. */ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len) { - int err, pnum, tries = 0, vol_id = vol->vol_id; + int err, pnum, tries, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; if (ubi->ro_mode) @@ -695,7 +928,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err) return err; - pnum = vol->eba_tbl[lnum]; + pnum = vol->eba_tbl->entries[lnum].pnum; if (pnum >= 0) { dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", len, offset, vol_id, lnum, pnum); @@ -706,23 +939,23 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, if (err == -EIO && ubi->bad_allowed) err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); - if (err) - ubi_ro_mode(ubi); } - leb_write_unlock(ubi, vol_id, lnum); - return err; + + goto out; } /* * The logical eraseblock is not mapped. We have to get a free physical * eraseblock and write the volume identifier header there first. */ - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) { leb_write_unlock(ubi, vol_id, lnum); return -ENOMEM; } + vid_hdr = ubi_get_vid_hdr(vidb); + vid_hdr->vol_type = UBI_VID_DYNAMIC; vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); @@ -730,67 +963,30 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, vid_hdr->compat = ubi_get_compat(ubi, vol_id); vid_hdr->data_pad = cpu_to_be32(vol->data_pad); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - ubi_free_vid_hdr(ubi, vid_hdr); - leb_write_unlock(ubi, vol_id, lnum); - up_read(&ubi->fm_eba_sem); - return pnum; - } - - dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", - len, offset, vol_id, lnum, pnum); - - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, offset, len); + if (err != -EIO || !ubi->bad_allowed) + break; - if (len) { - err = ubi_io_write_data(ubi, buf, pnum, offset, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", - len, offset, vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + /* + * Fortunately, this is the first write operation to this + * physical eraseblock, so just put it and request a new one. + * We assume that if this physical eraseblock went bad, the + * erase code will handle that. + */ + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); - - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + ubi_free_vid_buf(vidb); -write_error: - if (err != -EIO || !ubi->bad_allowed) { +out: + if (err) ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } - /* - * Fortunately, this is the first write operation to this physical - * eraseblock, so just put it and request a new one. We assume that if - * this physical eraseblock went bad, the erase code will handle that. - */ - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } + leb_write_unlock(ubi, vol_id, lnum); - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; + return err; } /** @@ -818,7 +1014,8 @@ write_error: int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len, int used_ebs) { - int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; + int err, tries, data_size = len, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -831,15 +1028,15 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, else ubi_assert(!(len & (ubi->min_io_size - 1))); - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + err = leb_write_lock(ubi, vol_id, lnum); - if (err) { - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } + if (err) + goto out; vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); @@ -853,66 +1050,26 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->used_ebs = cpu_to_be32(used_ebs); vid_hdr->data_crc = cpu_to_be32(crc); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - ubi_free_vid_hdr(ubi, vid_hdr); - leb_write_unlock(ubi, vol_id, lnum); - up_read(&ubi->fm_eba_sem); - return pnum; - } + ubi_assert(vol->eba_tbl->entries[lnum].pnum < 0); - dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", - len, vol_id, lnum, pnum, used_ebs); + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, 0, len); + if (err != -EIO || !ubi->bad_allowed) + break; - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - err = ubi_io_write_data(ubi, buf, pnum, 0, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", - len, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } - - ubi_assert(vol->eba_tbl[lnum] < 0); - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); + if (err) + ubi_ro_mode(ubi); leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; - -write_error: - if (err != -EIO || !ubi->bad_allowed) { - /* - * This flash device does not admit of bad eraseblocks or - * something nasty and unexpected happened. Switch to read-only - * mode just in case. - */ - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - leb_write_unlock(ubi, vol_id, lnum); - ubi_free_vid_hdr(ubi, vid_hdr); - return err; - } +out: + ubi_free_vid_buf(vidb); - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; + return err; } /* @@ -935,7 +1092,8 @@ write_error: int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len) { - int err, pnum, old_pnum, tries = 0, vol_id = vol->vol_id; + int err, tries, vol_id = vol->vol_id; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -953,10 +1111,12 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); } - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + mutex_lock(&ubi->alc_mutex); err = leb_write_lock(ubi, vol_id, lnum); if (err) @@ -974,70 +1134,31 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->copy_flag = 1; vid_hdr->data_crc = cpu_to_be32(crc); -retry: - pnum = ubi_wl_get_peb(ubi); - if (pnum < 0) { - err = pnum; - up_read(&ubi->fm_eba_sem); - goto out_leb_unlock; - } - - dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", - vol_id, lnum, vol->eba_tbl[lnum], pnum); + dbg_eba("change LEB %d:%d", vol_id, lnum); - err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); - if (err) { - ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", - vol_id, lnum, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; - } + for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { + err = try_write_vid_and_data(vol, lnum, vidb, buf, 0, len); + if (err != -EIO || !ubi->bad_allowed) + break; - err = ubi_io_write_data(ubi, buf, pnum, 0, len); - if (err) { - ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", - len, pnum); - up_read(&ubi->fm_eba_sem); - goto write_error; + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ubi_msg(ubi, "try another PEB"); } - old_pnum = vol->eba_tbl[lnum]; - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_eba_sem); - - if (old_pnum >= 0) { - err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0); - if (err) - goto out_leb_unlock; - } + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + if (err) + ubi_ro_mode(ubi); -out_leb_unlock: leb_write_unlock(ubi, vol_id, lnum); + out_mutex: mutex_unlock(&ubi->alc_mutex); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); return err; - -write_error: - if (err != -EIO || !ubi->bad_allowed) { - /* - * This flash device does not admit of bad eraseblocks or - * something nasty and unexpected happened. Switch to read-only - * mode just in case. - */ - ubi_ro_mode(ubi); - goto out_leb_unlock; - } - - err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); - if (err || ++tries > UBI_IO_RETRIES) { - ubi_ro_mode(ubi); - goto out_leb_unlock; - } - - vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ubi_msg(ubi, "try another PEB"); - goto retry; } /** @@ -1082,12 +1203,15 @@ static int is_error_sane(int err) * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, - struct ubi_vid_hdr *vid_hdr) + struct ubi_vid_io_buf *vidb) { int err, vol_id, lnum, data_size, aldata_size, idx; + struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb); struct ubi_volume *vol; uint32_t crc; + ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem)); + vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); @@ -1142,9 +1266,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * probably waiting on @ubi->move_mutex. No need to continue the work, * cancel it. */ - if (vol->eba_tbl[lnum] != from) { + if (vol->eba_tbl->entries[lnum].pnum != from) { dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to PEB %d, cancel", - vol_id, lnum, from, vol->eba_tbl[lnum]); + vol_id, lnum, from, vol->eba_tbl->entries[lnum].pnum); err = MOVE_CANCEL_RACE; goto out_unlock_leb; } @@ -1196,7 +1320,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, } vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + err = ubi_io_write_vid_hdr(ubi, to, vidb); if (err) { if (err == -EIO) err = MOVE_TARGET_WR_ERR; @@ -1206,7 +1330,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, cond_resched(); /* Read the VID header back and check if it was written correctly */ - err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); + err = ubi_io_read_vid_hdr(ubi, to, vidb, 1); if (err) { if (err != UBI_IO_BITFLIPS) { ubi_warn(ubi, "error %d while reading VID header back from PEB %d", @@ -1229,10 +1353,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, cond_resched(); } - ubi_assert(vol->eba_tbl[lnum] == from); - down_read(&ubi->fm_eba_sem); - vol->eba_tbl[lnum] = to; - up_read(&ubi->fm_eba_sem); + ubi_assert(vol->eba_tbl->entries[lnum].pnum == from); + vol->eba_tbl->entries[lnum].pnum = to; out_unlock_buf: mutex_unlock(&ubi->buf_mutex); @@ -1388,7 +1510,7 @@ out_free: */ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) { - int i, j, err, num_volumes; + int i, err, num_volumes; struct ubi_ainf_volume *av; struct ubi_volume *vol; struct ubi_ainf_peb *aeb; @@ -1404,35 +1526,39 @@ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; for (i = 0; i < num_volumes; i++) { + struct ubi_eba_table *tbl; + vol = ubi->volumes[i]; if (!vol) continue; cond_resched(); - vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), - GFP_KERNEL); - if (!vol->eba_tbl) { - err = -ENOMEM; + tbl = ubi_eba_create_table(vol, vol->reserved_pebs); + if (IS_ERR(tbl)) { + err = PTR_ERR(tbl); goto out_free; } - for (j = 0; j < vol->reserved_pebs; j++) - vol->eba_tbl[j] = UBI_LEB_UNMAPPED; + ubi_eba_replace_table(vol, tbl); av = ubi_find_av(ai, idx2vol_id(ubi, i)); if (!av) continue; ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) { - if (aeb->lnum >= vol->reserved_pebs) + if (aeb->lnum >= vol->reserved_pebs) { /* * This may happen in case of an unclean reboot * during re-size. */ ubi_move_aeb_to_list(av, aeb, &ai->erase); - else - vol->eba_tbl[aeb->lnum] = aeb->pnum; + } else { + struct ubi_eba_entry *entry; + + entry = &vol->eba_tbl->entries[aeb->lnum]; + entry->pnum = aeb->pnum; + } } } @@ -1469,8 +1595,7 @@ out_free: for (i = 0; i < num_volumes; i++) { if (!ubi->volumes[i]) continue; - kfree(ubi->volumes[i]->eba_tbl); - ubi->volumes[i]->eba_tbl = NULL; + ubi_eba_replace_table(ubi->volumes[i], NULL); } return err; } diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c index 30d3999dddba..4f0bd6b4422a 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -262,6 +262,8 @@ static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) struct ubi_fm_pool *pool = &ubi->fm_wl_pool; int pnum; + ubi_assert(rwsem_is_locked(&ubi->fm_eba_sem)); + if (pool->used == pool->size) { /* We cannot update the fastmap here because this * function is called in atomic context. @@ -303,7 +305,7 @@ int ubi_ensure_anchor_pebs(struct ubi_device *ubi) wrk->anchor = 1; wrk->func = &wear_leveling_worker; - schedule_ubi_work(ubi, wrk); + __schedule_ubi_work(ubi, wrk); return 0; } @@ -344,7 +346,7 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, spin_unlock(&ubi->wl_lock); vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; - return schedule_erase(ubi, e, vol_id, lnum, torture); + return schedule_erase(ubi, e, vol_id, lnum, torture, true); } /** diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 48eb55f344eb..d6384d965788 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -110,21 +110,23 @@ size_t ubi_calc_fm_size(struct ubi_device *ubi) * Returns a new struct ubi_vid_hdr on success. * NULL indicates out of memory. */ -static struct ubi_vid_hdr *new_fm_vhdr(struct ubi_device *ubi, int vol_id) +static struct ubi_vid_io_buf *new_fm_vbuf(struct ubi_device *ubi, int vol_id) { - struct ubi_vid_hdr *new; + struct ubi_vid_io_buf *new; + struct ubi_vid_hdr *vh; - new = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + new = ubi_alloc_vid_buf(ubi, GFP_KERNEL); if (!new) goto out; - new->vol_type = UBI_VID_DYNAMIC; - new->vol_id = cpu_to_be32(vol_id); + vh = ubi_get_vid_hdr(new); + vh->vol_type = UBI_VID_DYNAMIC; + vh->vol_id = cpu_to_be32(vol_id); /* UBI implementations without fastmap support have to delete the * fastmap. */ - new->compat = UBI_COMPAT_DELETE; + vh->compat = UBI_COMPAT_DELETE; out: return new; @@ -145,12 +147,10 @@ static int add_aeb(struct ubi_attach_info *ai, struct list_head *list, { struct ubi_ainf_peb *aeb; - aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + aeb = ubi_alloc_aeb(ai, pnum, ec); if (!aeb) return -ENOMEM; - aeb->pnum = pnum; - aeb->ec = ec; aeb->lnum = -1; aeb->scrub = scrub; aeb->copy_flag = aeb->sqnum = 0; @@ -186,40 +186,19 @@ static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id, int last_eb_bytes) { struct ubi_ainf_volume *av; - struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; - - while (*p) { - parent = *p; - av = rb_entry(parent, struct ubi_ainf_volume, rb); - - if (vol_id > av->vol_id) - p = &(*p)->rb_left; - else if (vol_id < av->vol_id) - p = &(*p)->rb_right; - else - return ERR_PTR(-EINVAL); - } - av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL); - if (!av) - goto out; + av = ubi_add_av(ai, vol_id); + if (IS_ERR(av)) + return av; - av->highest_lnum = av->leb_count = av->used_ebs = 0; - av->vol_id = vol_id; av->data_pad = data_pad; av->last_data_size = last_eb_bytes; av->compat = 0; av->vol_type = vol_type; - av->root = RB_ROOT; if (av->vol_type == UBI_STATIC_VOLUME) av->used_ebs = used_ebs; dbg_bld("found volume (ID %i)", vol_id); - - rb_link_node(&av->rb, parent, p); - rb_insert_color(&av->rb, &ai->volumes); - -out: return av; } @@ -297,7 +276,7 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai, */ if (aeb->pnum == new_aeb->pnum) { ubi_assert(aeb->lnum == new_aeb->lnum); - kmem_cache_free(ai->aeb_slab_cache, new_aeb); + ubi_free_aeb(ai, new_aeb); return 0; } @@ -308,13 +287,10 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai, /* new_aeb is newer */ if (cmp_res & 1) { - victim = kmem_cache_alloc(ai->aeb_slab_cache, - GFP_KERNEL); + victim = ubi_alloc_aeb(ai, aeb->ec, aeb->pnum); if (!victim) return -ENOMEM; - victim->ec = aeb->ec; - victim->pnum = aeb->pnum; list_add_tail(&victim->u.list, &ai->erase); if (av->highest_lnum == be32_to_cpu(new_vh->lnum)) @@ -328,7 +304,8 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai, aeb->pnum = new_aeb->pnum; aeb->copy_flag = new_vh->copy_flag; aeb->scrub = new_aeb->scrub; - kmem_cache_free(ai->aeb_slab_cache, new_aeb); + aeb->sqnum = new_aeb->sqnum; + ubi_free_aeb(ai, new_aeb); /* new_aeb is older */ } else { @@ -370,41 +347,24 @@ static int process_pool_aeb(struct ubi_device *ubi, struct ubi_attach_info *ai, struct ubi_vid_hdr *new_vh, struct ubi_ainf_peb *new_aeb) { - struct ubi_ainf_volume *av, *tmp_av = NULL; - struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; - int found = 0; + int vol_id = be32_to_cpu(new_vh->vol_id); + struct ubi_ainf_volume *av; - if (be32_to_cpu(new_vh->vol_id) == UBI_FM_SB_VOLUME_ID || - be32_to_cpu(new_vh->vol_id) == UBI_FM_DATA_VOLUME_ID) { - kmem_cache_free(ai->aeb_slab_cache, new_aeb); + if (vol_id == UBI_FM_SB_VOLUME_ID || vol_id == UBI_FM_DATA_VOLUME_ID) { + ubi_free_aeb(ai, new_aeb); return 0; } /* Find the volume this SEB belongs to */ - while (*p) { - parent = *p; - tmp_av = rb_entry(parent, struct ubi_ainf_volume, rb); - - if (be32_to_cpu(new_vh->vol_id) > tmp_av->vol_id) - p = &(*p)->rb_left; - else if (be32_to_cpu(new_vh->vol_id) < tmp_av->vol_id) - p = &(*p)->rb_right; - else { - found = 1; - break; - } - } - - if (found) - av = tmp_av; - else { + av = ubi_find_av(ai, vol_id); + if (!av) { ubi_err(ubi, "orphaned volume in fastmap pool!"); - kmem_cache_free(ai->aeb_slab_cache, new_aeb); + ubi_free_aeb(ai, new_aeb); return UBI_BAD_FASTMAP; } - ubi_assert(be32_to_cpu(new_vh->vol_id) == av->vol_id); + ubi_assert(vol_id == av->vol_id); return update_vol(ubi, ai, av, new_vh, new_aeb); } @@ -423,16 +383,12 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum) struct rb_node *node, *node2; struct ubi_ainf_peb *aeb; - for (node = rb_first(&ai->volumes); node; node = rb_next(node)) { - av = rb_entry(node, struct ubi_ainf_volume, rb); - - for (node2 = rb_first(&av->root); node2; - node2 = rb_next(node2)) { - aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb); + ubi_rb_for_each_entry(node, av, &ai->volumes, rb) { + ubi_rb_for_each_entry(node2, aeb, &av->root, u.rb) { if (aeb->pnum == pnum) { rb_erase(&aeb->u.rb, &av->root); av->leb_count--; - kmem_cache_free(ai->aeb_slab_cache, aeb); + ubi_free_aeb(ai, aeb); return; } } @@ -455,6 +411,7 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, __be32 *pebs, int pool_size, unsigned long long *max_sqnum, struct list_head *free) { + struct ubi_vid_io_buf *vb; struct ubi_vid_hdr *vh; struct ubi_ec_hdr *ech; struct ubi_ainf_peb *new_aeb; @@ -464,12 +421,14 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, if (!ech) return -ENOMEM; - vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vh) { + vb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!vb) { kfree(ech); return -ENOMEM; } + vh = ubi_get_vid_hdr(vb); + dbg_bld("scanning fastmap pool: size = %i", pool_size); /* @@ -510,15 +469,16 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, goto out; } - err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + err = ubi_io_read_vid_hdr(ubi, pnum, vb, 0); if (err == UBI_IO_FF || err == UBI_IO_FF_BITFLIPS) { unsigned long long ec = be64_to_cpu(ech->ec); unmap_peb(ai, pnum); dbg_bld("Adding PEB to free: %i", pnum); + if (err == UBI_IO_FF_BITFLIPS) - add_aeb(ai, free, pnum, ec, 1); - else - add_aeb(ai, free, pnum, ec, 0); + scrub = 1; + + add_aeb(ai, free, pnum, ec, scrub); continue; } else if (err == 0 || err == UBI_IO_BITFLIPS) { dbg_bld("Found non empty PEB:%i in pool", pnum); @@ -526,15 +486,12 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, if (err == UBI_IO_BITFLIPS) scrub = 1; - new_aeb = kmem_cache_alloc(ai->aeb_slab_cache, - GFP_KERNEL); + new_aeb = ubi_alloc_aeb(ai, pnum, be64_to_cpu(ech->ec)); if (!new_aeb) { ret = -ENOMEM; goto out; } - new_aeb->ec = be64_to_cpu(ech->ec); - new_aeb->pnum = pnum; new_aeb->lnum = be32_to_cpu(vh->lnum); new_aeb->sqnum = be64_to_cpu(vh->sqnum); new_aeb->copy_flag = vh->copy_flag; @@ -558,7 +515,7 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, } out: - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vb); kfree(ech); return ret; } @@ -841,11 +798,11 @@ fail_bad: fail: list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) { list_del(&tmp_aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + ubi_free_aeb(ai, tmp_aeb); } list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) { list_del(&tmp_aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + ubi_free_aeb(ai, tmp_aeb); } return ret; @@ -886,6 +843,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, struct ubi_attach_info *scan_ai) { struct ubi_fm_sb *fmsb, *fmsb2; + struct ubi_vid_io_buf *vb; struct ubi_vid_hdr *vh; struct ubi_ec_hdr *ech; struct ubi_fastmap_layout *fm; @@ -919,7 +877,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, goto out; } - ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb)); + ret = ubi_io_read_data(ubi, fmsb, fm_anchor, 0, sizeof(*fmsb)); if (ret && ret != UBI_IO_BITFLIPS) goto free_fm_sb; else if (ret == UBI_IO_BITFLIPS) @@ -961,12 +919,14 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, goto free_fm_sb; } - vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vh) { + vb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!vb) { ret = -ENOMEM; goto free_hdr; } + vh = ubi_get_vid_hdr(vb); + for (i = 0; i < used_blocks; i++) { int image_seq; @@ -1009,7 +969,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, goto free_hdr; } - ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + ret = ubi_io_read_vid_hdr(ubi, pnum, vb, 0); if (ret && ret != UBI_IO_BITFLIPS) { ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i)", i, pnum); @@ -1037,8 +997,8 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, if (sqnum < be64_to_cpu(vh->sqnum)) sqnum = be64_to_cpu(vh->sqnum); - ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum, - ubi->leb_start, ubi->leb_size); + ret = ubi_io_read_data(ubi, ubi->fm_buf + (ubi->leb_size * i), + pnum, 0, ubi->leb_size); if (ret && ret != UBI_IO_BITFLIPS) { ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i, " "err: %i)", i, pnum, ret); @@ -1099,7 +1059,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, ubi->fm_disabled = 0; ubi->fast_attach = 1; - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vb); kfree(ech); out: up_write(&ubi->fm_protect); @@ -1108,7 +1068,7 @@ out: return ret; free_hdr: - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vb); kfree(ech); free_fm_sb: kfree(fmsb); @@ -1136,6 +1096,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, struct ubi_fm_eba *feba; struct ubi_wl_entry *wl_e; struct ubi_volume *vol; + struct ubi_vid_io_buf *avbuf, *dvbuf; struct ubi_vid_hdr *avhdr, *dvhdr; struct ubi_work *ubi_wrk; struct rb_node *tmp_rb; @@ -1146,18 +1107,21 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fm_raw = ubi->fm_buf; memset(ubi->fm_buf, 0, ubi->fm_size); - avhdr = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); - if (!avhdr) { + avbuf = new_fm_vbuf(ubi, UBI_FM_SB_VOLUME_ID); + if (!avbuf) { ret = -ENOMEM; goto out; } - dvhdr = new_fm_vhdr(ubi, UBI_FM_DATA_VOLUME_ID); - if (!dvhdr) { + dvbuf = new_fm_vbuf(ubi, UBI_FM_DATA_VOLUME_ID); + if (!dvbuf) { ret = -ENOMEM; goto out_kfree; } + avhdr = ubi_get_vid_hdr(avbuf); + dvhdr = ubi_get_vid_hdr(dvbuf); + seen_pebs = init_seen(ubi); if (IS_ERR(seen_pebs)) { ret = PTR_ERR(seen_pebs); @@ -1306,8 +1270,12 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fm_pos += sizeof(*feba) + (sizeof(__be32) * vol->reserved_pebs); ubi_assert(fm_pos <= ubi->fm_size); - for (j = 0; j < vol->reserved_pebs; j++) - feba->pnum[j] = cpu_to_be32(vol->eba_tbl[j]); + for (j = 0; j < vol->reserved_pebs; j++) { + struct ubi_eba_leb_desc ldesc; + + ubi_eba_get_ldesc(vol, j, &ldesc); + feba->pnum[j] = cpu_to_be32(ldesc.pnum); + } feba->reserved_pebs = cpu_to_be32(j); feba->magic = cpu_to_be32(UBI_FM_EBA_MAGIC); @@ -1322,7 +1290,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, spin_unlock(&ubi->volumes_lock); dbg_bld("writing fastmap SB to PEB %i", new_fm->e[0]->pnum); - ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avhdr); + ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avbuf); if (ret) { ubi_err(ubi, "unable to write vid_hdr to fastmap SB!"); goto out_kfree; @@ -1343,7 +1311,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, dvhdr->lnum = cpu_to_be32(i); dbg_bld("writing fastmap data to PEB %i sqnum %llu", new_fm->e[i]->pnum, be64_to_cpu(dvhdr->sqnum)); - ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvhdr); + ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvbuf); if (ret) { ubi_err(ubi, "unable to write vid_hdr to PEB %i!", new_fm->e[i]->pnum); @@ -1352,8 +1320,8 @@ static int ubi_write_fastmap(struct ubi_device *ubi, } for (i = 0; i < new_fm->used_blocks; i++) { - ret = ubi_io_write(ubi, fm_raw + (i * ubi->leb_size), - new_fm->e[i]->pnum, ubi->leb_start, ubi->leb_size); + ret = ubi_io_write_data(ubi, fm_raw + (i * ubi->leb_size), + new_fm->e[i]->pnum, 0, ubi->leb_size); if (ret) { ubi_err(ubi, "unable to write fastmap to PEB %i!", new_fm->e[i]->pnum); @@ -1368,8 +1336,8 @@ static int ubi_write_fastmap(struct ubi_device *ubi, dbg_bld("fastmap written!"); out_kfree: - ubi_free_vid_hdr(ubi, avhdr); - ubi_free_vid_hdr(ubi, dvhdr); + ubi_free_vid_buf(avbuf); + ubi_free_vid_buf(dvbuf); free_seen(seen_pebs); out: return ret; @@ -1439,7 +1407,8 @@ static int invalidate_fastmap(struct ubi_device *ubi) int ret; struct ubi_fastmap_layout *fm; struct ubi_wl_entry *e; - struct ubi_vid_hdr *vh = NULL; + struct ubi_vid_io_buf *vb = NULL; + struct ubi_vid_hdr *vh; if (!ubi->fm) return 0; @@ -1451,10 +1420,12 @@ static int invalidate_fastmap(struct ubi_device *ubi) if (!fm) goto out; - vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); - if (!vh) + vb = new_fm_vbuf(ubi, UBI_FM_SB_VOLUME_ID); + if (!vb) goto out_free_fm; + vh = ubi_get_vid_hdr(vb); + ret = -ENOSPC; e = ubi_wl_get_fm_peb(ubi, 1); if (!e) @@ -1465,7 +1436,7 @@ static int invalidate_fastmap(struct ubi_device *ubi) * to scanning mode. */ vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh); + ret = ubi_io_write_vid_hdr(ubi, e->pnum, vb); if (ret < 0) { ubi_wl_put_fm_peb(ubi, e, 0, 0); goto out_free_fm; @@ -1477,7 +1448,7 @@ static int invalidate_fastmap(struct ubi_device *ubi) ubi->fm = fm; out: - ubi_free_vid_hdr(ubi, vh); + ubi_free_vid_buf(vb); return ret; out_free_fm: @@ -1522,22 +1493,30 @@ int ubi_update_fastmap(struct ubi_device *ubi) struct ubi_wl_entry *tmp_e; down_write(&ubi->fm_protect); + down_write(&ubi->work_sem); + down_write(&ubi->fm_eba_sem); ubi_refill_pools(ubi); if (ubi->ro_mode || ubi->fm_disabled) { + up_write(&ubi->fm_eba_sem); + up_write(&ubi->work_sem); up_write(&ubi->fm_protect); return 0; } ret = ubi_ensure_anchor_pebs(ubi); if (ret) { + up_write(&ubi->fm_eba_sem); + up_write(&ubi->work_sem); up_write(&ubi->fm_protect); return ret; } new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL); if (!new_fm) { + up_write(&ubi->fm_eba_sem); + up_write(&ubi->work_sem); up_write(&ubi->fm_protect); return -ENOMEM; } @@ -1646,16 +1625,14 @@ int ubi_update_fastmap(struct ubi_device *ubi) new_fm->e[0] = tmp_e; } - down_write(&ubi->work_sem); - down_write(&ubi->fm_eba_sem); ret = ubi_write_fastmap(ubi, new_fm); - up_write(&ubi->fm_eba_sem); - up_write(&ubi->work_sem); if (ret) goto err; out_unlock: + up_write(&ubi->fm_eba_sem); + up_write(&ubi->work_sem); up_write(&ubi->fm_protect); kfree(old_fm); return ret; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index ff8cafe1e5cd..b6fb8f945c21 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -502,6 +502,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum) loff_t addr; uint32_t data = 0; struct ubi_ec_hdr ec_hdr; + struct ubi_vid_io_buf vidb; /* * Note, we cannot generally define VID header buffers on stack, @@ -528,7 +529,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum) goto error; } - err = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0); + ubi_init_vid_buf(ubi, &vidb, &vid_hdr); + ubi_assert(&vid_hdr == ubi_get_vid_hdr(&vidb)); + + err = ubi_io_read_vid_hdr(ubi, pnum, &vidb, 0); if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR && err != UBI_IO_FF){ addr += ubi->vid_hdr_aloffset; @@ -995,12 +999,11 @@ bad: * ubi_io_read_vid_hdr - read and check a volume identifier header. * @ubi: UBI device description object * @pnum: physical eraseblock number to read from - * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume - * identifier header + * @vidb: the volume identifier buffer to store data in * @verbose: be verbose if the header is corrupted or wasn't found * * This function reads the volume identifier header from physical eraseblock - * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read + * @pnum and stores it in @vidb. It also checks CRC checksum of the read * volume identifier header. The error codes are the same as in * 'ubi_io_read_ec_hdr()'. * @@ -1008,16 +1011,16 @@ bad: * 'ubi_io_read_ec_hdr()', so refer commentaries in 'ubi_io_read_ec_hdr()'. */ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, - struct ubi_vid_hdr *vid_hdr, int verbose) + struct ubi_vid_io_buf *vidb, int verbose) { int err, read_err; uint32_t crc, magic, hdr_crc; - void *p; + struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb); + void *p = vidb->buffer; dbg_io("read VID header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - p = (char *)vid_hdr - ubi->vid_hdr_shift; read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_shift + UBI_VID_HDR_SIZE); if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err)) @@ -1080,23 +1083,24 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, * ubi_io_write_vid_hdr - write a volume identifier header. * @ubi: UBI device description object * @pnum: the physical eraseblock number to write to - * @vid_hdr: the volume identifier header to write + * @vidb: the volume identifier buffer to write * * This function writes the volume identifier header described by @vid_hdr to * physical eraseblock @pnum. This function automatically fills the - * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates - * header CRC checksum and stores it at vid_hdr->hdr_crc. + * @vidb->hdr->magic and the @vidb->hdr->version fields, as well as calculates + * header CRC checksum and stores it at vidb->hdr->hdr_crc. * * This function returns zero in case of success and a negative error code in * case of failure. If %-EIO is returned, the physical eraseblock probably went * bad. */ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, - struct ubi_vid_hdr *vid_hdr) + struct ubi_vid_io_buf *vidb) { + struct ubi_vid_hdr *vid_hdr = ubi_get_vid_hdr(vidb); int err; uint32_t crc; - void *p; + void *p = vidb->buffer; dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); @@ -1117,7 +1121,6 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) return -EROFS; - p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); return err; @@ -1283,17 +1286,19 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) { int err; uint32_t crc, hdr_crc; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; void *p; if (!ubi_dbg_chk_io(ubi)) return 0; - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; - p = (char *)vid_hdr - ubi->vid_hdr_shift; + vid_hdr = ubi_get_vid_hdr(vidb); + p = vidb->buffer; err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err)) @@ -1314,7 +1319,7 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) err = self_check_vid_hdr(ubi, pnum, vid_hdr); exit: - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); return err; } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index a9e2cef7c95c..88b1897aeb40 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -538,7 +538,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || + if (!ubi_leb_valid(vol, lnum) || offset < 0 || len < 0 || offset + len > vol->usable_leb_size || offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1)) return -EINVAL; @@ -583,7 +583,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + if (!ubi_leb_valid(vol, lnum) || len < 0 || len > vol->usable_leb_size || len & (ubi->min_io_size - 1)) return -EINVAL; @@ -620,7 +620,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - if (lnum < 0 || lnum >= vol->reserved_pebs) + if (!ubi_leb_valid(vol, lnum)) return -EINVAL; if (vol->upd_marker) @@ -680,7 +680,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - if (lnum < 0 || lnum >= vol->reserved_pebs) + if (!ubi_leb_valid(vol, lnum)) return -EINVAL; if (vol->upd_marker) @@ -716,13 +716,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum) if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - if (lnum < 0 || lnum >= vol->reserved_pebs) + if (!ubi_leb_valid(vol, lnum)) return -EINVAL; if (vol->upd_marker) return -EBADF; - if (vol->eba_tbl[lnum] >= 0) + if (ubi_eba_is_mapped(vol, lnum)) return -EBADMSG; return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); @@ -751,13 +751,13 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) dbg_gen("test LEB %d:%d", vol->vol_id, lnum); - if (lnum < 0 || lnum >= vol->reserved_pebs) + if (!ubi_leb_valid(vol, lnum)) return -EINVAL; if (vol->upd_marker) return -EBADF; - return vol->eba_tbl[lnum] >= 0; + return ubi_eba_is_mapped(vol, lnum); } EXPORT_SYMBOL_GPL(ubi_is_mapped); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index b616a115c9d3..697dbcba7371 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -167,6 +167,17 @@ enum { }; /** + * struct ubi_vid_io_buf - VID buffer used to read/write VID info to/from the + * flash. + * @hdr: a pointer to the VID header stored in buffer + * @buffer: underlying buffer + */ +struct ubi_vid_io_buf { + struct ubi_vid_hdr *hdr; + void *buffer; +}; + +/** * struct ubi_wl_entry - wear-leveling entry. * @u.rb: link in the corresponding (free/used) RB-tree * @u.list: link in the protection queue @@ -267,6 +278,21 @@ struct ubi_fm_pool { }; /** + * struct ubi_eba_leb_desc - EBA logical eraseblock descriptor + * @lnum: the logical eraseblock number + * @pnum: the physical eraseblock where the LEB can be found + * + * This structure is here to hide EBA's internal from other part of the + * UBI implementation. + * + * One can query the position of a LEB by calling ubi_eba_get_ldesc(). + */ +struct ubi_eba_leb_desc { + int lnum; + int pnum; +}; + +/** * struct ubi_volume - UBI volume description data structure. * @dev: device object to make use of the the Linux device model * @cdev: character device object to create character device @@ -344,7 +370,7 @@ struct ubi_volume { long long upd_received; void *upd_buf; - int *eba_tbl; + struct ubi_eba_table *eba_tbl; unsigned int checked:1; unsigned int corrupted:1; unsigned int upd_marker:1; @@ -724,6 +750,8 @@ struct ubi_ainf_volume { * @ec_sum: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec * @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects + * @ech: temporary EC header. Only available during scan + * @vidh: temporary VID buffer. Only available during scan * * This data structure contains the result of attaching an MTD device and may * be used by other UBI sub-systems to build final UBI data structures, further @@ -752,6 +780,8 @@ struct ubi_attach_info { uint64_t ec_sum; int ec_count; struct kmem_cache *aeb_slab_cache; + struct ubi_ec_hdr *ech; + struct ubi_vid_io_buf *vidb; }; /** @@ -792,8 +822,12 @@ extern struct mutex ubi_devices_mutex; extern struct blocking_notifier_head ubi_notifiers; /* attach.c */ +struct ubi_ainf_peb *ubi_alloc_aeb(struct ubi_attach_info *ai, int pnum, + int ec); +void ubi_free_aeb(struct ubi_attach_info *ai, struct ubi_ainf_peb *aeb); int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips); +struct ubi_ainf_volume *ubi_add_av(struct ubi_attach_info *ai, int vol_id); struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, int vol_id); void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av); @@ -835,7 +869,21 @@ void ubi_update_reserved(struct ubi_device *ubi); void ubi_calculate_reserved(struct ubi_device *ubi); int ubi_check_pattern(const void *buf, uint8_t patt, int size); +static inline bool ubi_leb_valid(struct ubi_volume *vol, int lnum) +{ + return lnum >= 0 && lnum < vol->reserved_pebs; +} + /* eba.c */ +struct ubi_eba_table *ubi_eba_create_table(struct ubi_volume *vol, + int nentries); +void ubi_eba_destroy_table(struct ubi_eba_table *tbl); +void ubi_eba_copy_table(struct ubi_volume *vol, struct ubi_eba_table *dst, + int nentries); +void ubi_eba_replace_table(struct ubi_volume *vol, struct ubi_eba_table *tbl); +void ubi_eba_get_ldesc(struct ubi_volume *vol, int lnum, + struct ubi_eba_leb_desc *ldesc); +bool ubi_eba_is_mapped(struct ubi_volume *vol, int lnum); int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum); int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, @@ -850,7 +898,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len); int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, - struct ubi_vid_hdr *vid_hdr); + struct ubi_vid_io_buf *vidb); int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai); unsigned long long ubi_next_sqnum(struct ubi_device *ubi); int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap, @@ -885,9 +933,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, struct ubi_ec_hdr *ec_hdr); int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, - struct ubi_vid_hdr *vid_hdr, int verbose); + struct ubi_vid_io_buf *vidb, int verbose); int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, - struct ubi_vid_hdr *vid_hdr); + struct ubi_vid_io_buf *vidb); /* build.c */ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, @@ -1008,44 +1056,68 @@ static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av, } /** - * ubi_zalloc_vid_hdr - allocate a volume identifier header object. - * @ubi: UBI device description object - * @gfp_flags: GFP flags to allocate with - * - * This function returns a pointer to the newly allocated and zero-filled - * volume identifier header object in case of success and %NULL in case of - * failure. + * ubi_init_vid_buf - Initialize a VID buffer + * @ubi: the UBI device + * @vidb: the VID buffer to initialize + * @buf: the underlying buffer */ -static inline struct ubi_vid_hdr * -ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags) +static inline void ubi_init_vid_buf(const struct ubi_device *ubi, + struct ubi_vid_io_buf *vidb, + void *buf) { - void *vid_hdr; + if (buf) + memset(buf, 0, ubi->vid_hdr_alsize); - vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags); - if (!vid_hdr) + vidb->buffer = buf; + vidb->hdr = buf + ubi->vid_hdr_shift; +} + +/** + * ubi_init_vid_buf - Allocate a VID buffer + * @ubi: the UBI device + * @gfp_flags: GFP flags to use for the allocation + */ +static inline struct ubi_vid_io_buf * +ubi_alloc_vid_buf(const struct ubi_device *ubi, gfp_t gfp_flags) +{ + struct ubi_vid_io_buf *vidb; + void *buf; + + vidb = kzalloc(sizeof(*vidb), gfp_flags); + if (!vidb) + return NULL; + + buf = kmalloc(ubi->vid_hdr_alsize, gfp_flags); + if (!buf) { + kfree(vidb); return NULL; + } - /* - * VID headers may be stored at un-aligned flash offsets, so we shift - * the pointer. - */ - return vid_hdr + ubi->vid_hdr_shift; + ubi_init_vid_buf(ubi, vidb, buf); + + return vidb; } /** - * ubi_free_vid_hdr - free a volume identifier header object. - * @ubi: UBI device description object - * @vid_hdr: the object to free + * ubi_free_vid_buf - Free a VID buffer + * @vidb: the VID buffer to free */ -static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, - struct ubi_vid_hdr *vid_hdr) +static inline void ubi_free_vid_buf(struct ubi_vid_io_buf *vidb) { - void *p = vid_hdr; - - if (!p) + if (!vidb) return; - kfree(p - ubi->vid_hdr_shift); + kfree(vidb->buffer); + kfree(vidb); +} + +/** + * ubi_get_vid_hdr - Get the VID header attached to a VID buffer + * @vidb: VID buffer + */ +static inline struct ubi_vid_hdr *ubi_get_vid_hdr(struct ubi_vid_io_buf *vidb) +{ + return vidb->hdr; } /* diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 0138f526474a..7ac78c13dd1c 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -138,7 +138,7 @@ static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); - kfree(vol->eba_tbl); + ubi_eba_replace_table(vol, NULL); kfree(vol); } @@ -158,6 +158,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) int i, err, vol_id = req->vol_id, do_free = 1; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; + struct ubi_eba_table *eba_tbl = NULL; dev_t dev; if (ubi->ro_mode) @@ -241,14 +242,13 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) if (err) goto out_acc; - vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); - if (!vol->eba_tbl) { - err = -ENOMEM; + eba_tbl = ubi_eba_create_table(vol, vol->reserved_pebs); + if (IS_ERR(eba_tbl)) { + err = PTR_ERR(eba_tbl); goto out_acc; } - for (i = 0; i < vol->reserved_pebs; i++) - vol->eba_tbl[i] = UBI_LEB_UNMAPPED; + ubi_eba_replace_table(vol, eba_tbl); if (vol->vol_type == UBI_DYNAMIC_VOLUME) { vol->used_ebs = vol->reserved_pebs; @@ -329,7 +329,7 @@ out_cdev: cdev_del(&vol->cdev); out_mapping: if (do_free) - kfree(vol->eba_tbl); + ubi_eba_destroy_table(eba_tbl); out_acc: spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; @@ -427,10 +427,11 @@ out_unlock: */ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) { - int i, err, pebs, *new_mapping; + int i, err, pebs; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; struct ubi_vtbl_record vtbl_rec; + struct ubi_eba_table *new_eba_tbl = NULL; int vol_id = vol->vol_id; if (ubi->ro_mode) @@ -450,12 +451,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (reserved_pebs == vol->reserved_pebs) return 0; - new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); - if (!new_mapping) - return -ENOMEM; - - for (i = 0; i < reserved_pebs; i++) - new_mapping[i] = UBI_LEB_UNMAPPED; + new_eba_tbl = ubi_eba_create_table(vol, reserved_pebs); + if (IS_ERR(new_eba_tbl)) + return PTR_ERR(new_eba_tbl); spin_lock(&ubi->volumes_lock); if (vol->ref_count > 1) { @@ -481,10 +479,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) } ubi->avail_pebs -= pebs; ubi->rsvd_pebs += pebs; - for (i = 0; i < vol->reserved_pebs; i++) - new_mapping[i] = vol->eba_tbl[i]; - kfree(vol->eba_tbl); - vol->eba_tbl = new_mapping; + ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs); + ubi_eba_replace_table(vol, new_eba_tbl); spin_unlock(&ubi->volumes_lock); } @@ -498,10 +494,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) ubi->rsvd_pebs += pebs; ubi->avail_pebs -= pebs; ubi_update_reserved(ubi); - for (i = 0; i < reserved_pebs; i++) - new_mapping[i] = vol->eba_tbl[i]; - kfree(vol->eba_tbl); - vol->eba_tbl = new_mapping; + ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs); + ubi_eba_replace_table(vol, new_eba_tbl); spin_unlock(&ubi->volumes_lock); } @@ -543,7 +537,7 @@ out_acc: spin_unlock(&ubi->volumes_lock); } out_free: - kfree(new_mapping); + kfree(new_eba_tbl); return err; } diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index d85c19762160..263743e7b741 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -299,15 +299,18 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, int copy, void *vtbl) { int err, tries = 0; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; struct ubi_ainf_peb *new_aeb; dbg_gen("create volume table (copy #%d)", copy + 1); - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + retry: new_aeb = ubi_early_get_peb(ubi, ai); if (IS_ERR(new_aeb)) { @@ -324,7 +327,7 @@ retry: vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum); /* The EC header is already there, write the VID header */ - err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr); + err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vidb); if (err) goto write_error; @@ -338,8 +341,8 @@ retry: * of this LEB as it will be deleted and freed in 'ubi_add_to_av()'. */ err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0); - kmem_cache_free(ai->aeb_slab_cache, new_aeb); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_aeb(ai, new_aeb); + ubi_free_vid_buf(vidb); return err; write_error: @@ -351,9 +354,9 @@ write_error: list_add(&new_aeb->u.list, &ai->erase); goto retry; } - kmem_cache_free(ai->aeb_slab_cache, new_aeb); + ubi_free_aeb(ai, new_aeb); out_free: - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); return err; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index f4533266d7b2..b5b8cd6f481c 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -580,7 +580,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, * failure. */ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, - int vol_id, int lnum, int torture) + int vol_id, int lnum, int torture, bool nested) { struct ubi_work *wl_wrk; @@ -599,7 +599,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, wl_wrk->lnum = lnum; wl_wrk->torture = torture; - schedule_ubi_work(ubi, wl_wrk); + if (nested) + __schedule_ubi_work(ubi, wl_wrk); + else + schedule_ubi_work(ubi, wl_wrk); return 0; } @@ -644,11 +647,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int shutdown) { int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; - int vol_id = -1, lnum = -1; + int erase = 0, keep = 0, vol_id = -1, lnum = -1; #ifdef CONFIG_MTD_UBI_FASTMAP int anchor = wrk->anchor; #endif struct ubi_wl_entry *e1, *e2; + struct ubi_vid_io_buf *vidb; struct ubi_vid_hdr *vid_hdr; int dst_leb_clean = 0; @@ -656,10 +660,13 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (shutdown) return 0; - vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) + vidb = ubi_alloc_vid_buf(ubi, GFP_NOFS); + if (!vidb) return -ENOMEM; + vid_hdr = ubi_get_vid_hdr(vidb); + + down_read(&ubi->fm_eba_sem); mutex_lock(&ubi->move_mutex); spin_lock(&ubi->wl_lock); ubi_assert(!ubi->move_from && !ubi->move_to); @@ -753,7 +760,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * which is being moved was unmapped. */ - err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vidb, 0); if (err && err != UBI_IO_BITFLIPS) { dst_leb_clean = 1; if (err == UBI_IO_FF) { @@ -780,6 +787,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, e1->pnum); scrubbing = 1; goto out_not_moved; + } else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) { + /* + * While a full scan would detect interrupted erasures + * at attach time we can face them here when attached from + * Fastmap. + */ + dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure", + e1->pnum); + erase = 1; + goto out_not_moved; } ubi_err(ubi, "error %d while reading VID header from PEB %d", @@ -790,7 +807,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); - err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vidb); if (err) { if (err == MOVE_CANCEL_RACE) { /* @@ -815,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * Target PEB had bit-flips or write error - torture it. */ torture = 1; + keep = 1; goto out_not_moved; } @@ -847,7 +865,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (scrubbing) ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", e1->pnum, vol_id, lnum, e2->pnum); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); spin_lock(&ubi->wl_lock); if (!ubi->move_to_put) { @@ -879,6 +897,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, dbg_wl("done"); mutex_unlock(&ubi->move_mutex); + up_read(&ubi->fm_eba_sem); return 0; /* @@ -901,7 +920,7 @@ out_not_moved: ubi->erroneous_peb_count += 1; } else if (scrubbing) wl_tree_add(e1, &ubi->scrub); - else + else if (keep) wl_tree_add(e1, &ubi->used); if (dst_leb_clean) { wl_tree_add(e2, &ubi->free); @@ -913,7 +932,7 @@ out_not_moved: ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); if (dst_leb_clean) { ensure_wear_leveling(ubi, 1); } else { @@ -922,7 +941,14 @@ out_not_moved: goto out_ro; } + if (erase) { + err = do_sync_erase(ubi, e1, vol_id, lnum, 1); + if (err) + goto out_ro; + } + mutex_unlock(&ubi->move_mutex); + up_read(&ubi->fm_eba_sem); return 0; out_error: @@ -937,13 +963,14 @@ out_error: ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); + ubi_free_vid_buf(vidb); wl_entry_destroy(ubi, e1); wl_entry_destroy(ubi, e2); out_ro: ubi_ro_mode(ubi); mutex_unlock(&ubi->move_mutex); + up_read(&ubi->fm_eba_sem); ubi_assert(err != 0); return err < 0 ? err : -EIO; @@ -951,7 +978,8 @@ out_cancel: ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); mutex_unlock(&ubi->move_mutex); - ubi_free_vid_hdr(ubi, vid_hdr); + up_read(&ubi->fm_eba_sem); + ubi_free_vid_buf(vidb); return 0; } @@ -1073,7 +1101,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk) int err1; /* Re-schedule the LEB for erasure */ - err1 = schedule_erase(ubi, e, vol_id, lnum, 0); + err1 = schedule_erase(ubi, e, vol_id, lnum, 0, false); if (err1) { wl_entry_destroy(ubi, e); err = err1; @@ -1254,7 +1282,7 @@ retry: } spin_unlock(&ubi->wl_lock); - err = schedule_erase(ubi, e, vol_id, lnum, torture); + err = schedule_erase(ubi, e, vol_id, lnum, torture, false); if (err) { spin_lock(&ubi->wl_lock); wl_tree_add(e, &ubi->used); @@ -1545,7 +1573,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; ubi->lookuptbl[e->pnum] = e; - if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { + if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0, false)) { wl_entry_destroy(ubi, e); goto out_free; } @@ -1624,7 +1652,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->ec = aeb->ec; ubi_assert(!ubi->lookuptbl[e->pnum]); ubi->lookuptbl[e->pnum] = e; - if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { + if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0, false)) { wl_entry_destroy(ubi, e); goto out_free; } diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig index 124c2432ac9c..8b2b740d6679 100644 --- a/drivers/nvdimm/Kconfig +++ b/drivers/nvdimm/Kconfig @@ -89,7 +89,7 @@ config NVDIMM_PFN Select Y if unsure config NVDIMM_DAX - bool "NVDIMM DAX: Raw access to persistent memory" + tristate "NVDIMM DAX: Raw access to persistent memory" default LIBNVDIMM depends on NVDIMM_PFN help diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 935866fe5ec2..a8b6949a8778 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -217,6 +217,8 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, return rc; if (cmd_rc < 0) return cmd_rc; + + nvdimm_clear_from_poison_list(nvdimm_bus, phys, len); return clear_err.cleared; } EXPORT_SYMBOL_GPL(nvdimm_clear_poison); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 4d7bbd2df5c0..7ceba08774b6 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -547,11 +547,12 @@ void nvdimm_badblocks_populate(struct nd_region *nd_region, } EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); -static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) +static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length, + gfp_t flags) { struct nd_poison *pl; - pl = kzalloc(sizeof(*pl), GFP_KERNEL); + pl = kzalloc(sizeof(*pl), flags); if (!pl) return -ENOMEM; @@ -567,7 +568,7 @@ static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) struct nd_poison *pl; if (list_empty(&nvdimm_bus->poison_list)) - return add_poison(nvdimm_bus, addr, length); + return add_poison(nvdimm_bus, addr, length, GFP_KERNEL); /* * There is a chance this is a duplicate, check for those first. @@ -587,7 +588,7 @@ static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) * as any overlapping ranges will get resolved when the list is consumed * and converted to badblocks */ - return add_poison(nvdimm_bus, addr, length); + return add_poison(nvdimm_bus, addr, length, GFP_KERNEL); } int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) @@ -602,6 +603,70 @@ int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) } EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); +void nvdimm_clear_from_poison_list(struct nvdimm_bus *nvdimm_bus, + phys_addr_t start, unsigned int len) +{ + struct list_head *poison_list = &nvdimm_bus->poison_list; + u64 clr_end = start + len - 1; + struct nd_poison *pl, *next; + + nvdimm_bus_lock(&nvdimm_bus->dev); + WARN_ON_ONCE(list_empty(poison_list)); + + /* + * [start, clr_end] is the poison interval being cleared. + * [pl->start, pl_end] is the poison_list entry we're comparing + * the above interval against. The poison list entry may need + * to be modified (update either start or length), deleted, or + * split into two based on the overlap characteristics + */ + + list_for_each_entry_safe(pl, next, poison_list, list) { + u64 pl_end = pl->start + pl->length - 1; + + /* Skip intervals with no intersection */ + if (pl_end < start) + continue; + if (pl->start > clr_end) + continue; + /* Delete completely overlapped poison entries */ + if ((pl->start >= start) && (pl_end <= clr_end)) { + list_del(&pl->list); + kfree(pl); + continue; + } + /* Adjust start point of partially cleared entries */ + if ((start <= pl->start) && (clr_end > pl->start)) { + pl->length -= clr_end - pl->start + 1; + pl->start = clr_end + 1; + continue; + } + /* Adjust pl->length for partial clearing at the tail end */ + if ((pl->start < start) && (pl_end <= clr_end)) { + /* pl->start remains the same */ + pl->length = start - pl->start; + continue; + } + /* + * If clearing in the middle of an entry, we split it into + * two by modifying the current entry to represent one half of + * the split, and adding a new entry for the second half. + */ + if ((pl->start < start) && (pl_end > clr_end)) { + u64 new_start = clr_end + 1; + u64 new_len = pl_end - new_start + 1; + + /* Add new entry covering the right half */ + add_poison(nvdimm_bus, new_start, new_len, GFP_NOIO); + /* Adjust this entry to cover the left half */ + pl->length = start - pl->start; + continue; + } + } + nvdimm_bus_unlock(&nvdimm_bus->dev); +} +EXPORT_SYMBOL_GPL(nvdimm_clear_from_poison_list); + #ifdef CONFIG_BLK_DEV_INTEGRITY int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) { diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 71d12bb67339..619834e144d1 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -26,6 +26,14 @@ static int nvdimm_probe(struct device *dev) struct nvdimm_drvdata *ndd; int rc; + rc = nvdimm_check_config_data(dev); + if (rc) { + /* not required for non-aliased nvdimm, ex. NVDIMM-N */ + if (rc == -ENOTTY) + rc = 0; + return rc; + } + ndd = kzalloc(sizeof(*ndd), GFP_KERNEL); if (!ndd) return -ENOMEM; @@ -72,6 +80,9 @@ static int nvdimm_remove(struct device *dev) { struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); + if (!ndd) + return 0; + nvdimm_bus_lock(dev); dev_set_drvdata(dev, NULL); nvdimm_bus_unlock(dev); diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index d9bba5edd8dc..d614493ad5ac 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -28,28 +28,30 @@ static DEFINE_IDA(dimm_ida); * Retrieve bus and dimm handle and return if this bus supports * get_config_data commands */ -static int __validate_dimm(struct nvdimm_drvdata *ndd) +int nvdimm_check_config_data(struct device *dev) { - struct nvdimm *nvdimm; - - if (!ndd) - return -EINVAL; - - nvdimm = to_nvdimm(ndd->dev); + struct nvdimm *nvdimm = to_nvdimm(dev); - if (!nvdimm->cmd_mask) - return -ENXIO; - if (!test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) - return -ENXIO; + if (!nvdimm->cmd_mask || + !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { + if (nvdimm->flags & NDD_ALIASING) + return -ENXIO; + else + return -ENOTTY; + } return 0; } static int validate_dimm(struct nvdimm_drvdata *ndd) { - int rc = __validate_dimm(ndd); + int rc; - if (rc && ndd) + if (!ndd) + return -EINVAL; + + rc = nvdimm_check_config_data(ndd->dev); + if (rc) dev_dbg(ndd->dev, "%pf: %s error: %d\n", __builtin_return_address(0), __func__, rc); return rc; @@ -263,6 +265,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm) } EXPORT_SYMBOL_GPL(nvdimm_name); +struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) +{ + return &nvdimm->dev.kobj; +} +EXPORT_SYMBOL_GPL(nvdimm_kobj); + unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) { return nvdimm->cmd_mask; @@ -378,40 +386,166 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, } EXPORT_SYMBOL_GPL(nvdimm_create); +int alias_dpa_busy(struct device *dev, void *data) +{ + resource_size_t map_end, blk_start, new, busy; + struct blk_alloc_info *info = data; + struct nd_mapping *nd_mapping; + struct nd_region *nd_region; + struct nvdimm_drvdata *ndd; + struct resource *res; + int i; + + if (!is_nd_pmem(dev)) + return 0; + + nd_region = to_nd_region(dev); + for (i = 0; i < nd_region->ndr_mappings; i++) { + nd_mapping = &nd_region->mapping[i]; + if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) + break; + } + + if (i >= nd_region->ndr_mappings) + return 0; + + ndd = to_ndd(nd_mapping); + map_end = nd_mapping->start + nd_mapping->size - 1; + blk_start = nd_mapping->start; + + /* + * In the allocation case ->res is set to free space that we are + * looking to validate against PMEM aliasing collision rules + * (i.e. BLK is allocated after all aliased PMEM). + */ + if (info->res) { + if (info->res->start >= nd_mapping->start + && info->res->start < map_end) + /* pass */; + else + return 0; + } + + retry: + /* + * Find the free dpa from the end of the last pmem allocation to + * the end of the interleave-set mapping that is not already + * covered by a blk allocation. + */ + busy = 0; + for_each_dpa_resource(ndd, res) { + if ((res->start >= blk_start && res->start < map_end) + || (res->end >= blk_start + && res->end <= map_end)) { + if (strncmp(res->name, "pmem", 4) == 0) { + new = max(blk_start, min(map_end + 1, + res->end + 1)); + if (new != blk_start) { + blk_start = new; + goto retry; + } + } else + busy += min(map_end, res->end) + - max(nd_mapping->start, res->start) + 1; + } else if (nd_mapping->start > res->start + && map_end < res->end) { + /* total eclipse of the PMEM region mapping */ + busy += nd_mapping->size; + break; + } + } + + /* update the free space range with the probed blk_start */ + if (info->res && blk_start > info->res->start) { + info->res->start = max(info->res->start, blk_start); + if (info->res->start > info->res->end) + info->res->end = info->res->start - 1; + return 1; + } + + info->available -= blk_start - nd_mapping->start + busy; + + return 0; +} + +static int blk_dpa_busy(struct device *dev, void *data) +{ + struct blk_alloc_info *info = data; + struct nd_mapping *nd_mapping; + struct nd_region *nd_region; + resource_size_t map_end; + int i; + + if (!is_nd_pmem(dev)) + return 0; + + nd_region = to_nd_region(dev); + for (i = 0; i < nd_region->ndr_mappings; i++) { + nd_mapping = &nd_region->mapping[i]; + if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) + break; + } + + if (i >= nd_region->ndr_mappings) + return 0; + + map_end = nd_mapping->start + nd_mapping->size - 1; + if (info->res->start >= nd_mapping->start + && info->res->start < map_end) { + if (info->res->end <= map_end) { + info->busy = 0; + return 1; + } else { + info->busy -= info->res->end - map_end; + return 0; + } + } else if (info->res->end >= nd_mapping->start + && info->res->end <= map_end) { + info->busy -= nd_mapping->start - info->res->start; + return 0; + } else { + info->busy -= nd_mapping->size; + return 0; + } +} + /** * nd_blk_available_dpa - account the unused dpa of BLK region * @nd_mapping: container of dpa-resource-root + labels * - * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges. + * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but + * we arrange for them to never start at an lower dpa than the last + * PMEM allocation in an aliased region. */ -resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping) +resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) { + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); - resource_size_t map_end, busy = 0, available; + struct blk_alloc_info info = { + .nd_mapping = nd_mapping, + .available = nd_mapping->size, + .res = NULL, + }; struct resource *res; if (!ndd) return 0; - map_end = nd_mapping->start + nd_mapping->size - 1; - for_each_dpa_resource(ndd, res) - if (res->start >= nd_mapping->start && res->start < map_end) { - resource_size_t end = min(map_end, res->end); + device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); - busy += end - res->start + 1; - } else if (res->end >= nd_mapping->start - && res->end <= map_end) { - busy += res->end - nd_mapping->start; - } else if (nd_mapping->start > res->start - && nd_mapping->start < res->end) { - /* total eclipse of the BLK region mapping */ - busy += nd_mapping->size; - } + /* now account for busy blk allocations in unaliased dpa */ + for_each_dpa_resource(ndd, res) { + if (strncmp(res->name, "blk", 3) != 0) + continue; - available = map_end - nd_mapping->start + 1; - if (busy < available) - return available - busy; - return 0; + info.res = res; + info.busy = resource_size(res); + device_for_each_child(&nvdimm_bus->dev, &info, blk_dpa_busy); + info.available -= info.busy; + } + + return info.available; } /** @@ -443,21 +577,16 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, map_start = nd_mapping->start; map_end = map_start + nd_mapping->size - 1; blk_start = max(map_start, map_end + 1 - *overlap); - for_each_dpa_resource(ndd, res) + for_each_dpa_resource(ndd, res) { if (res->start >= map_start && res->start < map_end) { if (strncmp(res->name, "blk", 3) == 0) - blk_start = min(blk_start, res->start); - else if (res->start != map_start) { + blk_start = min(blk_start, + max(map_start, res->start)); + else if (res->end > map_end) { reason = "misaligned to iset"; goto err; - } else { - if (busy) { - reason = "duplicate overlapping PMEM reservations?"; - goto err; - } + } else busy += resource_size(res); - continue; - } } else if (res->end >= map_start && res->end <= map_end) { if (strncmp(res->name, "blk", 3) == 0) { /* @@ -466,15 +595,14 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, * be used for BLK. */ blk_start = map_start; - } else { - reason = "misaligned to iset"; - goto err; - } + } else + busy += resource_size(res); } else if (map_start > res->start && map_start < res->end) { /* total eclipse of the mapping */ busy += nd_mapping->size; blk_start = map_start; } + } *overlap = map_end + 1 - blk_start; available = blk_start - map_start; @@ -483,10 +611,6 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, return 0; err: - /* - * Something is wrong, PMEM must align with the start of the - * interleave set, and there can only be one allocation per set. - */ nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); return 0; } diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 96526dcfdd37..fac7cabe8f56 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -494,11 +494,13 @@ static int __pmem_label_update(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, int pos) { - u64 cookie = nd_region_interleave_set_cookie(nd_region), rawsize; + u64 cookie = nd_region_interleave_set_cookie(nd_region); struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); - struct nd_namespace_label *victim_label; + struct nd_label_ent *label_ent, *victim = NULL; struct nd_namespace_label *nd_label; struct nd_namespace_index *nsindex; + struct nd_label_id label_id; + struct resource *res; unsigned long *free; u32 nslot, slot; size_t offset; @@ -507,6 +509,16 @@ static int __pmem_label_update(struct nd_region *nd_region, if (!preamble_next(ndd, &nsindex, &free, &nslot)) return -ENXIO; + nd_label_gen_id(&label_id, nspm->uuid, 0); + for_each_dpa_resource(ndd, res) + if (strcmp(res->name, label_id.id) == 0) + break; + + if (!res) { + WARN_ON_ONCE(1); + return -ENXIO; + } + /* allocate and write the label to the staging (next) index */ slot = nd_label_alloc_slot(ndd); if (slot == UINT_MAX) @@ -522,11 +534,10 @@ static int __pmem_label_update(struct nd_region *nd_region, nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); nd_label->position = __cpu_to_le16(pos); nd_label->isetcookie = __cpu_to_le64(cookie); - rawsize = div_u64(resource_size(&nspm->nsio.res), - nd_region->ndr_mappings); - nd_label->rawsize = __cpu_to_le64(rawsize); - nd_label->dpa = __cpu_to_le64(nd_mapping->start); + nd_label->rawsize = __cpu_to_le64(resource_size(res)); + nd_label->dpa = __cpu_to_le64(res->start); nd_label->slot = __cpu_to_le32(slot); + nd_dbg_dpa(nd_region, ndd, res, "%s\n", __func__); /* update label */ offset = nd_label_offset(ndd, nd_label); @@ -536,38 +547,43 @@ static int __pmem_label_update(struct nd_region *nd_region, return rc; /* Garbage collect the previous label */ - victim_label = nd_mapping->labels[0]; - if (victim_label) { - slot = to_slot(ndd, victim_label); - nd_label_free_slot(ndd, slot); + mutex_lock(&nd_mapping->lock); + list_for_each_entry(label_ent, &nd_mapping->labels, list) { + if (!label_ent->label) + continue; + if (memcmp(nspm->uuid, label_ent->label->uuid, + NSLABEL_UUID_LEN) != 0) + continue; + victim = label_ent; + list_move_tail(&victim->list, &nd_mapping->labels); + break; + } + if (victim) { dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); + slot = to_slot(ndd, victim->label); + nd_label_free_slot(ndd, slot); + victim->label = NULL; } /* update index */ rc = nd_label_write_index(ndd, ndd->ns_next, nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); - if (rc < 0) - return rc; - - nd_mapping->labels[0] = nd_label; - - return 0; -} - -static void del_label(struct nd_mapping *nd_mapping, int l) -{ - struct nd_namespace_label *next_label, *nd_label; - struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); - unsigned int slot; - int j; - - nd_label = nd_mapping->labels[l]; - slot = to_slot(ndd, nd_label); - dev_vdbg(ndd->dev, "%s: clear: %d\n", __func__, slot); + if (rc == 0) { + list_for_each_entry(label_ent, &nd_mapping->labels, list) + if (!label_ent->label) { + label_ent->label = nd_label; + nd_label = NULL; + break; + } + dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, + "failed to track label: %d\n", + to_slot(ndd, nd_label)); + if (nd_label) + rc = -ENXIO; + } + mutex_unlock(&nd_mapping->lock); - for (j = l; (next_label = nd_mapping->labels[j + 1]); j++) - nd_mapping->labels[j] = next_label; - nd_mapping->labels[j] = NULL; + return rc; } static bool is_old_resource(struct resource *res, struct resource **list, int n) @@ -607,14 +623,16 @@ static int __blk_label_update(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, int num_labels) { - int i, l, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; + int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct nd_namespace_label *nd_label; + struct nd_label_ent *label_ent, *e; struct nd_namespace_index *nsindex; unsigned long *free, *victim_map = NULL; struct resource *res, **old_res_list; struct nd_label_id label_id; u8 uuid[NSLABEL_UUID_LEN]; + LIST_HEAD(list); u32 nslot, slot; if (!preamble_next(ndd, &nsindex, &free, &nslot)) @@ -736,15 +754,22 @@ static int __blk_label_update(struct nd_region *nd_region, * entries in nd_mapping->labels */ nlabel = 0; - for_each_label(l, nd_label, nd_mapping->labels) { + mutex_lock(&nd_mapping->lock); + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + nd_label = label_ent->label; + if (!nd_label) + continue; nlabel++; memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) continue; nlabel--; - del_label(nd_mapping, l); - l--; /* retry with the new label at this index */ + list_move(&label_ent->list, &list); + label_ent->label = NULL; } + list_splice_tail_init(&list, &nd_mapping->labels); + mutex_unlock(&nd_mapping->lock); + if (nlabel + nsblk->num_resources > num_labels) { /* * Bug, we can't end up with more resources than @@ -755,6 +780,15 @@ static int __blk_label_update(struct nd_region *nd_region, goto out; } + mutex_lock(&nd_mapping->lock); + label_ent = list_first_entry_or_null(&nd_mapping->labels, + typeof(*label_ent), list); + if (!label_ent) { + WARN_ON(1); + mutex_unlock(&nd_mapping->lock); + rc = -ENXIO; + goto out; + } for_each_clear_bit_le(slot, free, nslot) { nd_label = nd_label_base(ndd) + slot; memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); @@ -762,11 +796,19 @@ static int __blk_label_update(struct nd_region *nd_region, continue; res = to_resource(ndd, nd_label); res->flags &= ~DPA_RESOURCE_ADJUSTED; - dev_vdbg(&nsblk->common.dev, "assign label[%d] slot: %d\n", - l, slot); - nd_mapping->labels[l++] = nd_label; + dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); + list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { + if (label_ent->label) + continue; + label_ent->label = nd_label; + nd_label = NULL; + break; + } + if (nd_label) + dev_WARN(&nsblk->common.dev, + "failed to track label slot%d\n", slot); } - nd_mapping->labels[l] = NULL; + mutex_unlock(&nd_mapping->lock); out: kfree(old_res_list); @@ -788,32 +830,28 @@ static int __blk_label_update(struct nd_region *nd_region, static int init_labels(struct nd_mapping *nd_mapping, int num_labels) { - int i, l, old_num_labels = 0; + int i, old_num_labels = 0; + struct nd_label_ent *label_ent; struct nd_namespace_index *nsindex; - struct nd_namespace_label *nd_label; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); - size_t size = (num_labels + 1) * sizeof(struct nd_namespace_label *); - for_each_label(l, nd_label, nd_mapping->labels) + mutex_lock(&nd_mapping->lock); + list_for_each_entry(label_ent, &nd_mapping->labels, list) old_num_labels++; + mutex_unlock(&nd_mapping->lock); /* * We need to preserve all the old labels for the mapping so * they can be garbage collected after writing the new labels. */ - if (num_labels > old_num_labels) { - struct nd_namespace_label **labels; - - labels = krealloc(nd_mapping->labels, size, GFP_KERNEL); - if (!labels) + for (i = old_num_labels; i < num_labels; i++) { + label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); + if (!label_ent) return -ENOMEM; - nd_mapping->labels = labels; + mutex_lock(&nd_mapping->lock); + list_add_tail(&label_ent->list, &nd_mapping->labels); + mutex_unlock(&nd_mapping->lock); } - if (!nd_mapping->labels) - return -ENOMEM; - - for (i = old_num_labels; i <= num_labels; i++) - nd_mapping->labels[i] = NULL; if (ndd->ns_current == -1 || ndd->ns_next == -1) /* pass */; @@ -837,42 +875,45 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels) static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) { struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); - struct nd_namespace_label *nd_label; + struct nd_label_ent *label_ent, *e; struct nd_namespace_index *nsindex; u8 label_uuid[NSLABEL_UUID_LEN]; - int l, num_freed = 0; unsigned long *free; + LIST_HEAD(list); u32 nslot, slot; + int active = 0; if (!uuid) return 0; /* no index || no labels == nothing to delete */ - if (!preamble_next(ndd, &nsindex, &free, &nslot) - || !nd_mapping->labels) + if (!preamble_next(ndd, &nsindex, &free, &nslot)) return 0; - for_each_label(l, nd_label, nd_mapping->labels) { + mutex_lock(&nd_mapping->lock); + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + struct nd_namespace_label *nd_label = label_ent->label; + + if (!nd_label) + continue; + active++; memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) continue; + active--; slot = to_slot(ndd, nd_label); nd_label_free_slot(ndd, slot); dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); - del_label(nd_mapping, l); - num_freed++; - l--; /* retry with new label at this index */ + list_move_tail(&label_ent->list, &list); + label_ent->label = NULL; } + list_splice_tail_init(&list, &nd_mapping->labels); - if (num_freed > l) { - /* - * num_freed will only ever be > l when we delete the last - * label - */ - kfree(nd_mapping->labels); - nd_mapping->labels = NULL; - dev_dbg(ndd->dev, "%s: no more labels\n", __func__); + if (active == 0) { + nd_mapping_free_labels(nd_mapping); + dev_dbg(ndd->dev, "%s: no more active labels\n", __func__); } + mutex_unlock(&nd_mapping->lock); return nd_label_write_index(ndd, ndd->ns_next, nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); @@ -885,7 +926,9 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - int rc; + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); + struct resource *res; + int rc, count = 0; if (size == 0) { rc = del_labels(nd_mapping, nspm->uuid); @@ -894,7 +937,12 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, continue; } - rc = init_labels(nd_mapping, 1); + for_each_dpa_resource(ndd, res) + if (strncmp(res->name, "pmem", 3) == 0) + count++; + WARN_ON_ONCE(!count); + + rc = init_labels(nd_mapping, count); if (rc < 0) return rc; diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index c5e3196c45b0..3509cff68ef9 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -12,8 +12,10 @@ */ #include <linux/module.h> #include <linux/device.h> +#include <linux/sort.h> #include <linux/slab.h> #include <linux/pmem.h> +#include <linux/list.h> #include <linux/nd.h> #include "nd-core.h" #include "nd.h" @@ -28,7 +30,10 @@ static void namespace_io_release(struct device *dev) static void namespace_pmem_release(struct device *dev) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); + struct nd_region *nd_region = to_nd_region(dev->parent); + if (nspm->id >= 0) + ida_simple_remove(&nd_region->ns_ida, nspm->id); kfree(nspm->alt_name); kfree(nspm->uuid); kfree(nspm); @@ -62,17 +67,17 @@ static struct device_type namespace_blk_device_type = { .release = namespace_blk_release, }; -static bool is_namespace_pmem(struct device *dev) +static bool is_namespace_pmem(const struct device *dev) { return dev ? dev->type == &namespace_pmem_device_type : false; } -static bool is_namespace_blk(struct device *dev) +static bool is_namespace_blk(const struct device *dev) { return dev ? dev->type == &namespace_blk_device_type : false; } -static bool is_namespace_io(struct device *dev) +static bool is_namespace_io(const struct device *dev) { return dev ? dev->type == &namespace_io_device_type : false; } @@ -168,7 +173,21 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, suffix = "s"; if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) { - sprintf(name, "pmem%d%s", nd_region->id, suffix ? suffix : ""); + int nsidx = 0; + + if (is_namespace_pmem(&ndns->dev)) { + struct nd_namespace_pmem *nspm; + + nspm = to_nd_namespace_pmem(&ndns->dev); + nsidx = nspm->id; + } + + if (nsidx) + sprintf(name, "pmem%d.%d%s", nd_region->id, nsidx, + suffix ? suffix : ""); + else + sprintf(name, "pmem%d%s", nd_region->id, + suffix ? suffix : ""); } else if (is_namespace_blk(&ndns->dev)) { struct nd_namespace_blk *nsblk; @@ -294,7 +313,7 @@ static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk) if (strcmp(res->name, label_id.id) != 0) continue; /* - * Resources with unacknoweldged adjustments indicate a + * Resources with unacknowledged adjustments indicate a * failure to update labels */ if (res->flags & DPA_RESOURCE_ADJUSTED) @@ -510,19 +529,68 @@ static resource_size_t init_dpa_allocation(struct nd_label_id *label_id, return rc ? n : 0; } -static bool space_valid(bool is_pmem, bool is_reserve, - struct nd_label_id *label_id, struct resource *res) + +/** + * space_valid() - validate free dpa space against constraints + * @nd_region: hosting region of the free space + * @ndd: dimm device data for debug + * @label_id: namespace id to allocate space + * @prev: potential allocation that precedes free space + * @next: allocation that follows the given free space range + * @exist: first allocation with same id in the mapping + * @n: range that must satisfied for pmem allocations + * @valid: free space range to validate + * + * BLK-space is valid as long as it does not precede a PMEM + * allocation in a given region. PMEM-space must be contiguous + * and adjacent to an existing existing allocation (if one + * exists). If reserving PMEM any space is valid. + */ +static void space_valid(struct nd_region *nd_region, struct nvdimm_drvdata *ndd, + struct nd_label_id *label_id, struct resource *prev, + struct resource *next, struct resource *exist, + resource_size_t n, struct resource *valid) { - /* - * For BLK-space any space is valid, for PMEM-space, it must be - * contiguous with an existing allocation unless we are - * reserving pmem. - */ - if (is_reserve || !is_pmem) - return true; - if (!res || strcmp(res->name, label_id->id) == 0) - return true; - return false; + bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; + bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0; + + if (valid->start >= valid->end) + goto invalid; + + if (is_reserve) + return; + + if (!is_pmem) { + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; + struct nvdimm_bus *nvdimm_bus; + struct blk_alloc_info info = { + .nd_mapping = nd_mapping, + .available = nd_mapping->size, + .res = valid, + }; + + WARN_ON(!is_nd_blk(&nd_region->dev)); + nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); + device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); + return; + } + + /* allocation needs to be contiguous, so this is all or nothing */ + if (resource_size(valid) < n) + goto invalid; + + /* we've got all the space we need and no existing allocation */ + if (!exist) + return; + + /* allocation needs to be contiguous with the existing namespace */ + if (valid->start == exist->end + 1 + || valid->end == exist->start - 1) + return; + + invalid: + /* truncate @valid size to 0 */ + valid->end = valid->start - 1; } enum alloc_loc { @@ -534,18 +602,24 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, resource_size_t n) { resource_size_t mapping_end = nd_mapping->start + nd_mapping->size - 1; - bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); + struct resource *res, *exist = NULL, valid; const resource_size_t to_allocate = n; - struct resource *res; int first; + for_each_dpa_resource(ndd, res) + if (strcmp(label_id->id, res->name) == 0) + exist = res; + + valid.start = nd_mapping->start; + valid.end = mapping_end; + valid.name = "free space"; retry: first = 0; for_each_dpa_resource(ndd, res) { - resource_size_t allocate, available = 0, free_start, free_end; struct resource *next = res->sibling, *new_res = NULL; + resource_size_t allocate, available = 0; enum alloc_loc loc = ALLOC_ERR; const char *action; int rc = 0; @@ -558,32 +632,35 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, /* space at the beginning of the mapping */ if (!first++ && res->start > nd_mapping->start) { - free_start = nd_mapping->start; - available = res->start - free_start; - if (space_valid(is_pmem, is_reserve, label_id, NULL)) + valid.start = nd_mapping->start; + valid.end = res->start - 1; + space_valid(nd_region, ndd, label_id, NULL, next, exist, + to_allocate, &valid); + available = resource_size(&valid); + if (available) loc = ALLOC_BEFORE; } /* space between allocations */ if (!loc && next) { - free_start = res->start + resource_size(res); - free_end = min(mapping_end, next->start - 1); - if (space_valid(is_pmem, is_reserve, label_id, res) - && free_start < free_end) { - available = free_end + 1 - free_start; + valid.start = res->start + resource_size(res); + valid.end = min(mapping_end, next->start - 1); + space_valid(nd_region, ndd, label_id, res, next, exist, + to_allocate, &valid); + available = resource_size(&valid); + if (available) loc = ALLOC_MID; - } } /* space at the end of the mapping */ if (!loc && !next) { - free_start = res->start + resource_size(res); - free_end = mapping_end; - if (space_valid(is_pmem, is_reserve, label_id, res) - && free_start < free_end) { - available = free_end + 1 - free_start; + valid.start = res->start + resource_size(res); + valid.end = mapping_end; + space_valid(nd_region, ndd, label_id, res, next, exist, + to_allocate, &valid); + available = resource_size(&valid); + if (available) loc = ALLOC_AFTER; - } } if (!loc || !available) @@ -593,8 +670,6 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, case ALLOC_BEFORE: if (strcmp(res->name, label_id->id) == 0) { /* adjust current resource up */ - if (is_pmem && !is_reserve) - return n; rc = adjust_resource(res, res->start - allocate, resource_size(res) + allocate); action = "cur grow up"; @@ -604,8 +679,6 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, case ALLOC_MID: if (strcmp(next->name, label_id->id) == 0) { /* adjust next resource up */ - if (is_pmem && !is_reserve) - return n; rc = adjust_resource(next, next->start - allocate, resource_size(next) + allocate); @@ -629,12 +702,10 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, if (strcmp(action, "allocate") == 0) { /* BLK allocate bottom up */ if (!is_pmem) - free_start += available - allocate; - else if (!is_reserve && free_start != nd_mapping->start) - return n; + valid.start += available - allocate; new_res = nvdimm_allocate_dpa(ndd, label_id, - free_start, allocate); + valid.start, allocate); if (!new_res) rc = -EBUSY; } else if (strcmp(action, "grow down") == 0) { @@ -832,13 +903,45 @@ static int grow_dpa_allocation(struct nd_region *nd_region, return 0; } -static void nd_namespace_pmem_set_size(struct nd_region *nd_region, +static void nd_namespace_pmem_set_resource(struct nd_region *nd_region, struct nd_namespace_pmem *nspm, resource_size_t size) { struct resource *res = &nspm->nsio.res; + resource_size_t offset = 0; - res->start = nd_region->ndr_start; - res->end = nd_region->ndr_start + size - 1; + if (size && !nspm->uuid) { + WARN_ON_ONCE(1); + size = 0; + } + + if (size && nspm->uuid) { + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); + struct nd_label_id label_id; + struct resource *res; + + if (!ndd) { + size = 0; + goto out; + } + + nd_label_gen_id(&label_id, nspm->uuid, 0); + + /* calculate a spa offset from the dpa allocation offset */ + for_each_dpa_resource(ndd, res) + if (strcmp(res->name, label_id.id) == 0) { + offset = (res->start - nd_mapping->start) + * nd_region->ndr_mappings; + goto out; + } + + WARN_ON_ONCE(1); + size = 0; + } + + out: + res->start = nd_region->ndr_start + offset; + res->end = res->start + size - 1; } static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where) @@ -929,7 +1032,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); - nd_namespace_pmem_set_size(nd_region, nspm, + nd_namespace_pmem_set_resource(nd_region, nspm, val * nd_region->ndr_mappings); } else if (is_namespace_blk(dev)) { struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); @@ -1031,22 +1134,27 @@ static ssize_t size_show(struct device *dev, } static DEVICE_ATTR(size, S_IRUGO, size_show, size_store); -static ssize_t uuid_show(struct device *dev, - struct device_attribute *attr, char *buf) +static u8 *namespace_to_uuid(struct device *dev) { - u8 *uuid; - if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); - uuid = nspm->uuid; + return nspm->uuid; } else if (is_namespace_blk(dev)) { struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); - uuid = nsblk->uuid; + return nsblk->uuid; } else - return -ENXIO; + return ERR_PTR(-ENXIO); +} +static ssize_t uuid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 *uuid = namespace_to_uuid(dev); + + if (IS_ERR(uuid)) + return PTR_ERR(uuid); if (uuid) return sprintf(buf, "%pUb\n", uuid); return sprintf(buf, "\n"); @@ -1089,7 +1197,7 @@ static int namespace_update_uuid(struct nd_region *nd_region, * * FIXME: can we delete uuid with zero dpa allocated? */ - if (nd_mapping->labels) + if (list_empty(&nd_mapping->labels)) return -EBUSY; } @@ -1491,14 +1599,19 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - struct nd_namespace_label *nd_label; + struct nd_label_ent *label_ent; bool found_uuid = false; - int l; - for_each_label(l, nd_label, nd_mapping->labels) { - u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); - u16 position = __le16_to_cpu(nd_label->position); - u16 nlabel = __le16_to_cpu(nd_label->nlabel); + list_for_each_entry(label_ent, &nd_mapping->labels, list) { + struct nd_namespace_label *nd_label = label_ent->label; + u16 position, nlabel; + u64 isetcookie; + + if (!nd_label) + continue; + isetcookie = __le64_to_cpu(nd_label->isetcookie); + position = __le16_to_cpu(nd_label->position); + nlabel = __le16_to_cpu(nd_label->nlabel); if (isetcookie != cookie) continue; @@ -1528,7 +1641,6 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) { - struct nd_namespace_label *select = NULL; int i; if (!pmem_id) @@ -1536,90 +1648,106 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - struct nd_namespace_label *nd_label; + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); + struct nd_namespace_label *nd_label = NULL; u64 hw_start, hw_end, pmem_start, pmem_end; - int l; + struct nd_label_ent *label_ent; - for_each_label(l, nd_label, nd_mapping->labels) + WARN_ON(!mutex_is_locked(&nd_mapping->lock)); + list_for_each_entry(label_ent, &nd_mapping->labels, list) { + nd_label = label_ent->label; + if (!nd_label) + continue; if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0) break; + nd_label = NULL; + } if (!nd_label) { WARN_ON(1); return -EINVAL; } - select = nd_label; /* * Check that this label is compliant with the dpa * range published in NFIT */ hw_start = nd_mapping->start; hw_end = hw_start + nd_mapping->size; - pmem_start = __le64_to_cpu(select->dpa); - pmem_end = pmem_start + __le64_to_cpu(select->rawsize); - if (pmem_start == hw_start && pmem_end <= hw_end) + pmem_start = __le64_to_cpu(nd_label->dpa); + pmem_end = pmem_start + __le64_to_cpu(nd_label->rawsize); + if (pmem_start >= hw_start && pmem_start < hw_end + && pmem_end <= hw_end && pmem_end > hw_start) /* pass */; - else + else { + dev_dbg(&nd_region->dev, "%s invalid label for %pUb\n", + dev_name(ndd->dev), nd_label->uuid); return -EINVAL; + } - nd_mapping->labels[0] = select; - nd_mapping->labels[1] = NULL; + /* move recently validated label to the front of the list */ + list_move(&label_ent->list, &nd_mapping->labels); } return 0; } /** - * find_pmem_label_set - validate interleave set labelling, retrieve label0 + * create_namespace_pmem - validate interleave set labelling, retrieve label0 * @nd_region: region with mappings to validate + * @nspm: target namespace to create + * @nd_label: target pmem namespace label to evaluate */ -static int find_pmem_label_set(struct nd_region *nd_region, - struct nd_namespace_pmem *nspm) +struct device *create_namespace_pmem(struct nd_region *nd_region, + struct nd_namespace_label *nd_label) { u64 cookie = nd_region_interleave_set_cookie(nd_region); - struct nd_namespace_label *nd_label; - u8 select_id[NSLABEL_UUID_LEN]; + struct nd_label_ent *label_ent; + struct nd_namespace_pmem *nspm; + struct nd_mapping *nd_mapping; resource_size_t size = 0; - u8 *pmem_id = NULL; - int rc = -ENODEV, l; + struct resource *res; + struct device *dev; + int rc = 0; u16 i; - if (cookie == 0) - return -ENXIO; + if (cookie == 0) { + dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n"); + return ERR_PTR(-ENXIO); + } - /* - * Find a complete set of labels by uuid. By definition we can start - * with any mapping as the reference label - */ - for_each_label(l, nd_label, nd_region->mapping[0].labels) { - u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); + if (__le64_to_cpu(nd_label->isetcookie) != cookie) { + dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", + nd_label->uuid); + return ERR_PTR(-EAGAIN); + } - if (isetcookie != cookie) - continue; + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); + if (!nspm) + return ERR_PTR(-ENOMEM); - for (i = 0; nd_region->ndr_mappings; i++) - if (!has_uuid_at_pos(nd_region, nd_label->uuid, - cookie, i)) - break; - if (i < nd_region->ndr_mappings) { - /* - * Give up if we don't find an instance of a - * uuid at each position (from 0 to - * nd_region->ndr_mappings - 1), or if we find a - * dimm with two instances of the same uuid. - */ - rc = -EINVAL; - goto err; - } else if (pmem_id) { - /* - * If there is more than one valid uuid set, we - * need userspace to clean this up. - */ - rc = -EBUSY; - goto err; - } - memcpy(select_id, nd_label->uuid, NSLABEL_UUID_LEN); - pmem_id = select_id; + nspm->id = -1; + dev = &nspm->nsio.common.dev; + dev->type = &namespace_pmem_device_type; + dev->parent = &nd_region->dev; + res = &nspm->nsio.res; + res->name = dev_name(&nd_region->dev); + res->flags = IORESOURCE_MEM; + + for (i = 0; i < nd_region->ndr_mappings; i++) + if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) + break; + if (i < nd_region->ndr_mappings) { + struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); + + /* + * Give up if we don't find an instance of a uuid at each + * position (from 0 to nd_region->ndr_mappings - 1), or if we + * find a dimm with two instances of the same uuid. + */ + dev_err(&nd_region->dev, "%s missing label for %pUb\n", + dev_name(ndd->dev), nd_label->uuid); + rc = -EINVAL; + goto err; } /* @@ -1630,14 +1758,23 @@ static int find_pmem_label_set(struct nd_region *nd_region, * the dimm being enabled (i.e. nd_label_reserve_dpa() * succeeded). */ - rc = select_pmem_id(nd_region, pmem_id); + rc = select_pmem_id(nd_region, nd_label->uuid); if (rc) goto err; /* Calculate total size and populate namespace properties from label0 */ for (i = 0; i < nd_region->ndr_mappings; i++) { - struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - struct nd_namespace_label *label0 = nd_mapping->labels[0]; + struct nd_namespace_label *label0; + + nd_mapping = &nd_region->mapping[i]; + label_ent = list_first_entry_or_null(&nd_mapping->labels, + typeof(*label_ent), list); + label0 = label_ent ? label_ent->label : 0; + + if (!label0) { + WARN_ON(1); + continue; + } size += __le64_to_cpu(label0->rawsize); if (__le16_to_cpu(label0->position) != 0) @@ -1654,10 +1791,11 @@ static int find_pmem_label_set(struct nd_region *nd_region, goto err; } - nd_namespace_pmem_set_size(nd_region, nspm, size); + nd_namespace_pmem_set_resource(nd_region, nspm, size); - return 0; + return dev; err: + namespace_pmem_release(dev); switch (rc) { case -EINVAL: dev_dbg(&nd_region->dev, "%s: invalid label(s)\n", __func__); @@ -1670,55 +1808,7 @@ static int find_pmem_label_set(struct nd_region *nd_region, __func__, rc); break; } - return rc; -} - -static struct device **create_namespace_pmem(struct nd_region *nd_region) -{ - struct nd_namespace_pmem *nspm; - struct device *dev, **devs; - struct resource *res; - int rc; - - nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); - if (!nspm) - return NULL; - - dev = &nspm->nsio.common.dev; - dev->type = &namespace_pmem_device_type; - dev->parent = &nd_region->dev; - res = &nspm->nsio.res; - res->name = dev_name(&nd_region->dev); - res->flags = IORESOURCE_MEM; - rc = find_pmem_label_set(nd_region, nspm); - if (rc == -ENODEV) { - int i; - - /* Pass, try to permit namespace creation... */ - for (i = 0; i < nd_region->ndr_mappings; i++) { - struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - - kfree(nd_mapping->labels); - nd_mapping->labels = NULL; - } - - /* Publish a zero-sized namespace for userspace to configure. */ - nd_namespace_pmem_set_size(nd_region, nspm, 0); - - rc = 0; - } else if (rc) - goto err; - - devs = kcalloc(2, sizeof(struct device *), GFP_KERNEL); - if (!devs) - goto err; - - devs[0] = dev; - return devs; - - err: - namespace_pmem_release(&nspm->nsio.common.dev); - return NULL; + return ERR_PTR(rc); } struct resource *nsblk_add_resource(struct nd_region *nd_region, @@ -1770,16 +1860,58 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) return &nsblk->common.dev; } -void nd_region_create_blk_seed(struct nd_region *nd_region) +static struct device *nd_namespace_pmem_create(struct nd_region *nd_region) +{ + struct nd_namespace_pmem *nspm; + struct resource *res; + struct device *dev; + + if (!is_nd_pmem(&nd_region->dev)) + return NULL; + + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); + if (!nspm) + return NULL; + + dev = &nspm->nsio.common.dev; + dev->type = &namespace_pmem_device_type; + dev->parent = &nd_region->dev; + res = &nspm->nsio.res; + res->name = dev_name(&nd_region->dev); + res->flags = IORESOURCE_MEM; + + nspm->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL); + if (nspm->id < 0) { + kfree(nspm); + return NULL; + } + dev_set_name(dev, "namespace%d.%d", nd_region->id, nspm->id); + dev->parent = &nd_region->dev; + dev->groups = nd_namespace_attribute_groups; + nd_namespace_pmem_set_resource(nd_region, nspm, 0); + + return dev; +} + +void nd_region_create_ns_seed(struct nd_region *nd_region) { WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); - nd_region->ns_seed = nd_namespace_blk_create(nd_region); + + if (nd_region_to_nstype(nd_region) == ND_DEVICE_NAMESPACE_IO) + return; + + if (is_nd_blk(&nd_region->dev)) + nd_region->ns_seed = nd_namespace_blk_create(nd_region); + else + nd_region->ns_seed = nd_namespace_pmem_create(nd_region); + /* * Seed creation failures are not fatal, provisioning is simply * disabled until memory becomes available */ if (!nd_region->ns_seed) - dev_err(&nd_region->dev, "failed to create blk namespace\n"); + dev_err(&nd_region->dev, "failed to create %s namespace\n", + is_nd_blk(&nd_region->dev) ? "blk" : "pmem"); else nd_device_register(nd_region->ns_seed); } @@ -1820,43 +1952,137 @@ void nd_region_create_btt_seed(struct nd_region *nd_region) dev_err(&nd_region->dev, "failed to create btt namespace\n"); } -static struct device **create_namespace_blk(struct nd_region *nd_region) +static int add_namespace_resource(struct nd_region *nd_region, + struct nd_namespace_label *nd_label, struct device **devs, + int count) { struct nd_mapping *nd_mapping = &nd_region->mapping[0]; - struct nd_namespace_label *nd_label; - struct device *dev, **devs = NULL; + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); + int i; + + for (i = 0; i < count; i++) { + u8 *uuid = namespace_to_uuid(devs[i]); + struct resource *res; + + if (IS_ERR_OR_NULL(uuid)) { + WARN_ON(1); + continue; + } + + if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0) + continue; + if (is_namespace_blk(devs[i])) { + res = nsblk_add_resource(nd_region, ndd, + to_nd_namespace_blk(devs[i]), + __le64_to_cpu(nd_label->dpa)); + if (!res) + return -ENXIO; + nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count); + } else { + dev_err(&nd_region->dev, + "error: conflicting extents for uuid: %pUb\n", + nd_label->uuid); + return -ENXIO; + } + break; + } + + return i; +} + +struct device *create_namespace_blk(struct nd_region *nd_region, + struct nd_namespace_label *nd_label, int count) +{ + + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct nd_namespace_blk *nsblk; - struct nvdimm_drvdata *ndd; - int i, l, count = 0; + char *name[NSLABEL_NAME_LEN]; + struct device *dev = NULL; struct resource *res; - if (nd_region->ndr_mappings == 0) - return NULL; + nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); + if (!nsblk) + return ERR_PTR(-ENOMEM); + dev = &nsblk->common.dev; + dev->type = &namespace_blk_device_type; + dev->parent = &nd_region->dev; + nsblk->id = -1; + nsblk->lbasize = __le64_to_cpu(nd_label->lbasize); + nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, + GFP_KERNEL); + if (!nsblk->uuid) + goto blk_err; + memcpy(name, nd_label->name, NSLABEL_NAME_LEN); + if (name[0]) + nsblk->alt_name = kmemdup(name, NSLABEL_NAME_LEN, + GFP_KERNEL); + res = nsblk_add_resource(nd_region, ndd, nsblk, + __le64_to_cpu(nd_label->dpa)); + if (!res) + goto blk_err; + nd_dbg_dpa(nd_region, ndd, res, "%d: assign\n", count); + return dev; + blk_err: + namespace_blk_release(dev); + return ERR_PTR(-ENXIO); +} + +static int cmp_dpa(const void *a, const void *b) +{ + const struct device *dev_a = *(const struct device **) a; + const struct device *dev_b = *(const struct device **) b; + struct nd_namespace_blk *nsblk_a, *nsblk_b; + struct nd_namespace_pmem *nspm_a, *nspm_b; + + if (is_namespace_io(dev_a)) + return 0; + + if (is_namespace_blk(dev_a)) { + nsblk_a = to_nd_namespace_blk(dev_a); + nsblk_b = to_nd_namespace_blk(dev_b); + + return memcmp(&nsblk_a->res[0]->start, &nsblk_b->res[0]->start, + sizeof(resource_size_t)); + } + + nspm_a = to_nd_namespace_pmem(dev_a); + nspm_b = to_nd_namespace_pmem(dev_b); + + return memcmp(&nspm_a->nsio.res.start, &nspm_b->nsio.res.start, + sizeof(resource_size_t)); +} - ndd = to_ndd(nd_mapping); - for_each_label(l, nd_label, nd_mapping->labels) { - u32 flags = __le32_to_cpu(nd_label->flags); - char *name[NSLABEL_NAME_LEN]; +static struct device **scan_labels(struct nd_region *nd_region) +{ + int i, count = 0; + struct device *dev, **devs = NULL; + struct nd_label_ent *label_ent, *e; + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; + resource_size_t map_end = nd_mapping->start + nd_mapping->size - 1; + + /* "safe" because create_namespace_pmem() might list_move() label_ent */ + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + struct nd_namespace_label *nd_label = label_ent->label; struct device **__devs; + u32 flags; - if (flags & NSLABEL_FLAG_LOCAL) - /* pass */; + if (!nd_label) + continue; + flags = __le32_to_cpu(nd_label->flags); + if (is_nd_blk(&nd_region->dev) + == !!(flags & NSLABEL_FLAG_LOCAL)) + /* pass, region matches label type */; else continue; - for (i = 0; i < count; i++) { - nsblk = to_nd_namespace_blk(devs[i]); - if (memcmp(nsblk->uuid, nd_label->uuid, - NSLABEL_UUID_LEN) == 0) { - res = nsblk_add_resource(nd_region, ndd, nsblk, - __le64_to_cpu(nd_label->dpa)); - if (!res) - goto err; - nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", - dev_name(&nsblk->common.dev)); - break; - } - } + /* skip labels that describe extents outside of the region */ + if (nd_label->dpa < nd_mapping->start || nd_label->dpa > map_end) + continue; + + i = add_namespace_resource(nd_region, nd_label, devs, count); + if (i < 0) + goto err; if (i < count) continue; __devs = kcalloc(count + 2, sizeof(dev), GFP_KERNEL); @@ -1866,67 +2092,126 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) kfree(devs); devs = __devs; - nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); - if (!nsblk) - goto err; - dev = &nsblk->common.dev; - dev->type = &namespace_blk_device_type; - dev->parent = &nd_region->dev; - dev_set_name(dev, "namespace%d.%d", nd_region->id, count); - devs[count++] = dev; - nsblk->id = -1; - nsblk->lbasize = __le64_to_cpu(nd_label->lbasize); - nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, - GFP_KERNEL); - if (!nsblk->uuid) - goto err; - memcpy(name, nd_label->name, NSLABEL_NAME_LEN); - if (name[0]) - nsblk->alt_name = kmemdup(name, NSLABEL_NAME_LEN, - GFP_KERNEL); - res = nsblk_add_resource(nd_region, ndd, nsblk, - __le64_to_cpu(nd_label->dpa)); - if (!res) - goto err; - nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", - dev_name(&nsblk->common.dev)); + if (is_nd_blk(&nd_region->dev)) { + dev = create_namespace_blk(nd_region, nd_label, count); + if (IS_ERR(dev)) + goto err; + devs[count++] = dev; + } else { + dev = create_namespace_pmem(nd_region, nd_label); + if (IS_ERR(dev)) { + switch (PTR_ERR(dev)) { + case -EAGAIN: + /* skip invalid labels */ + continue; + case -ENODEV: + /* fallthrough to seed creation */ + break; + default: + goto err; + } + } else + devs[count++] = dev; + } } - dev_dbg(&nd_region->dev, "%s: discovered %d blk namespace%s\n", - __func__, count, count == 1 ? "" : "s"); + dev_dbg(&nd_region->dev, "%s: discovered %d %s namespace%s\n", + __func__, count, is_nd_blk(&nd_region->dev) + ? "blk" : "pmem", count == 1 ? "" : "s"); if (count == 0) { /* Publish a zero-sized namespace for userspace to configure. */ - for (i = 0; i < nd_region->ndr_mappings; i++) { - struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - - kfree(nd_mapping->labels); - nd_mapping->labels = NULL; - } + nd_mapping_free_labels(nd_mapping); devs = kcalloc(2, sizeof(dev), GFP_KERNEL); if (!devs) goto err; - nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); - if (!nsblk) - goto err; - dev = &nsblk->common.dev; - dev->type = &namespace_blk_device_type; + if (is_nd_blk(&nd_region->dev)) { + struct nd_namespace_blk *nsblk; + + nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); + if (!nsblk) + goto err; + dev = &nsblk->common.dev; + dev->type = &namespace_blk_device_type; + } else { + struct nd_namespace_pmem *nspm; + + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); + if (!nspm) + goto err; + dev = &nspm->nsio.common.dev; + dev->type = &namespace_pmem_device_type; + nd_namespace_pmem_set_resource(nd_region, nspm, 0); + } dev->parent = &nd_region->dev; devs[count++] = dev; + } else if (is_nd_pmem(&nd_region->dev)) { + /* clean unselected labels */ + for (i = 0; i < nd_region->ndr_mappings; i++) { + struct list_head *l, *e; + LIST_HEAD(list); + int j; + + nd_mapping = &nd_region->mapping[i]; + if (list_empty(&nd_mapping->labels)) { + WARN_ON(1); + continue; + } + + j = count; + list_for_each_safe(l, e, &nd_mapping->labels) { + if (!j--) + break; + list_move_tail(l, &list); + } + nd_mapping_free_labels(nd_mapping); + list_splice_init(&list, &nd_mapping->labels); + } } + if (count > 1) + sort(devs, count, sizeof(struct device *), cmp_dpa, NULL); + return devs; -err: - for (i = 0; i < count; i++) { - nsblk = to_nd_namespace_blk(devs[i]); - namespace_blk_release(&nsblk->common.dev); - } + err: + for (i = 0; devs[i]; i++) + if (is_nd_blk(&nd_region->dev)) + namespace_blk_release(devs[i]); + else + namespace_pmem_release(devs[i]); kfree(devs); return NULL; } +static struct device **create_namespaces(struct nd_region *nd_region) +{ + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; + struct device **devs; + int i; + + if (nd_region->ndr_mappings == 0) + return NULL; + + /* lock down all mappings while we scan labels */ + for (i = 0; i < nd_region->ndr_mappings; i++) { + nd_mapping = &nd_region->mapping[i]; + mutex_lock_nested(&nd_mapping->lock, i); + } + + devs = scan_labels(nd_region); + + for (i = 0; i < nd_region->ndr_mappings; i++) { + int reverse = nd_region->ndr_mappings - 1 - i; + + nd_mapping = &nd_region->mapping[reverse]; + mutex_unlock(&nd_mapping->lock); + } + + return devs; +} + static int init_active_labels(struct nd_region *nd_region) { int i; @@ -1935,6 +2220,7 @@ static int init_active_labels(struct nd_region *nd_region) struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct nvdimm *nvdimm = nd_mapping->nvdimm; + struct nd_label_ent *label_ent; int count, j; /* @@ -1956,16 +2242,27 @@ static int init_active_labels(struct nd_region *nd_region) dev_dbg(ndd->dev, "%s: %d\n", __func__, count); if (!count) continue; - nd_mapping->labels = kcalloc(count + 1, sizeof(void *), - GFP_KERNEL); - if (!nd_mapping->labels) - return -ENOMEM; for (j = 0; j < count; j++) { struct nd_namespace_label *label; + label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); + if (!label_ent) + break; label = nd_label_active(ndd, j); - nd_mapping->labels[j] = label; + label_ent->label = label; + + mutex_lock(&nd_mapping->lock); + list_add_tail(&label_ent->list, &nd_mapping->labels); + mutex_unlock(&nd_mapping->lock); } + + if (j >= count) + continue; + + mutex_lock(&nd_mapping->lock); + nd_mapping_free_labels(nd_mapping); + mutex_unlock(&nd_mapping->lock); + return -ENOMEM; } return 0; @@ -1990,10 +2287,8 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) devs = create_namespace_io(nd_region); break; case ND_DEVICE_NAMESPACE_PMEM: - devs = create_namespace_pmem(nd_region); - break; case ND_DEVICE_NAMESPACE_BLK: - devs = create_namespace_blk(nd_region); + devs = create_namespaces(nd_region); break; default: break; @@ -2014,6 +2309,13 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL); nsblk->id = id; + } else if (type == ND_DEVICE_NAMESPACE_PMEM) { + struct nd_namespace_pmem *nspm; + + nspm = to_nd_namespace_pmem(dev); + id = ida_simple_get(&nd_region->ns_ida, 0, 0, + GFP_KERNEL); + nspm->id = id; } else id = i; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 38ce6bbbc170..8623e57c2ce3 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -44,6 +44,23 @@ struct nvdimm { struct resource *flush_wpq; }; +/** + * struct blk_alloc_info - tracking info for BLK dpa scanning + * @nd_mapping: blk region mapping boundaries + * @available: decremented in alias_dpa_busy as aliased PMEM is scanned + * @busy: decremented in blk_dpa_busy to account for ranges already + * handled by alias_dpa_busy + * @res: alias_dpa_busy interprets this a free space range that needs to + * be truncated to the valid BLK allocation starting DPA, blk_dpa_busy + * treats it as a busy range that needs the aliased PMEM ranges + * truncated. + */ +struct blk_alloc_info { + struct nd_mapping *nd_mapping; + resource_size_t available, busy; + struct resource *res; +}; + bool is_nvdimm(struct device *dev); bool is_nd_pmem(struct device *dev); bool is_nd_blk(struct device *dev); @@ -54,7 +71,7 @@ void nvdimm_devs_exit(void); void nd_region_devs_exit(void); void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev); struct nd_region; -void nd_region_create_blk_seed(struct nd_region *nd_region); +void nd_region_create_ns_seed(struct nd_region *nd_region); void nd_region_create_btt_seed(struct nd_region *nd_region); void nd_region_create_pfn_seed(struct nd_region *nd_region); void nd_region_create_dax_seed(struct nd_region *nd_region); @@ -73,13 +90,14 @@ bool nd_is_uuid_unique(struct device *dev, u8 *uuid); struct nd_region; struct nvdimm_drvdata; struct nd_mapping; +void nd_mapping_free_labels(struct nd_mapping *nd_mapping); resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, struct nd_mapping *nd_mapping, resource_size_t *overlap); -resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping); +resource_size_t nd_blk_available_dpa(struct nd_region *nd_region); resource_size_t nd_region_available_dpa(struct nd_region *nd_region); resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, struct nd_label_id *label_id); -struct nd_mapping; +int alias_dpa_busy(struct device *dev, void *data); struct resource *nsblk_add_resource(struct nd_region *nd_region, struct nvdimm_drvdata *ndd, struct nd_namespace_blk *nsblk, resource_size_t start); diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 0b78a8211f4a..d3b2fca8deec 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -101,9 +101,6 @@ static inline struct nd_namespace_index *to_next_namespace_index( (unsigned long long) (res ? resource_size(res) : 0), \ (unsigned long long) (res ? res->start : 0), ##arg) -#define for_each_label(l, label, labels) \ - for (l = 0; (label = labels ? labels[l] : NULL); l++) - #define for_each_dpa_resource(ndd, res) \ for (res = (ndd)->dpa.child; res; res = res->sibling) @@ -116,6 +113,31 @@ struct nd_percpu_lane { spinlock_t lock; }; +struct nd_label_ent { + struct list_head list; + struct nd_namespace_label *label; +}; + +enum nd_mapping_lock_class { + ND_MAPPING_CLASS0, + ND_MAPPING_UUID_SCAN, +}; + +struct nd_mapping { + struct nvdimm *nvdimm; + u64 start; + u64 size; + struct list_head labels; + struct mutex lock; + /* + * @ndd is for private use at region enable / disable time for + * get_ndd() + put_ndd(), all other nd_mapping to ndd + * conversions use to_ndd() which respects enabled state of the + * nvdimm. + */ + struct nvdimm_drvdata *ndd; +}; + struct nd_region { struct device dev; struct ida ns_ida; @@ -209,6 +231,7 @@ void nvdimm_exit(void); void nd_region_exit(void); struct nvdimm; struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping); +int nvdimm_check_config_data(struct device *dev); int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd); int nvdimm_init_config_data(struct nvdimm_drvdata *ndd); int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 571a6c7ee2fc..42b3a8217073 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -66,13 +66,32 @@ static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset, invalidate_pmem(pmem->virt_addr + offset, len); } +static void write_pmem(void *pmem_addr, struct page *page, + unsigned int off, unsigned int len) +{ + void *mem = kmap_atomic(page); + + memcpy_to_pmem(pmem_addr, mem + off, len); + kunmap_atomic(mem); +} + +static int read_pmem(struct page *page, unsigned int off, + void *pmem_addr, unsigned int len) +{ + int rc; + void *mem = kmap_atomic(page); + + rc = memcpy_from_pmem(mem + off, pmem_addr, len); + kunmap_atomic(mem); + return rc; +} + static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, unsigned int len, unsigned int off, bool is_write, sector_t sector) { int rc = 0; bool bad_pmem = false; - void *mem = kmap_atomic(page); phys_addr_t pmem_off = sector * 512 + pmem->data_offset; void *pmem_addr = pmem->virt_addr + pmem_off; @@ -83,7 +102,7 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, if (unlikely(bad_pmem)) rc = -EIO; else { - rc = memcpy_from_pmem(mem + off, pmem_addr, len); + rc = read_pmem(page, off, pmem_addr, len); flush_dcache_page(page); } } else { @@ -102,14 +121,13 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, * after clear poison. */ flush_dcache_page(page); - memcpy_to_pmem(pmem_addr, mem + off, len); + write_pmem(pmem_addr, page, off, len); if (unlikely(bad_pmem)) { pmem_clear_poison(pmem, pmem_off, len); - memcpy_to_pmem(pmem_addr, mem + off, len); + write_pmem(pmem_addr, page, off, len); } } - kunmap_atomic(mem); return rc; } diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 4c0ac4abb629..6af5e629140c 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -70,7 +70,7 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm, int nd_region_activate(struct nd_region *nd_region) { - int i, num_flush = 0; + int i, j, num_flush = 0; struct nd_region_data *ndrd; struct device *dev = &nd_region->dev; size_t flush_data_size = sizeof(void *); @@ -107,6 +107,21 @@ int nd_region_activate(struct nd_region *nd_region) return rc; } + /* + * Clear out entries that are duplicates. This should prevent the + * extra flushings. + */ + for (i = 0; i < nd_region->ndr_mappings - 1; i++) { + /* ignore if NULL already */ + if (!ndrd_get_flush_wpq(ndrd, i, 0)) + continue; + + for (j = i + 1; j < nd_region->ndr_mappings; j++) + if (ndrd_get_flush_wpq(ndrd, i, 0) == + ndrd_get_flush_wpq(ndrd, j, 0)) + ndrd_set_flush_wpq(ndrd, j, 0, NULL); + } + return 0; } @@ -298,9 +313,8 @@ resource_size_t nd_region_available_dpa(struct nd_region *nd_region) blk_max_overlap = overlap; goto retry; } - } else if (is_nd_blk(&nd_region->dev)) { - available += nd_blk_available_dpa(nd_mapping); - } + } else if (is_nd_blk(&nd_region->dev)) + available += nd_blk_available_dpa(nd_region); } return available; @@ -491,6 +505,17 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region) return 0; } +void nd_mapping_free_labels(struct nd_mapping *nd_mapping) +{ + struct nd_label_ent *label_ent, *e; + + WARN_ON(!mutex_is_locked(&nd_mapping->lock)); + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + list_del(&label_ent->list); + kfree(label_ent); + } +} + /* * Upon successful probe/remove, take/release a reference on the * associated interleave set (if present), and plant new btt + namespace @@ -511,8 +536,10 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, struct nvdimm_drvdata *ndd = nd_mapping->ndd; struct nvdimm *nvdimm = nd_mapping->nvdimm; - kfree(nd_mapping->labels); - nd_mapping->labels = NULL; + mutex_lock(&nd_mapping->lock); + nd_mapping_free_labels(nd_mapping); + mutex_unlock(&nd_mapping->lock); + put_ndd(ndd); nd_mapping->ndd = NULL; if (ndd) @@ -522,11 +549,12 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, if (is_nd_pmem(dev)) return; } - if (dev->parent && is_nd_blk(dev->parent) && probe) { + if (dev->parent && (is_nd_blk(dev->parent) || is_nd_pmem(dev->parent)) + && probe) { nd_region = to_nd_region(dev->parent); nvdimm_bus_lock(dev); if (nd_region->ns_seed == dev) - nd_region_create_blk_seed(nd_region); + nd_region_create_ns_seed(nd_region); nvdimm_bus_unlock(dev); } if (is_nd_btt(dev) && probe) { @@ -536,23 +564,30 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, nvdimm_bus_lock(dev); if (nd_region->btt_seed == dev) nd_region_create_btt_seed(nd_region); - if (nd_region->ns_seed == &nd_btt->ndns->dev && - is_nd_blk(dev->parent)) - nd_region_create_blk_seed(nd_region); + if (nd_region->ns_seed == &nd_btt->ndns->dev) + nd_region_create_ns_seed(nd_region); nvdimm_bus_unlock(dev); } if (is_nd_pfn(dev) && probe) { + struct nd_pfn *nd_pfn = to_nd_pfn(dev); + nd_region = to_nd_region(dev->parent); nvdimm_bus_lock(dev); if (nd_region->pfn_seed == dev) nd_region_create_pfn_seed(nd_region); + if (nd_region->ns_seed == &nd_pfn->ndns->dev) + nd_region_create_ns_seed(nd_region); nvdimm_bus_unlock(dev); } if (is_nd_dax(dev) && probe) { + struct nd_dax *nd_dax = to_nd_dax(dev); + nd_region = to_nd_region(dev->parent); nvdimm_bus_lock(dev); if (nd_region->dax_seed == dev) nd_region_create_dax_seed(nd_region); + if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) + nd_region_create_ns_seed(nd_region); nvdimm_bus_unlock(dev); } } @@ -759,10 +794,10 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, int ro = 0; for (i = 0; i < ndr_desc->num_mappings; i++) { - struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; - struct nvdimm *nvdimm = nd_mapping->nvdimm; + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; + struct nvdimm *nvdimm = mapping->nvdimm; - if ((nd_mapping->start | nd_mapping->size) % SZ_4K) { + if ((mapping->start | mapping->size) % SZ_4K) { dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not 4K aligned\n", caller, dev_name(&nvdimm->dev), i); @@ -813,11 +848,15 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, ndl->count = 0; } - memcpy(nd_region->mapping, ndr_desc->nd_mapping, - sizeof(struct nd_mapping) * ndr_desc->num_mappings); for (i = 0; i < ndr_desc->num_mappings; i++) { - struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; - struct nvdimm *nvdimm = nd_mapping->nvdimm; + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; + struct nvdimm *nvdimm = mapping->nvdimm; + + nd_region->mapping[i].nvdimm = nvdimm; + nd_region->mapping[i].start = mapping->start; + nd_region->mapping[i].size = mapping->size; + INIT_LIST_HEAD(&nd_region->mapping[i].labels); + mutex_init(&nd_region->mapping[i].lock); get_device(&nvdimm->dev); } diff --git a/drivers/of/irq.c b/drivers/of/irq.c index a2e68f740eda..393fea85eb4e 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> +#include <linux/of_pci.h> #include <linux/string.h> #include <linux/slab.h> @@ -592,87 +593,16 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np, u32 rid_in) { struct device *parent_dev; - struct device_node *msi_controller_node; - struct device_node *msi_np = *np; - u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle; - int msi_map_len; - bool matched; u32 rid_out = rid_in; - const __be32 *msi_map = NULL; /* * Walk up the device parent links looking for one with a * "msi-map" property. */ - for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!parent_dev->of_node) - continue; - - msi_map = of_get_property(parent_dev->of_node, - "msi-map", &msi_map_len); - if (!msi_map) - continue; - - if (msi_map_len % (4 * sizeof(__be32))) { - dev_err(parent_dev, "Error: Bad msi-map length: %d\n", - msi_map_len); - return rid_out; - } - /* We have a good parent_dev and msi_map, let's use them. */ - break; - } - if (!msi_map) - return rid_out; - - /* The default is to select all bits. */ - map_mask = 0xffffffff; - - /* - * Can be overridden by "msi-map-mask" property. If - * of_property_read_u32() fails, the default is used. - */ - of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask); - - masked_rid = map_mask & rid_in; - matched = false; - while (!matched && msi_map_len >= 4 * sizeof(__be32)) { - rid_base = be32_to_cpup(msi_map + 0); - phandle = be32_to_cpup(msi_map + 1); - msi_base = be32_to_cpup(msi_map + 2); - rid_len = be32_to_cpup(msi_map + 3); - - if (rid_base & ~map_mask) { - dev_err(parent_dev, - "Invalid msi-map translation - msi-map-mask (0x%x) ignores rid-base (0x%x)\n", - map_mask, rid_base); - return rid_out; - } - - msi_controller_node = of_find_node_by_phandle(phandle); - - matched = (masked_rid >= rid_base && - masked_rid < rid_base + rid_len); - if (msi_np) - matched &= msi_np == msi_controller_node; - - if (matched && !msi_np) { - *np = msi_np = msi_controller_node; + for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) + if (!of_pci_map_rid(parent_dev->of_node, rid_in, "msi-map", + "msi-map-mask", np, &rid_out)) break; - } - - of_node_put(msi_controller_node); - msi_map_len -= 4 * sizeof(__be32); - msi_map += 4; - } - if (!matched) - return rid_out; - - rid_out = masked_rid - rid_base + msi_base; - dev_dbg(dev, - "msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n", - dev_name(parent_dev), map_mask, rid_base, msi_base, - rid_len, rid_in, rid_out); - return rid_out; } diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 589b30c68e14..b58be12ab277 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -308,3 +308,105 @@ struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node) EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node); #endif /* CONFIG_PCI_MSI */ + +/** + * of_pci_map_rid - Translate a requester ID through a downstream mapping. + * @np: root complex device node. + * @rid: PCI requester ID to map. + * @map_name: property name of the map to use. + * @map_mask_name: optional property name of the mask to use. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Given a PCI requester ID, look up the appropriate implementation-defined + * platform ID and/or the target device which receives transactions on that + * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or + * @id_out may be NULL if only the other is required. If @target points to + * a non-NULL device node pointer, only entries targeting that node will be + * matched; if it points to a NULL value, it will receive the device node of + * the first matching target phandle, with a reference held. + * + * Return: 0 on success or a standard error code on failure. + */ +int of_pci_map_rid(struct device_node *np, u32 rid, + const char *map_name, const char *map_mask_name, + struct device_node **target, u32 *id_out) +{ + u32 map_mask, masked_rid; + int map_len; + const __be32 *map = NULL; + + if (!np || !map_name || (!target && !id_out)) + return -EINVAL; + + map = of_get_property(np, map_name, &map_len); + if (!map) { + if (target) + return -ENODEV; + /* Otherwise, no map implies no translation */ + *id_out = rid; + return 0; + } + + if (!map_len || map_len % (4 * sizeof(*map))) { + pr_err("%s: Error: Bad %s length: %d\n", np->full_name, + map_name, map_len); + return -EINVAL; + } + + /* The default is to select all bits. */ + map_mask = 0xffffffff; + + /* + * Can be overridden by "{iommu,msi}-map-mask" property. + * If of_property_read_u32() fails, the default is used. + */ + if (map_mask_name) + of_property_read_u32(np, map_mask_name, &map_mask); + + masked_rid = map_mask & rid; + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + struct device_node *phandle_node; + u32 rid_base = be32_to_cpup(map + 0); + u32 phandle = be32_to_cpup(map + 1); + u32 out_base = be32_to_cpup(map + 2); + u32 rid_len = be32_to_cpup(map + 3); + + if (rid_base & ~map_mask) { + pr_err("%s: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n", + np->full_name, map_name, map_name, + map_mask, rid_base); + return -EFAULT; + } + + if (masked_rid < rid_base || masked_rid >= rid_base + rid_len) + continue; + + phandle_node = of_find_node_by_phandle(phandle); + if (!phandle_node) + return -ENODEV; + + if (target) { + if (*target) + of_node_put(phandle_node); + else + *target = phandle_node; + + if (*target != phandle_node) + continue; + } + + if (id_out) + *id_out = masked_rid - rid_base + out_base; + + pr_debug("%s: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n", + np->full_name, map_name, map_mask, rid_base, out_base, + rid_len, rid, *id_out); + return 0; + } + + pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n", + np->full_name, map_name, rid, + target && *target ? (*target)->full_name : "any target"); + return -EFAULT; +} diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7292f23954df..6620d96ee44d 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -31,11 +31,11 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/pulse8-cec/Kconfig" -source "drivers/staging/media/tw686x-kh/Kconfig" - source "drivers/staging/media/s5p-cec/Kconfig" # Keep LIRC at the end, as it has sub-menus source "drivers/staging/media/lirc/Kconfig" +source "drivers/staging/media/st-cec/Kconfig" + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 87ce8ad1e22a..906257e94dda 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec/ -obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh/ +obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += st-cec/ diff --git a/drivers/staging/media/cec/Kconfig b/drivers/staging/media/cec/Kconfig index 21457a1f6c9f..6e12d41b1f86 100644 --- a/drivers/staging/media/cec/Kconfig +++ b/drivers/staging/media/cec/Kconfig @@ -5,9 +5,6 @@ config MEDIA_CEC ---help--- Enable the CEC API. - To compile this driver as a module, choose M here: the - module will be called cec. - config MEDIA_CEC_DEBUG bool "CEC debugfs interface (EXPERIMENTAL)" depends on MEDIA_CEC && DEBUG_FS diff --git a/drivers/staging/media/cec/cec-adap.c b/drivers/staging/media/cec/cec-adap.c index 946986f3ac0d..611e07b78bfe 100644 --- a/drivers/staging/media/cec/cec-adap.c +++ b/drivers/staging/media/cec/cec-adap.c @@ -1164,8 +1164,6 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (IS_ERR_OR_NULL(adap)) return; - if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR)) - return; mutex_lock(&adap->lock); __cec_s_phys_addr(adap, phys_addr, block); mutex_unlock(&adap->lock); @@ -1306,8 +1304,6 @@ int cec_s_log_addrs(struct cec_adapter *adap, { int err; - if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS)) - return -EINVAL; mutex_lock(&adap->lock); err = __cec_s_log_addrs(adap, log_addrs, block); mutex_unlock(&adap->lock); diff --git a/drivers/staging/media/cec/cec-core.c b/drivers/staging/media/cec/cec-core.c index 3b1e4d2b190d..b0137e247dc9 100644 --- a/drivers/staging/media/cec/cec-core.c +++ b/drivers/staging/media/cec/cec-core.c @@ -357,8 +357,7 @@ void cec_delete_adapter(struct cec_adapter *adap) if (adap->kthread_config) kthread_stop(adap->kthread_config); #if IS_REACHABLE(CONFIG_RC_CORE) - if (adap->rc) - rc_free_device(adap->rc); + rc_free_device(adap->rc); #endif kfree(adap); } diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index 64d99ec292e5..bfb76a45bfbf 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -650,7 +650,7 @@ static int __init lirc_parallel_init(void) if (!pport) { pr_notice("no port at %x found\n", io); result = -ENXIO; - goto exit_device_put; + goto exit_device_del; } ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, pf, kf, lirc_lirc_irq_handler, 0, @@ -659,7 +659,7 @@ static int __init lirc_parallel_init(void) if (!ppdevice) { pr_notice("parport_register_device() failed\n"); result = -ENXIO; - goto exit_device_put; + goto exit_device_del; } if (parport_claim(ppdevice) != 0) goto skip_init; @@ -678,7 +678,7 @@ static int __init lirc_parallel_init(void) parport_release(pport); parport_unregister_device(ppdevice); result = -EIO; - goto exit_device_put; + goto exit_device_del; } #endif @@ -695,11 +695,13 @@ static int __init lirc_parallel_init(void) pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; - goto exit_device_put; + goto exit_device_del; } pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; +exit_device_del: + platform_device_del(lirc_parallel_dev); exit_device_put: platform_device_put(lirc_parallel_dev); exit_driver_unregister: diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index 6ceb4eb00493..c26c99fd4a24 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -61,7 +61,7 @@ static void iss_print_status(struct iss_device *iss) * See this link for reference: * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html */ -void omap4iss_flush(struct iss_device *iss) +static void omap4iss_flush(struct iss_device *iss) { iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION, 0); iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); @@ -362,6 +362,10 @@ static irqreturn_t iss_isr(int irq, void *_iss) return IRQ_HANDLED; } +static const struct media_device_ops iss_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + /* ----------------------------------------------------------------------------- * Pipeline stream management */ @@ -988,7 +992,7 @@ static int iss_register_entities(struct iss_device *iss) strlcpy(iss->media_dev.model, "TI OMAP4 ISS", sizeof(iss->media_dev.model)); iss->media_dev.hw_revision = iss->revision; - iss->media_dev.link_notify = v4l2_pipeline_link_notify; + iss->media_dev.ops = &iss_media_ops; ret = media_device_register(&iss->media_dev); if (ret < 0) { dev_err(iss->dev, "Media device registration failed (%d)\n", diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 90b7ff56722d..c16927ac8eb0 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -646,6 +646,103 @@ iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) } static int +iss_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev_format format; + struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + u32 pad; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + /* Try the get selection operation first and fallback to get format if not + * implemented. + */ + sdsel.pad = pad; + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; + if (ret != -ENOIOCTLCMD) + return ret; + + format.pad = pad; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format.format.width; + sel->r.height = format.format.height; + + return 0; +} + +static int +iss_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + u32 pad; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + sdsel.pad = pad; + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); + mutex_unlock(&video->mutex); + if (!ret) + sel->r = sdsel.r; + + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; +} + +static int iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) { struct iss_video_fh *vfh = to_iss_video_fh(fh); @@ -971,6 +1068,8 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_g_fmt_vid_out = iss_video_get_format, .vidioc_s_fmt_vid_out = iss_video_set_format, .vidioc_try_fmt_vid_out = iss_video_try_format, + .vidioc_g_selection = iss_video_get_selection, + .vidioc_s_selection = iss_video_set_selection, .vidioc_g_parm = iss_video_get_param, .vidioc_s_parm = iss_video_set_param, .vidioc_reqbufs = iss_video_reqbufs, diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c index ed8bd95ad6d0..1732c3857b8e 100644 --- a/drivers/staging/media/pulse8-cec/pulse8-cec.c +++ b/drivers/staging/media/pulse8-cec/pulse8-cec.c @@ -10,6 +10,29 @@ * this archive for more details. */ +/* + * Notes: + * + * - Devices with firmware version < 2 do not store their configuration in + * EEPROM. + * + * - In autonomous mode, only messages from a TV will be acknowledged, even + * polling messages. Upon receiving a message from a TV, the dongle will + * respond to messages from any logical address. + * + * - In autonomous mode, the dongle will by default reply Feature Abort + * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will + * however observe vendor ID's reported by other devices and possibly + * alter this behavior. When TV's (and TV's only) report that their vendor ID + * is LG (0x00e091), the dongle will itself reply that it has the same vendor + * ID, and it will respond to at least one vendor specific command. + * + * - In autonomous mode, the dongle is known to attempt wakeup if it receives + * <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it + * receives <Set Stream Path> with its own physical address. It also does this + * if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV. + */ + #include <linux/completion.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -28,8 +51,11 @@ MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); MODULE_LICENSE("GPL"); static int debug; +static int persistent_config = 1; module_param(debug, int, 0644); +module_param(persistent_config, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-1)"); +MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)"); enum pulse8_msgcodes { MSGCODE_NOTHING = 0, @@ -86,12 +112,16 @@ enum pulse8_msgcodes { #define DATA_SIZE 256 +#define PING_PERIOD (15 * HZ) + struct pulse8 { struct device *dev; struct serio *serio; struct cec_adapter *adap; + unsigned int vers; struct completion cmd_done; struct work_struct work; + struct delayed_work ping_eeprom_work; struct cec_msg rx_msg; u8 data[DATA_SIZE]; unsigned int len; @@ -99,8 +129,15 @@ struct pulse8 { unsigned int idx; bool escape; bool started; + struct mutex config_lock; + struct mutex write_lock; + bool config_pending; + bool restoring_config; + bool autonomous; }; +static void pulse8_ping_eeprom_work_handler(struct work_struct *work); + static void pulse8_irq_work_handler(struct work_struct *work) { struct pulse8 *pulse8 = @@ -205,6 +242,7 @@ static void pulse8_disconnect(struct serio *serio) struct pulse8 *pulse8 = serio_get_drvdata(serio); cec_unregister_adapter(pulse8->adap); + cancel_delayed_work_sync(&pulse8->ping_eeprom_work); dev_info(&serio->dev, "disconnected\n"); serio_close(serio); serio_set_drvdata(serio, NULL); @@ -228,13 +266,14 @@ static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len) } } if (!err) - err = serio_write(serio, 0xfe); + err = serio_write(serio, MSGEND); return err; } -static int pulse8_send_and_wait(struct pulse8 *pulse8, - const u8 *cmd, u8 cmd_len, u8 response, u8 size) +static int pulse8_send_and_wait_once(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, + u8 response, u8 size) { int err; @@ -250,24 +289,8 @@ static int pulse8_send_and_wait(struct pulse8 *pulse8, if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED && cmd[0] != MSGCODE_SET_CONTROLLED && cmd[0] != MSGCODE_SET_AUTO_ENABLED && - cmd[0] != MSGCODE_GET_BUILDDATE) { - u8 cmd_sc[2]; - - cmd_sc[0] = MSGCODE_SET_CONTROLLED; - cmd_sc[1] = 1; - err = pulse8_send_and_wait(pulse8, cmd_sc, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (err) - return err; - init_completion(&pulse8->cmd_done); - - err = pulse8_send(pulse8->serio, cmd, cmd_len); - if (err) - return err; - - if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ)) - return -ETIMEDOUT; - } + cmd[0] != MSGCODE_GET_BUILDDATE) + return -ENOTTY; if (response && ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) { dev_info(pulse8->dev, "transmit: failed %02x\n", @@ -277,74 +300,155 @@ static int pulse8_send_and_wait(struct pulse8 *pulse8, return 0; } -static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio) +static int pulse8_send_and_wait(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, u8 response, u8 size) +{ + u8 cmd_sc[2]; + int err; + + mutex_lock(&pulse8->write_lock); + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); + + if (err == -ENOTTY) { + cmd_sc[0] = MSGCODE_SET_CONTROLLED; + cmd_sc[1] = 1; + err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + if (err) + goto unlock; + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, + response, size); + } + +unlock: + mutex_unlock(&pulse8->write_lock); + return err == -ENOTTY ? -EIO : err; +} + +static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio, + struct cec_log_addrs *log_addrs, u16 *pa) { u8 *data = pulse8->data + 1; - unsigned int count = 0; - unsigned int vers = 0; u8 cmd[2]; int err; + struct tm tm; + time_t date; + + pulse8->vers = 0; - cmd[0] = MSGCODE_PING; - err = pulse8_send_and_wait(pulse8, cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 0); cmd[0] = MSGCODE_FIRMWARE_VERSION; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); if (err) return err; - - vers = (data[0] << 8) | data[1]; - - dev_info(pulse8->dev, "Firmware version %04x\n", vers); - if (vers < 2) + pulse8->vers = (data[0] << 8) | data[1]; + dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers); + if (pulse8->vers < 2) { + *pa = CEC_PHYS_ADDR_INVALID; return 0; + } cmd[0] = MSGCODE_GET_BUILDDATE; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4); - if (!err) { - time_t date = (data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | data[3]; - struct tm tm; - - time_to_tm(date, 0, &tm); + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4); + if (err) + return err; + date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + time_to_tm(date, 0, &tm); + dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + dev_dbg(pulse8->dev, "Persistent config:\n"); + cmd[0] = MSGCODE_GET_AUTO_ENABLED; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + pulse8->autonomous = data[0]; + dev_dbg(pulse8->dev, "Autonomous mode: %s", + data[0] ? "on" : "off"); - dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); + cmd[0] = MSGCODE_GET_DEVICE_TYPE; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->primary_device_type[0] = data[0]; + dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]); + switch (log_addrs->primary_device_type[0]) { + case CEC_OP_PRIM_DEVTYPE_TV: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC; + break; + default: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n", + log_addrs->primary_device_type[0]); + break; } - do { - if (count) - msleep(500); - cmd[0] = MSGCODE_SET_AUTO_ENABLED; - cmd[1] = 0; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (err && count == 0) { - dev_info(pulse8->dev, "No Auto Enabled supported\n"); - return 0; - } + cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); + if (err) + return err; + log_addrs->log_addr_mask = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n", + log_addrs->log_addr_mask); + if (log_addrs->log_addr_mask) + log_addrs->num_log_addrs = 1; + + cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + *pa = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n", + cec_phys_addr_exp(*pa)); - cmd[0] = MSGCODE_GET_AUTO_ENABLED; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (!err && !data[0]) { - cmd[0] = MSGCODE_WRITE_EEPROM; - err = pulse8_send_and_wait(pulse8, cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 1); - cmd[0] = MSGCODE_GET_AUTO_ENABLED; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, - cmd[0], 1); - } - } while (!err && data[0] && count++ < 5); + cmd[0] = MSGCODE_GET_HDMI_VERSION; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->cec_version = data[0]; + dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version); - if (!err && data[0]) - err = -EIO; + cmd[0] = MSGCODE_GET_OSD_NAME; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0); + if (err) + return err; + strncpy(log_addrs->osd_name, data, 13); + dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name); - return err; + return 0; +} + +static int pulse8_apply_persistent_config(struct pulse8 *pulse8, + struct cec_log_addrs *log_addrs, + u16 pa) +{ + int err; + + err = cec_s_log_addrs(pulse8->adap, log_addrs, false); + if (err) + return err; + + cec_s_phys_addr(pulse8->adap, pa, false); + + return 0; } static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) @@ -364,9 +468,11 @@ static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) { struct pulse8 *pulse8 = adap->priv; u16 mask = 0; - u8 cmd[3]; - int err; + u16 pa = adap->phys_addr; + u8 cmd[16]; + int err = 0; + mutex_lock(&pulse8->config_lock); if (log_addr != CEC_LOG_ADDR_INVALID) mask = 1 << log_addr; cmd[0] = MSGCODE_SET_ACK_MASK; @@ -374,8 +480,106 @@ static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) cmd[2] = mask & 0xff; err = pulse8_send_and_wait(pulse8, cmd, 3, MSGCODE_COMMAND_ACCEPTED, 0); - if (mask == 0) - return 0; + if ((err && mask != 0) || pulse8->restoring_config) + goto unlock; + + cmd[0] = MSGCODE_SET_AUTO_ENABLED; + cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + pulse8->autonomous = cmd[1]; + if (log_addr == CEC_LOG_ADDR_INVALID) + goto unlock; + + cmd[0] = MSGCODE_SET_DEVICE_TYPE; + cmd[1] = adap->log_addrs.primary_device_type[0]; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + switch (adap->log_addrs.primary_device_type[0]) { + case CEC_OP_PRIM_DEVTYPE_TV: + mask = CEC_LOG_ADDR_MASK_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + mask = CEC_LOG_ADDR_MASK_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + mask = CEC_LOG_ADDR_MASK_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + mask = CEC_LOG_ADDR_MASK_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + mask = CEC_LOG_ADDR_MASK_UNREGISTERED; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + mask = CEC_LOG_ADDR_MASK_SPECIFIC; + break; + default: + mask = 0; + break; + } + cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK; + cmd[1] = mask >> 8; + cmd[2] = mask & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS; + cmd[1] = log_addr; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS; + cmd[1] = pa >> 8; + cmd[2] = pa & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_HDMI_VERSION; + cmd[1] = adap->log_addrs.cec_version; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + if (adap->log_addrs.osd_name[0]) { + size_t osd_len = strlen(adap->log_addrs.osd_name); + char *osd_str = cmd + 1; + + cmd[0] = MSGCODE_SET_OSD_NAME; + strncpy(cmd + 1, adap->log_addrs.osd_name, 13); + if (osd_len < 4) { + memset(osd_str + osd_len, ' ', 4 - osd_len); + osd_len = 4; + osd_str[osd_len] = '\0'; + strcpy(adap->log_addrs.osd_name, osd_str); + } + err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + } + +unlock: + if (pulse8->restoring_config) + pulse8->restoring_config = false; + else + pulse8->config_pending = true; + mutex_unlock(&pulse8->config_lock); return err; } @@ -437,6 +641,8 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL; struct pulse8 *pulse8; int err = -ENOMEM; + struct cec_log_addrs log_addrs = {}; + u16 pa = CEC_PHYS_ADDR_INVALID; pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL); @@ -453,12 +659,15 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) pulse8->dev = &serio->dev; serio_set_drvdata(serio, pulse8); INIT_WORK(&pulse8->work, pulse8_irq_work_handler); + mutex_init(&pulse8->write_lock); + mutex_init(&pulse8->config_lock); + pulse8->config_pending = false; err = serio_open(serio, drv); if (err) goto delete_adap; - err = pulse8_setup(pulse8, serio); + err = pulse8_setup(pulse8, serio, &log_addrs, &pa); if (err) goto close_serio; @@ -467,6 +676,18 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) goto close_serio; pulse8->dev = &pulse8->adap->devnode.dev; + + if (persistent_config && pulse8->autonomous) { + err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa); + if (err) + goto close_serio; + pulse8->restoring_config = true; + } + + INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, + pulse8_ping_eeprom_work_handler); + schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); + return 0; close_serio: @@ -479,6 +700,33 @@ free_device: return err; } +static void pulse8_ping_eeprom_work_handler(struct work_struct *work) +{ + struct pulse8 *pulse8 = + container_of(work, struct pulse8, ping_eeprom_work.work); + u8 cmd; + + schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); + cmd = MSGCODE_PING; + pulse8_send_and_wait(pulse8, &cmd, 1, + MSGCODE_COMMAND_ACCEPTED, 0); + + if (pulse8->vers < 2) + return; + + mutex_lock(&pulse8->config_lock); + if (pulse8->config_pending && persistent_config) { + dev_dbg(pulse8->dev, "writing pending config to EEPROM\n"); + cmd = MSGCODE_WRITE_EEPROM; + if (pulse8_send_and_wait(pulse8, &cmd, 1, + MSGCODE_COMMAND_ACCEPTED, 0)) + dev_info(pulse8->dev, "failed to write pending config to EEPROM\n"); + else + pulse8->config_pending = false; + } + mutex_unlock(&pulse8->config_lock); +} + static struct serio_device_id pulse8_serio_ids[] = { { .type = SERIO_RS232, diff --git a/drivers/staging/media/s5p-cec/s5p_cec.c b/drivers/staging/media/s5p-cec/s5p_cec.c index 78333273c4e5..1780a08b73c9 100644 --- a/drivers/staging/media/s5p-cec/s5p_cec.c +++ b/drivers/staging/media/s5p-cec/s5p_cec.c @@ -173,7 +173,7 @@ static int s5p_cec_probe(struct platform_device *pdev) int ret; cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); - if (!dev) + if (!cec) return -ENOMEM; cec->dev = dev; @@ -250,22 +250,9 @@ static int s5p_cec_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused s5p_cec_suspend(struct device *dev) -{ - if (pm_runtime_suspended(dev)) - return 0; - return s5p_cec_runtime_suspend(dev); -} - -static int __maybe_unused s5p_cec_resume(struct device *dev) -{ - if (pm_runtime_suspended(dev)) - return 0; - return s5p_cec_runtime_resume(dev); -} - static const struct dev_pm_ops s5p_cec_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume, NULL) }; diff --git a/drivers/staging/media/st-cec/Kconfig b/drivers/staging/media/st-cec/Kconfig new file mode 100644 index 000000000000..784d2c600aca --- /dev/null +++ b/drivers/staging/media/st-cec/Kconfig @@ -0,0 +1,8 @@ +config VIDEO_STI_HDMI_CEC + tristate "STMicroelectronics STiH4xx HDMI CEC driver" + depends on VIDEO_DEV && MEDIA_CEC && (ARCH_STI || COMPILE_TEST) + ---help--- + This is a driver for STIH4xx HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. diff --git a/drivers/staging/media/st-cec/Makefile b/drivers/staging/media/st-cec/Makefile new file mode 100644 index 000000000000..f07905e1448a --- /dev/null +++ b/drivers/staging/media/st-cec/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += stih-cec.o diff --git a/drivers/staging/media/st-cec/stih-cec.c b/drivers/staging/media/st-cec/stih-cec.c new file mode 100644 index 000000000000..214344866a6b --- /dev/null +++ b/drivers/staging/media/st-cec/stih-cec.c @@ -0,0 +1,380 @@ +/* + * drivers/staging/media/st-cec/stih-cec.c + * + * STIH4xx CEC driver + * Copyright (C) STMicroelectronic SA 2016 + * + * 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/clk.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/version.h> + +#include <media/cec.h> + +#define CEC_NAME "stih-cec" + +/* CEC registers */ +#define CEC_CLK_DIV 0x0 +#define CEC_CTRL 0x4 +#define CEC_IRQ_CTRL 0x8 +#define CEC_STATUS 0xC +#define CEC_EXT_STATUS 0x10 +#define CEC_TX_CTRL 0x14 +#define CEC_FREE_TIME_THRESH 0x18 +#define CEC_BIT_TOUT_THRESH 0x1C +#define CEC_BIT_PULSE_THRESH 0x20 +#define CEC_DATA 0x24 +#define CEC_TX_ARRAY_CTRL 0x28 +#define CEC_CTRL2 0x2C +#define CEC_TX_ERROR_STS 0x30 +#define CEC_ADDR_TABLE 0x34 +#define CEC_DATA_ARRAY_CTRL 0x38 +#define CEC_DATA_ARRAY_STATUS 0x3C +#define CEC_TX_DATA_BASE 0x40 +#define CEC_TX_DATA_TOP 0x50 +#define CEC_TX_DATA_SIZE 0x1 +#define CEC_RX_DATA_BASE 0x54 +#define CEC_RX_DATA_TOP 0x64 +#define CEC_RX_DATA_SIZE 0x1 + +/* CEC_CTRL2 */ +#define CEC_LINE_INACTIVE_EN BIT(0) +#define CEC_AUTO_BUS_ERR_EN BIT(1) +#define CEC_STOP_ON_ARB_ERR_EN BIT(2) +#define CEC_TX_REQ_WAIT_EN BIT(3) + +/* CEC_DATA_ARRAY_CTRL */ +#define CEC_TX_ARRAY_EN BIT(0) +#define CEC_RX_ARRAY_EN BIT(1) +#define CEC_TX_ARRAY_RESET BIT(2) +#define CEC_RX_ARRAY_RESET BIT(3) +#define CEC_TX_N_OF_BYTES_IRQ_EN BIT(4) +#define CEC_TX_STOP_ON_NACK BIT(7) + +/* CEC_TX_ARRAY_CTRL */ +#define CEC_TX_N_OF_BYTES 0x1F +#define CEC_TX_START BIT(5) +#define CEC_TX_AUTO_SOM_EN BIT(6) +#define CEC_TX_AUTO_EOM_EN BIT(7) + +/* CEC_IRQ_CTRL */ +#define CEC_TX_DONE_IRQ_EN BIT(0) +#define CEC_ERROR_IRQ_EN BIT(2) +#define CEC_RX_DONE_IRQ_EN BIT(3) +#define CEC_RX_SOM_IRQ_EN BIT(4) +#define CEC_RX_EOM_IRQ_EN BIT(5) +#define CEC_FREE_TIME_IRQ_EN BIT(6) +#define CEC_PIN_STS_IRQ_EN BIT(7) + +/* CEC_CTRL */ +#define CEC_IN_FILTER_EN BIT(0) +#define CEC_PWR_SAVE_EN BIT(1) +#define CEC_EN BIT(4) +#define CEC_ACK_CTRL BIT(5) +#define CEC_RX_RESET_EN BIT(6) +#define CEC_IGNORE_RX_ERROR BIT(7) + +/* CEC_STATUS */ +#define CEC_TX_DONE_STS BIT(0) +#define CEC_TX_ACK_GET_STS BIT(1) +#define CEC_ERROR_STS BIT(2) +#define CEC_RX_DONE_STS BIT(3) +#define CEC_RX_SOM_STS BIT(4) +#define CEC_RX_EOM_STS BIT(5) +#define CEC_FREE_TIME_IRQ_STS BIT(6) +#define CEC_PIN_STS BIT(7) +#define CEC_SBIT_TOUT_STS BIT(8) +#define CEC_DBIT_TOUT_STS BIT(9) +#define CEC_LPULSE_ERROR_STS BIT(10) +#define CEC_HPULSE_ERROR_STS BIT(11) +#define CEC_TX_ERROR BIT(12) +#define CEC_TX_ARB_ERROR BIT(13) +#define CEC_RX_ERROR_MIN BIT(14) +#define CEC_RX_ERROR_MAX BIT(15) + +/* Signal free time in bit periods (2.4ms) */ +#define CEC_PRESENT_INIT_SFT 7 +#define CEC_NEW_INIT_SFT 5 +#define CEC_RETRANSMIT_SFT 3 + +/* Constants for CEC_BIT_TOUT_THRESH register */ +#define CEC_SBIT_TOUT_47MS BIT(1) +#define CEC_SBIT_TOUT_48MS BIT(0) | BIT(1) +#define CEC_SBIT_TOUT_50MS BIT(2) +#define CEC_DBIT_TOUT_27MS BIT(0) +#define CEC_DBIT_TOUT_28MS BIT(1) +#define CEC_DBIT_TOUT_29MS BIT(0) | BIT(1) + +/* Constants for CEC_BIT_PULSE_THRESH register */ +#define CEC_BIT_LPULSE_03MS BIT(1) +#define CEC_BIT_HPULSE_03MS BIT(3) + +/* Constants for CEC_DATA_ARRAY_STATUS register */ +#define CEC_RX_N_OF_BYTES 0x1F +#define CEC_TX_N_OF_BYTES_SENT BIT(5) +#define CEC_RX_OVERRUN BIT(6) + +struct stih_cec { + struct cec_adapter *adap; + struct device *dev; + struct clk *clk; + void __iomem *regs; + int irq; + u32 irq_status; +}; + +static int stih_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct stih_cec *cec = adap->priv; + + if (enable) { + /* The doc says (input TCLK_PERIOD * CEC_CLK_DIV) = 0.1ms */ + unsigned long clk_freq = clk_get_rate(cec->clk); + u32 cec_clk_div = clk_freq / 10000; + + writel(cec_clk_div, cec->regs + CEC_CLK_DIV); + + /* Configuration of the durations activating a timeout */ + writel(CEC_SBIT_TOUT_47MS | (CEC_DBIT_TOUT_28MS << 4), + cec->regs + CEC_BIT_TOUT_THRESH); + + /* Configuration of the smallest allowed duration for pulses */ + writel(CEC_BIT_LPULSE_03MS | CEC_BIT_HPULSE_03MS, + cec->regs + CEC_BIT_PULSE_THRESH); + + /* Minimum received bit period threshold */ + writel(BIT(5) | BIT(7), cec->regs + CEC_TX_CTRL); + + /* Configuration of transceiver data arrays */ + writel(CEC_TX_ARRAY_EN | CEC_RX_ARRAY_EN | CEC_TX_STOP_ON_NACK, + cec->regs + CEC_DATA_ARRAY_CTRL); + + /* Configuration of the control bits for CEC Transceiver */ + writel(CEC_IN_FILTER_EN | CEC_EN | CEC_RX_RESET_EN, + cec->regs + CEC_CTRL); + + /* Clear logical addresses */ + writel(0, cec->regs + CEC_ADDR_TABLE); + + /* Clear the status register */ + writel(0x0, cec->regs + CEC_STATUS); + + /* Enable the interrupts */ + writel(CEC_TX_DONE_IRQ_EN | CEC_RX_DONE_IRQ_EN | + CEC_RX_SOM_IRQ_EN | CEC_RX_EOM_IRQ_EN | + CEC_ERROR_IRQ_EN, + cec->regs + CEC_IRQ_CTRL); + + } else { + /* Clear logical addresses */ + writel(0, cec->regs + CEC_ADDR_TABLE); + + /* Clear the status register */ + writel(0x0, cec->regs + CEC_STATUS); + + /* Disable the interrupts */ + writel(0, cec->regs + CEC_IRQ_CTRL); + } + + return 0; +} + +static int stih_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct stih_cec *cec = adap->priv; + u32 reg = readl(cec->regs + CEC_ADDR_TABLE); + + reg |= 1 << logical_addr; + + if (logical_addr == CEC_LOG_ADDR_INVALID) + reg = 0; + + writel(reg, cec->regs + CEC_ADDR_TABLE); + + return 0; +} + +static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct stih_cec *cec = adap->priv; + int i; + + /* Copy message into registers */ + for (i = 0; i < msg->len; i++) + writeb(msg->msg[i], cec->regs + CEC_TX_DATA_BASE + i); + + /* Start transmission, configure hardware to add start and stop bits + * Signal free time is handled by the hardware + */ + writel(CEC_TX_AUTO_SOM_EN | CEC_TX_AUTO_EOM_EN | CEC_TX_START | + msg->len, cec->regs + CEC_TX_ARRAY_CTRL); + + return 0; +} + +static void stih_tx_done(struct stih_cec *cec, u32 status) +{ + if (status & CEC_TX_ERROR) { + cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR, 0, 0, 0, 1); + return; + } + + if (status & CEC_TX_ARB_ERROR) { + cec_transmit_done(cec->adap, + CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); + return; + } + + if (!(status & CEC_TX_ACK_GET_STS)) { + cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK, 0, 1, 0, 0); + return; + } + + cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); +} + +static void stih_rx_done(struct stih_cec *cec, u32 status) +{ + struct cec_msg msg = {}; + u8 i; + + if (status & CEC_RX_ERROR_MIN) + return; + + if (status & CEC_RX_ERROR_MAX) + return; + + msg.len = readl(cec->regs + CEC_DATA_ARRAY_STATUS) & 0x1f; + + if (!msg.len) + return; + + if (msg.len > 16) + msg.len = 16; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = readl(cec->regs + CEC_RX_DATA_BASE + i); + + cec_received_msg(cec->adap, &msg); +} + +static irqreturn_t stih_cec_irq_handler_thread(int irq, void *priv) +{ + struct stih_cec *cec = priv; + + if (cec->irq_status & CEC_TX_DONE_STS) + stih_tx_done(cec, cec->irq_status); + + if (cec->irq_status & CEC_RX_DONE_STS) + stih_rx_done(cec, cec->irq_status); + + cec->irq_status = 0; + + return IRQ_HANDLED; +} + +static irqreturn_t stih_cec_irq_handler(int irq, void *priv) +{ + struct stih_cec *cec = priv; + + cec->irq_status = readl(cec->regs + CEC_STATUS); + writel(cec->irq_status, cec->regs + CEC_STATUS); + + return IRQ_WAKE_THREAD; +} + +static const struct cec_adap_ops sti_cec_adap_ops = { + .adap_enable = stih_cec_adap_enable, + .adap_log_addr = stih_cec_adap_log_addr, + .adap_transmit = stih_cec_adap_transmit, +}; + +static int stih_cec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct stih_cec *cec; + int ret; + + cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); + if (!cec) + return -ENOMEM; + + cec->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cec->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(cec->regs)) + return PTR_ERR(cec->regs); + + cec->irq = platform_get_irq(pdev, 0); + if (cec->irq < 0) + return cec->irq; + + ret = devm_request_threaded_irq(dev, cec->irq, stih_cec_irq_handler, + stih_cec_irq_handler_thread, 0, + pdev->name, cec); + if (ret) + return ret; + + cec->clk = devm_clk_get(dev, "cec-clk"); + if (IS_ERR(cec->clk)) { + dev_err(dev, "Cannot get cec clock\n"); + return PTR_ERR(cec->clk); + } + + cec->adap = cec_allocate_adapter(&sti_cec_adap_ops, cec, + CEC_NAME, + CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | + CEC_CAP_PHYS_ADDR | CEC_CAP_TRANSMIT, + 1, &pdev->dev); + ret = PTR_ERR_OR_ZERO(cec->adap); + if (ret) + return ret; + + ret = cec_register_adapter(cec->adap); + if (ret) { + cec_delete_adapter(cec->adap); + return ret; + } + + platform_set_drvdata(pdev, cec); + return 0; +} + +static int stih_cec_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id stih_cec_match[] = { + { + .compatible = "st,stih-cec", + }, + {}, +}; + +static struct platform_driver stih_cec_pdrv = { + .probe = stih_cec_probe, + .remove = stih_cec_remove, + .driver = { + .name = CEC_NAME, + .of_match_table = stih_cec_match, + }, +}; + +module_platform_driver(stih_cec_pdrv); + +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("STIH4xx CEC driver"); diff --git a/drivers/staging/media/tw686x-kh/Kconfig b/drivers/staging/media/tw686x-kh/Kconfig deleted file mode 100644 index 6264d30edf5a..000000000000 --- a/drivers/staging/media/tw686x-kh/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -config VIDEO_TW686X_KH - tristate "Intersil/Techwell TW686x Video For Linux" - depends on VIDEO_DEV && PCI && VIDEO_V4L2 - depends on !(VIDEO_TW686X=y || VIDEO_TW686X=m) || COMPILE_TEST - select VIDEOBUF2_DMA_SG - help - Support for Intersil/Techwell TW686x-based frame grabber cards. - - Currently supported chips: - - TW6864 (4 video channels), - - TW6865 (4 video channels, not tested, second generation chip), - - TW6868 (8 video channels but only 4 first channels using - built-in video decoder are supported, not tested), - - TW6869 (8 video channels, second generation chip). - - To compile this driver as a module, choose M here: the module - will be named tw686x-kh. diff --git a/drivers/staging/media/tw686x-kh/Makefile b/drivers/staging/media/tw686x-kh/Makefile deleted file mode 100644 index 2a36a38cf30e..000000000000 --- a/drivers/staging/media/tw686x-kh/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -tw686x-kh-objs := tw686x-kh-core.o tw686x-kh-video.o - -obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh.o diff --git a/drivers/staging/media/tw686x-kh/TODO b/drivers/staging/media/tw686x-kh/TODO deleted file mode 100644 index 480a495b11fb..000000000000 --- a/drivers/staging/media/tw686x-kh/TODO +++ /dev/null @@ -1,6 +0,0 @@ -TODO: - -- implement V4L2_FIELD_INTERLACED* mode(s). -- add audio support - -Please Cc: patches to Krzysztof Halasa <khalasa@piap.pl>. diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c deleted file mode 100644 index 03b3b62c59c4..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include "tw686x-kh.h" -#include "tw686x-kh-regs.h" - -static irqreturn_t tw686x_irq(int irq, void *dev_id) -{ - struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; - u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */ - unsigned long flags; - unsigned int handled = 0; - - if (int_status) { - spin_lock_irqsave(&dev->irq_lock, flags); - dev->dma_requests |= int_status; - spin_unlock_irqrestore(&dev->irq_lock, flags); - - if (int_status & 0xFF0000FF) - handled = tw686x_kh_video_irq(dev); - } - - return IRQ_RETVAL(handled); -} - -static int tw686x_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) -{ - struct tw686x_dev *dev; - int err; - - dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev) + - (pci_id->driver_data & TYPE_MAX_CHANNELS) * - sizeof(dev->video_channels[0]), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - sprintf(dev->name, "TW%04X", pci_dev->device); - dev->type = pci_id->driver_data; - - pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, - pci_name(pci_dev), pci_dev->irq, - (unsigned long)pci_resource_start(pci_dev, 0)); - - dev->pci_dev = pci_dev; - if (pcim_enable_device(pci_dev)) - return -EIO; - - pci_set_master(pci_dev); - - if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { - pr_err("%s: 32-bit PCI DMA not supported\n", dev->name); - return -EIO; - } - - err = pci_request_regions(pci_dev, dev->name); - if (err < 0) { - pr_err("%s: Unable to get MMIO region\n", dev->name); - return err; - } - - dev->mmio = pci_ioremap_bar(pci_dev, 0); - if (!dev->mmio) { - pr_err("%s: Unable to remap MMIO region\n", dev->name); - return -EIO; - } - - reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */ - mdelay(1); - - reg_write(dev, SRST[0], 0x3F); - if (max_channels(dev) > 4) - reg_write(dev, SRST[1], 0x3F); - reg_write(dev, DMA_CMD, 0); - reg_write(dev, DMA_CHANNEL_ENABLE, 0); - reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0); - reg_write(dev, DMA_TIMER_INTERVAL, 0x38000); - reg_write(dev, DMA_CONFIG, 0xFFFFFF04); - - spin_lock_init(&dev->irq_lock); - - err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - pr_err("%s: Unable to get IRQ\n", dev->name); - return err; - } - - err = tw686x_kh_video_init(dev); - if (err) - return err; - - pci_set_drvdata(pci_dev, dev); - return 0; -} - -static void tw686x_remove(struct pci_dev *pci_dev) -{ - struct tw686x_dev *dev = pci_get_drvdata(pci_dev); - - tw686x_kh_video_free(dev); -} - -/* driver_data is number of A/V channels */ -static const struct pci_device_id tw686x_pci_tbl[] = { - {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4}, - /* not tested */ - {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN}, - /* TW6868 supports 8 A/V channels with an external TW2865 chip - - not supported by the driver */ - {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */ - {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN}, - {} -}; - -static struct pci_driver tw686x_pci_driver = { - .name = "tw686x-kh", - .id_table = tw686x_pci_tbl, - .probe = tw686x_probe, - .remove = tw686x_remove, -}; - -MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); -MODULE_AUTHOR("Krzysztof Halasa"); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); -module_pci_driver(tw686x_pci_driver); diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h b/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h deleted file mode 100644 index 53e1889babd0..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h +++ /dev/null @@ -1,103 +0,0 @@ -/* DMA controller registers */ -#define REG8_1(a0) ((const u16[8]) {a0, a0 + 1, a0 + 2, a0 + 3, \ - a0 + 4, a0 + 5, a0 + 6, a0 + 7}) -#define REG8_2(a0) ((const u16[8]) {a0, a0 + 2, a0 + 4, a0 + 6, \ - a0 + 8, a0 + 0xA, a0 + 0xC, a0 + 0xE}) -#define REG8_8(a0) ((const u16[8]) {a0, a0 + 8, a0 + 0x10, a0 + 0x18, \ - a0 + 0x20, a0 + 0x28, a0 + 0x30, a0 + 0x38}) -#define INT_STATUS 0x00 -#define PB_STATUS 0x01 -#define DMA_CMD 0x02 -#define VIDEO_FIFO_STATUS 0x03 -#define VIDEO_CHANNEL_ID 0x04 -#define VIDEO_PARSER_STATUS 0x05 -#define SYS_SOFT_RST 0x06 -#define DMA_PAGE_TABLE0_ADDR ((const u16[8]) {0x08, 0xD0, 0xD2, 0xD4, \ - 0xD6, 0xD8, 0xDA, 0xDC}) -#define DMA_PAGE_TABLE1_ADDR ((const u16[8]) {0x09, 0xD1, 0xD3, 0xD5, \ - 0xD7, 0xD9, 0xDB, 0xDD}) -#define DMA_CHANNEL_ENABLE 0x0A -#define DMA_CONFIG 0x0B -#define DMA_TIMER_INTERVAL 0x0C -#define DMA_CHANNEL_TIMEOUT 0x0D -#define VDMA_CHANNEL_CONFIG REG8_1(0x10) -#define ADMA_P_ADDR REG8_2(0x18) -#define ADMA_B_ADDR REG8_2(0x19) -#define DMA10_P_ADDR 0x28 /* ??? */ -#define DMA10_B_ADDR 0x29 -#define VIDEO_CONTROL1 0x2A -#define VIDEO_CONTROL2 0x2B -#define AUDIO_CONTROL1 0x2C -#define AUDIO_CONTROL2 0x2D -#define PHASE_REF 0x2E -#define GPIO_REG 0x2F -#define INTL_HBAR_CTRL REG8_1(0x30) -#define AUDIO_CONTROL3 0x38 -#define VIDEO_FIELD_CTRL REG8_1(0x39) -#define HSCALER_CTRL REG8_1(0x42) -#define VIDEO_SIZE REG8_1(0x4A) -#define VIDEO_SIZE_F2 REG8_1(0x52) -#define MD_CONF REG8_1(0x60) -#define MD_INIT REG8_1(0x68) -#define MD_MAP0 REG8_1(0x70) -#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */ -#define VDMA_WHP REG8_8(0x81) -#define VDMA_B_ADDR REG8_8(0x82) -#define VDMA_F2_P_ADDR REG8_8(0x84) -#define VDMA_F2_WHP REG8_8(0x85) -#define VDMA_F2_B_ADDR REG8_8(0x86) -#define EP_REG_ADDR 0xFE -#define EP_REG_DATA 0xFF - -/* Video decoder registers */ -#define VDREG8(a0) ((const u16[8]) { \ - a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \ - a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130}) -#define VIDSTAT VDREG8(0x100) -#define BRIGHT VDREG8(0x101) -#define CONTRAST VDREG8(0x102) -#define SHARPNESS VDREG8(0x103) -#define SAT_U VDREG8(0x104) -#define SAT_V VDREG8(0x105) -#define HUE VDREG8(0x106) -#define CROP_HI VDREG8(0x107) -#define VDELAY_LO VDREG8(0x108) -#define VACTIVE_LO VDREG8(0x109) -#define HDELAY_LO VDREG8(0x10A) -#define HACTIVE_LO VDREG8(0x10B) -#define MVSN VDREG8(0x10C) -#define STATUS2 VDREG8(0x10C) -#define SDT VDREG8(0x10E) -#define SDT_EN VDREG8(0x10F) - -#define VSCALE_LO VDREG8(0x144) -#define SCALE_HI VDREG8(0x145) -#define HSCALE_LO VDREG8(0x146) -#define F2CROP_HI VDREG8(0x147) -#define F2VDELAY_LO VDREG8(0x148) -#define F2VACTIVE_LO VDREG8(0x149) -#define F2HDELAY_LO VDREG8(0x14A) -#define F2HACTIVE_LO VDREG8(0x14B) -#define F2VSCALE_LO VDREG8(0x14C) -#define F2SCALE_HI VDREG8(0x14D) -#define F2HSCALE_LO VDREG8(0x14E) -#define F2CNT VDREG8(0x14F) - -#define VDREG2(a0) ((const u16[2]) {a0, a0 + 0x100}) -#define SRST VDREG2(0x180) -#define ACNTL VDREG2(0x181) -#define ACNTL2 VDREG2(0x182) -#define CNTRL1 VDREG2(0x183) -#define CKHY VDREG2(0x184) -#define SHCOR VDREG2(0x185) -#define CORING VDREG2(0x186) -#define CLMPG VDREG2(0x187) -#define IAGC VDREG2(0x188) -#define VCTRL1 VDREG2(0x18F) -#define MISC1 VDREG2(0x194) -#define LOOP VDREG2(0x195) -#define MISC2 VDREG2(0x196) - -#define CLMD VDREG2(0x197) -#define AIGAIN ((const u16[8]) {0x1D0, 0x1D1, 0x1D2, 0x1D3, \ - 0x2D0, 0x2D1, 0x2D2, 0x2D3}) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c deleted file mode 100644 index 9bf32aec2fc6..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <media/v4l2-common.h> -#include <media/v4l2-event.h> -#include "tw686x-kh.h" -#include "tw686x-kh-regs.h" - -#define MAX_SG_ENTRY_SIZE (/* 8192 - 128 */ 4096) -#define MAX_SG_DESC_COUNT 256 /* PAL 704x576 needs up to 198 4-KB pages */ - -static const struct tw686x_format formats[] = { - { - .name = "4:2:2 packed, UYVY", /* aka Y422 */ - .fourcc = V4L2_PIX_FMT_UYVY, - .mode = 0, - .depth = 16, - }, { -#if 0 - .name = "4:2:0 packed, YUV", - .mode = 1, /* non-standard */ - .depth = 12, - }, { - .name = "4:1:1 packed, YUV", - .mode = 2, /* non-standard */ - .depth = 12, - }, { -#endif - .name = "4:1:1 packed, YUV", - .fourcc = V4L2_PIX_FMT_Y41P, - .mode = 3, - .depth = 12, - }, { - .name = "15 bpp RGB", - .fourcc = V4L2_PIX_FMT_RGB555, - .mode = 4, - .depth = 16, - }, { - .name = "16 bpp RGB", - .fourcc = V4L2_PIX_FMT_RGB565, - .mode = 5, - .depth = 16, - }, { - .name = "4:2:2 packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .mode = 6, - .depth = 16, - } - /* mode 7 is "reserved" */ -}; - -static const v4l2_std_id video_standards[7] = { - V4L2_STD_NTSC, - V4L2_STD_PAL, - V4L2_STD_SECAM, - V4L2_STD_NTSC_443, - V4L2_STD_PAL_M, - V4L2_STD_PAL_N, - V4L2_STD_PAL_60, -}; - -static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) -{ - unsigned int cnt; - - for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) - if (formats[cnt].fourcc == fourcc) - return &formats[cnt]; - return NULL; -} - -static void tw686x_get_format(struct tw686x_video_channel *vc, - struct v4l2_format *f) -{ - const struct tw686x_format *format; - unsigned int width, height, height_div = 1; - - format = format_by_fourcc(f->fmt.pix.pixelformat); - if (!format) { - format = &formats[0]; - f->fmt.pix.pixelformat = format->fourcc; - } - - width = 704; - if (f->fmt.pix.width < width * 3 / 4 /* halfway */) - width /= 2; - - height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480; - if (f->fmt.pix.height < height * 3 / 4 /* halfway */) - height_div = 2; - - switch (f->fmt.pix.field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - height_div = 2; - break; - case V4L2_FIELD_SEQ_BT: - if (height_div > 1) - f->fmt.pix.field = V4L2_FIELD_BOTTOM; - break; - default: - if (height_div > 1) - f->fmt.pix.field = V4L2_FIELD_TOP; - else - f->fmt.pix.field = V4L2_FIELD_SEQ_TB; - } - height /= height_div; - - f->fmt.pix.width = width; - f->fmt.pix.height = height; - f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -} - -/* video queue operations */ - -static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - unsigned int size = vc->width * vc->height * vc->format->depth / 8; - - if (*nbuffers < 2) - *nbuffers = 2; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - sizes[0] = size; - *nplanes = 1; /* packed formats only */ - return 0; -} - -static void tw686x_buf_queue(struct vb2_buffer *vb) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct tw686x_vb2_buf *buf; - - buf = container_of(vbuf, struct tw686x_vb2_buf, vb); - - spin_lock(&vc->qlock); - list_add_tail(&buf->list, &vc->vidq_queued); - spin_unlock(&vc->qlock); -} - -static void setup_descs(struct tw686x_video_channel *vc, unsigned int n) -{ -loop: - while (!list_empty(&vc->vidq_queued)) { - struct vdma_desc *descs = vc->sg_descs[n]; - struct tw686x_vb2_buf *buf; - struct sg_table *vbuf; - struct scatterlist *sg; - unsigned int buf_len, count = 0; - int i; - - buf = list_first_entry(&vc->vidq_queued, struct tw686x_vb2_buf, - list); - list_del(&buf->list); - - buf_len = vc->width * vc->height * vc->format->depth / 8; - if (vb2_plane_size(&buf->vb.vb2_buf, 0) < buf_len) { - pr_err("Video buffer size too small\n"); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - goto loop; /* try another */ - } - - vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); - for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { - dma_addr_t phys = sg_dma_address(sg); - unsigned int len = sg_dma_len(sg); - - while (len && buf_len) { - unsigned int entry_len = min_t(unsigned int, len, - MAX_SG_ENTRY_SIZE); - entry_len = min(entry_len, buf_len); - if (count == MAX_SG_DESC_COUNT) { - pr_err("Video buffer size too fragmented\n"); - vb2_buffer_done(&buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - goto loop; - } - descs[count].phys = cpu_to_le32(phys); - descs[count++].flags_length = - cpu_to_le32(0x40000000 /* available */ | - entry_len); - phys += entry_len; - len -= entry_len; - buf_len -= entry_len; - } - if (!buf_len) - break; - } - - /* clear the remaining entries */ - while (count < MAX_SG_DESC_COUNT) { - descs[count].phys = 0; - descs[count++].flags_length = 0; /* unavailable */ - } - - buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; - vc->curr_bufs[n] = buf; - return; - } - vc->curr_bufs[n] = NULL; -} - -/* On TW6864 and TW6868, all channels share the pair of video DMA SG tables, - with 10-bit start_idx and end_idx determining start and end of frame buffer - for particular channel. - TW6868 with all its 8 channels would be problematic (only 127 SG entries per - channel) but we support only 4 channels on this chip anyway (the first - 4 channels are driven with internal video decoder, the other 4 would require - an external TW286x part). - - On TW6865 and TW6869, each channel has its own DMA SG table, with indexes - starting with 0. Both chips have complete sets of internal video decoders - (respectively 4 or 8-channel). - - All chips have separate SG tables for two video frames. */ - -static void setup_dma_cfg(struct tw686x_video_channel *vc) -{ - unsigned int field_width = 704; - unsigned int field_height = (vc->video_standard & V4L2_STD_625_50) ? - 288 : 240; - unsigned int start_idx = is_second_gen(vc->dev) ? 0 : - vc->ch * MAX_SG_DESC_COUNT; - unsigned int end_idx = start_idx + MAX_SG_DESC_COUNT - 1; - u32 dma_cfg = (0 << 30) /* input selection */ | - (1 << 29) /* field2 dropped (if any) */ | - ((vc->height < 300) << 28) /* field dropping */ | - (1 << 27) /* master */ | - (0 << 25) /* master channel (for slave only) */ | - (0 << 24) /* (no) vertical (line) decimation */ | - ((vc->width < 400) << 23) /* horizontal decimation */ | - (vc->format->mode << 20) /* output video format */ | - (end_idx << 10) /* DMA end index */ | - start_idx /* DMA start index */; - u32 reg; - - reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], dma_cfg); - reg_write(vc->dev, VIDEO_SIZE[vc->ch], (1 << 31) | (field_height << 16) - | field_width); - reg = reg_read(vc->dev, VIDEO_CONTROL1); - if (vc->video_standard & V4L2_STD_625_50) - reg |= 1 << (vc->ch + 13); - else - reg &= ~(1 << (vc->ch + 13)); - reg_write(vc->dev, VIDEO_CONTROL1, reg); -} - -static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - struct tw686x_dev *dev = vc->dev; - u32 dma_ch_mask; - unsigned int n; - - setup_dma_cfg(vc); - - /* queue video buffers if available */ - spin_lock(&vc->qlock); - for (n = 0; n < 2; n++) - setup_descs(vc, n); - spin_unlock(&vc->qlock); - - dev->video_active |= 1 << vc->ch; - vc->seq = 0; - dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE) | (1 << vc->ch); - reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); - reg_write(dev, DMA_CMD, (1 << 31) | dma_ch_mask); - return 0; -} - -static void tw686x_stop_streaming(struct vb2_queue *vq) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - struct tw686x_dev *dev = vc->dev; - u32 dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); - u32 dma_cmd = reg_read(dev, DMA_CMD); - unsigned int n; - - dma_ch_mask &= ~(1 << vc->ch); - reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); - - dev->video_active &= ~(1 << vc->ch); - - dma_cmd &= ~(1 << vc->ch); - reg_write(dev, DMA_CMD, dma_cmd); - - if (!dev->video_active) { - reg_write(dev, DMA_CMD, 0); - reg_write(dev, DMA_CHANNEL_ENABLE, 0); - } - - spin_lock(&vc->qlock); - while (!list_empty(&vc->vidq_queued)) { - struct tw686x_vb2_buf *buf; - - buf = list_entry(vc->vidq_queued.next, struct tw686x_vb2_buf, - list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - for (n = 0; n < 2; n++) - if (vc->curr_bufs[n]) - vb2_buffer_done(&vc->curr_bufs[n]->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - - spin_unlock(&vc->qlock); -} - -static struct vb2_ops tw686x_video_qops = { - .queue_setup = tw686x_queue_setup, - .buf_queue = tw686x_buf_queue, - .start_streaming = tw686x_start_streaming, - .stop_streaming = tw686x_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct tw686x_video_channel *vc; - struct tw686x_dev *dev; - unsigned int ch; - - vc = container_of(ctrl->handler, struct tw686x_video_channel, - ctrl_handler); - dev = vc->dev; - ch = vc->ch; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - reg_write(dev, BRIGHT[ch], ctrl->val & 0xFF); - return 0; - - case V4L2_CID_CONTRAST: - reg_write(dev, CONTRAST[ch], ctrl->val); - return 0; - - case V4L2_CID_SATURATION: - reg_write(dev, SAT_U[ch], ctrl->val); - reg_write(dev, SAT_V[ch], ctrl->val); - return 0; - - case V4L2_CID_HUE: - reg_write(dev, HUE[ch], ctrl->val & 0xFF); - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops ctrl_ops = { - .s_ctrl = tw686x_s_ctrl, -}; - -static int tw686x_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - f->fmt.pix.width = vc->width; - f->fmt.pix.height = vc->height; - f->fmt.pix.field = vc->field; - f->fmt.pix.pixelformat = vc->format->fourcc; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.bytesperline = f->fmt.pix.width * vc->format->depth / 8; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; -} - -static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - tw686x_get_format(video_drvdata(file), f); - return 0; -} - -static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - tw686x_get_format(vc, f); - vc->format = format_by_fourcc(f->fmt.pix.pixelformat); - vc->field = f->fmt.pix.field; - vc->width = f->fmt.pix.width; - vc->height = f->fmt.pix.height; - return 0; -} - -static int tw686x_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - struct tw686x_dev *dev = vc->dev; - - strcpy(cap->driver, "tw686x-kh"); - strcpy(cap->card, dev->name); - sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci_dev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - unsigned int cnt; - u32 sdt = 0; /* default */ - - for (cnt = 0; cnt < ARRAY_SIZE(video_standards); cnt++) - if (id & video_standards[cnt]) { - sdt = cnt; - break; - } - - reg_write(vc->dev, SDT[vc->ch], sdt); - vc->video_standard = video_standards[sdt]; - return 0; -} - -static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - *id = vc->video_standard; - return 0; -} - -static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].fourcc; - return 0; -} - -static int tw686x_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&sp->parm.capture, 0, sizeof(sp->parm.capture)); - sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - v4l2_video_std_frame_period(vc->video_standard, - &sp->parm.capture.timeperframe); - - return 0; -} - -static int tw686x_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - /* the chip has internal multiplexer, support can be added - if the actual hw uses it */ - if (inp->index) - return -EINVAL; - - snprintf(inp->name, sizeof(inp->name), "Composite"); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; - inp->capabilities = V4L2_IN_CAP_STD; - return 0; -} - -static int tw686x_g_input(struct file *file, void *priv, unsigned int *v) -{ - *v = 0; - return 0; -} - -static int tw686x_s_input(struct file *file, void *priv, unsigned int v) -{ - if (v) - return -EINVAL; - return 0; -} - -static const struct v4l2_file_operations tw686x_video_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .unlocked_ioctl = video_ioctl2, - .release = vb2_fop_release, - .poll = vb2_fop_poll, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { - .vidioc_querycap = tw686x_querycap, - .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_std = tw686x_g_std, - .vidioc_s_std = tw686x_s_std, - .vidioc_g_parm = tw686x_g_parm, - .vidioc_enum_input = tw686x_enum_input, - .vidioc_g_input = tw686x_g_input, - .vidioc_s_input = tw686x_s_input, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int video_thread(void *arg) -{ - struct tw686x_dev *dev = arg; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&dev->video_thread_wait, &wait); - - while (1) { - long timeout = schedule_timeout_interruptible(HZ); - unsigned int ch; - - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc; - unsigned long flags; - u32 request, n, stat = VB2_BUF_STATE_DONE; - - vc = &dev->video_channels[ch]; - if (!(dev->video_active & (1 << ch))) - continue; - - spin_lock_irq(&dev->irq_lock); - request = dev->dma_requests & (0x01000001 << ch); - if (request) - dev->dma_requests &= ~request; - spin_unlock_irq(&dev->irq_lock); - - if (!request) - continue; - - request >>= ch; - - /* handle channel events */ - if ((request & 0x01000000) | - (reg_read(dev, VIDEO_FIFO_STATUS) & (0x01010001 << ch)) | - (reg_read(dev, VIDEO_PARSER_STATUS) & (0x00000101 << ch))) { - /* DMA Errors - reset channel */ - u32 reg; - - spin_lock_irqsave(&dev->irq_lock, flags); - reg = reg_read(dev, DMA_CMD); - /* Reset DMA channel */ - reg_write(dev, DMA_CMD, reg & ~(1 << ch)); - reg_write(dev, DMA_CMD, reg); - spin_unlock_irqrestore(&dev->irq_lock, flags); - stat = VB2_BUF_STATE_ERROR; - } - - /* handle video stream */ - mutex_lock(&vc->vb_mutex); - spin_lock(&vc->qlock); - n = !!(reg_read(dev, PB_STATUS) & (1 << ch)); - if (vc->curr_bufs[n]) { - struct vb2_v4l2_buffer *vb; - - vb = &vc->curr_bufs[n]->vb; - vb->vb2_buf.timestamp = ktime_get_ns(); - vb->field = vc->field; - if (V4L2_FIELD_HAS_BOTH(vc->field)) - vb->sequence = vc->seq++; - else - vb->sequence = (vc->seq++) / 2; - vb2_set_plane_payload(&vb->vb2_buf, 0, - vc->width * vc->height * vc->format->depth / 8); - vb2_buffer_done(&vb->vb2_buf, stat); - } - setup_descs(vc, n); - spin_unlock(&vc->qlock); - mutex_unlock(&vc->vb_mutex); - } - try_to_freeze(); - } - - remove_wait_queue(&dev->video_thread_wait, &wait); - return 0; -} - -int tw686x_kh_video_irq(struct tw686x_dev *dev) -{ - unsigned long flags, handled = 0; - u32 requests; - - spin_lock_irqsave(&dev->irq_lock, flags); - requests = dev->dma_requests; - spin_unlock_irqrestore(&dev->irq_lock, flags); - - if (requests & dev->video_active) { - wake_up_interruptible_all(&dev->video_thread_wait); - handled = 1; - } - return handled; -} - -void tw686x_kh_video_free(struct tw686x_dev *dev) -{ - unsigned int ch, n; - - if (dev->video_thread) - kthread_stop(dev->video_thread); - - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc = &dev->video_channels[ch]; - - v4l2_ctrl_handler_free(&vc->ctrl_handler); - if (vc->device) - video_unregister_device(vc->device); - for (n = 0; n < 2; n++) { - struct dma_desc *descs = &vc->sg_tables[n]; - - if (descs->virt) - pci_free_consistent(dev->pci_dev, descs->size, - descs->virt, descs->phys); - } - } - - v4l2_device_unregister(&dev->v4l2_dev); -} - -#define SG_TABLE_SIZE (MAX_SG_DESC_COUNT * sizeof(struct vdma_desc)) - -int tw686x_kh_video_init(struct tw686x_dev *dev) -{ - unsigned int ch, n; - int err; - - init_waitqueue_head(&dev->video_thread_wait); - - err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); - if (err) - return err; - - reg_write(dev, VIDEO_CONTROL1, 0); /* NTSC, disable scaler */ - reg_write(dev, PHASE_REF, 0x00001518); /* Scatter-gather DMA mode */ - - /* setup required SG table sizes */ - for (n = 0; n < 2; n++) - if (is_second_gen(dev)) { - /* TW 6865, TW6869 - each channel needs a pair of - descriptor tables */ - for (ch = 0; ch < max_channels(dev); ch++) - dev->video_channels[ch].sg_tables[n].size = - SG_TABLE_SIZE; - - } else - /* TW 6864, TW6868 - we need to allocate a pair of - descriptor tables, common for all channels. - Each table will be bigger than 4 KB. */ - dev->video_channels[0].sg_tables[n].size = - max_channels(dev) * SG_TABLE_SIZE; - - /* allocate SG tables and initialize video channels */ - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc = &dev->video_channels[ch]; - struct video_device *vdev; - - mutex_init(&vc->vb_mutex); - spin_lock_init(&vc->qlock); - INIT_LIST_HEAD(&vc->vidq_queued); - - vc->dev = dev; - vc->ch = ch; - - /* default settings: NTSC */ - vc->format = &formats[0]; - vc->video_standard = V4L2_STD_NTSC; - reg_write(vc->dev, SDT[vc->ch], 0); - vc->field = V4L2_FIELD_SEQ_BT; - vc->width = 704; - vc->height = 480; - - for (n = 0; n < 2; n++) { - void *cpu; - - if (vc->sg_tables[n].size) { - unsigned int reg = n ? DMA_PAGE_TABLE1_ADDR[ch] : - DMA_PAGE_TABLE0_ADDR[ch]; - - cpu = pci_alloc_consistent(dev->pci_dev, - vc->sg_tables[n].size, - &vc->sg_tables[n].phys); - if (!cpu) { - pr_err("Error allocating video DMA scatter-gather tables\n"); - err = -ENOMEM; - goto error; - } - vc->sg_tables[n].virt = cpu; - reg_write(dev, reg, vc->sg_tables[n].phys); - } else - cpu = dev->video_channels[0].sg_tables[n].virt + - ch * SG_TABLE_SIZE; - - vc->sg_descs[n] = cpu; - } - - reg_write(dev, VCTRL1[0], 0x24); - reg_write(dev, LOOP[0], 0xA5); - if (max_channels(dev) > 4) { - reg_write(dev, VCTRL1[1], 0x24); - reg_write(dev, LOOP[1], 0xA5); - } - reg_write(dev, VIDEO_FIELD_CTRL[ch], 0); - reg_write(dev, VDELAY_LO[ch], 0x14); - - vdev = video_device_alloc(); - if (!vdev) { - pr_warn("Unable to allocate video device\n"); - err = -ENOMEM; - goto error; - } - - vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; - vc->vidq.drv_priv = vc; - vc->vidq.buf_struct_size = sizeof(struct tw686x_vb2_buf); - vc->vidq.ops = &tw686x_video_qops; - vc->vidq.mem_ops = &vb2_dma_sg_memops; - vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vc->vidq.min_buffers_needed = 2; - vc->vidq.lock = &vc->vb_mutex; - vc->vidq.dev = &dev->pci_dev->dev; - vc->vidq.gfp_flags = GFP_DMA32; - - err = vb2_queue_init(&vc->vidq); - if (err) - goto error; - - strcpy(vdev->name, "TW686x-video"); - snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); - vdev->fops = &tw686x_video_fops; - vdev->ioctl_ops = &tw686x_video_ioctl_ops; - vdev->release = video_device_release; - vdev->v4l2_dev = &dev->v4l2_dev; - vdev->queue = &vc->vidq; - vdev->tvnorms = V4L2_STD_ALL; - vdev->minor = -1; - vdev->lock = &vc->vb_mutex; - - dev->video_channels[ch].device = vdev; - video_set_drvdata(vdev, vc); - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (err < 0) - goto error; - - v4l2_ctrl_handler_init(&vc->ctrl_handler, - 4 /* number of controls */); - vdev->ctrl_handler = &vc->ctrl_handler; - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 64); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, V4L2_CID_HUE, - -124, 127, 1, 0); - err = vc->ctrl_handler.error; - if (err) - goto error; - - v4l2_ctrl_handler_setup(&vc->ctrl_handler); - } - - dev->video_thread = kthread_run(video_thread, dev, "tw686x_video"); - if (IS_ERR(dev->video_thread)) { - err = PTR_ERR(dev->video_thread); - goto error; - } - - return 0; - -error: - tw686x_kh_video_free(dev); - return err; -} diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh.h b/drivers/staging/media/tw686x-kh/tw686x-kh.h deleted file mode 100644 index 6284a90d6fe3..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include <linux/delay.h> -#include <linux/freezer.h> -#include <linux/interrupt.h> -#include <linux/kthread.h> -#include <linux/mutex.h> -#include <linux/pci.h> -#include <media/videobuf2-dma-sg.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> - -#define TYPE_MAX_CHANNELS 0x0F -#define TYPE_SECOND_GEN 0x10 - -struct tw686x_format { - char *name; - unsigned int fourcc; - unsigned int depth; - unsigned int mode; -}; - -struct dma_desc { - dma_addr_t phys; - void *virt; - unsigned int size; -}; - -struct vdma_desc { - __le32 flags_length; /* 3 MSBits for flags, 13 LSBits for length */ - __le32 phys; -}; - -struct tw686x_vb2_buf { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct tw686x_video_channel { - struct tw686x_dev *dev; - - struct vb2_queue vidq; - struct list_head vidq_queued; - struct video_device *device; - struct dma_desc sg_tables[2]; - struct tw686x_vb2_buf *curr_bufs[2]; - struct vdma_desc *sg_descs[2]; - - struct v4l2_ctrl_handler ctrl_handler; - const struct tw686x_format *format; - struct mutex vb_mutex; - spinlock_t qlock; - v4l2_std_id video_standard; - unsigned int width, height; - enum v4l2_field field; /* supported TOP, BOTTOM, SEQ_TB and SEQ_BT */ - unsigned int seq; /* video field or frame counter */ - unsigned int ch; -}; - -/* global device status */ -struct tw686x_dev { - spinlock_t irq_lock; - - struct v4l2_device v4l2_dev; - struct snd_card *card; /* sound card */ - - unsigned int video_active; /* active video channel mask */ - - char name[32]; - unsigned int type; - struct pci_dev *pci_dev; - __u32 __iomem *mmio; - - struct task_struct *video_thread; - wait_queue_head_t video_thread_wait; - u32 dma_requests; - - struct tw686x_video_channel video_channels[0]; -}; - -static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned int reg) -{ - return readl(dev->mmio + reg); -} - -static inline void reg_write(struct tw686x_dev *dev, unsigned int reg, - uint32_t value) -{ - writel(value, dev->mmio + reg); -} - -static inline unsigned int max_channels(struct tw686x_dev *dev) -{ - return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ -} - -static inline unsigned int is_second_gen(struct tw686x_dev *dev) -{ - /* each channel has its own DMA SG table */ - return dev->type & TYPE_SECOND_GEN; -} - -int tw686x_kh_video_irq(struct tw686x_dev *dev); -int tw686x_kh_video_init(struct tw686x_dev *dev); -void tw686x_kh_video_free(struct tw686x_dev *dev); |