summaryrefslogtreecommitdiffstats
path: root/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-15 20:30:12 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-15 20:30:12 -0800
commit5d352e69c60e54b5f04d6e337a1d2bf0dbf3d94a (patch)
tree214e6b190715267ed02b6d415396c2bbcf2eaace /drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c
parent93ea0eb7d77afab34657715630d692a78b8cea6a (diff)
parentf2ecc3d0787e05d9145722feed01d4a11ab6bec1 (diff)
downloadlinux-stable-5d352e69c60e54b5f04d6e337a1d2bf0dbf3d94a.tar.gz
linux-stable-5d352e69c60e54b5f04d6e337a1d2bf0dbf3d94a.tar.bz2
linux-stable-5d352e69c60e54b5f04d6e337a1d2bf0dbf3d94a.zip
Merge tag 'media/v4.15-1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - Documentation for digital TV (both kAPI and uAPI) are now in sync with the implementation (except for legacy/deprecated ioctls). This is a major step, as there were always a gap there - New sensor driver: imx274 - New cec driver: cec-gpio - New platform driver for rockship rga and tegra CEC - New RC driver: tango-ir - Several cleanups at atomisp driver - Core improvements for RC, CEC, V4L2 async probing support and DVB - Lots of drivers cleanup, fixes and improvements. * tag 'media/v4.15-1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (332 commits) dvb_frontend: don't use-after-free the frontend struct media: dib0700: fix invalid dvb_detach argument media: v4l2-ctrls: Don't validate BITMASK twice media: s5p-mfc: fix lockdep warning media: dvb-core: always call invoke_release() in fe_free() media: usb: dvb-usb-v2: dvb_usb_core: remove redundant code in dvb_usb_fe_sleep media: au0828: make const array addr_list static media: cx88: make const arrays default_addr_list and pvr2000_addr_list static media: drxd: make const array fastIncrDecLUT static media: usb: fix spelling mistake: "synchronuously" -> "synchronously" media: ddbridge: fix build warnings media: av7110: avoid 2038 overflow in debug print media: Don't do DMA on stack for firmware upload in the AS102 driver media: v4l: async: fix unregister for implicitly registered sub-device notifiers media: v4l: async: fix return of unitialized variable ret media: imx274: fix missing return assignment from call to imx274_mode_regs media: camss-vfe: always initialize reg at vfe_set_xbar_cfg() media: atomisp: make function calls cleaner media: atomisp: get rid of storage_class.h media: atomisp: get rid of wrong stddef.h include ...
Diffstat (limited to 'drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c')
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c b/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c
new file mode 100644
index 000000000000..81e5ec0c2b64
--- /dev/null
+++ b/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * 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/i2c.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include "../include/linux/libmsrlisthelper.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/* Tagged binary data container structure definitions. */
+struct tbd_header {
+ uint32_t tag; /*!< Tag identifier, also checks endianness */
+ uint32_t size; /*!< Container size including this header */
+ uint32_t version; /*!< Version, format 0xYYMMDDVV */
+ uint32_t revision; /*!< Revision, format 0xYYMMDDVV */
+ uint32_t config_bits; /*!< Configuration flag bits set */
+ uint32_t checksum; /*!< Global checksum, header included */
+} __packed;
+
+struct tbd_record_header {
+ uint32_t size; /*!< Size of record including header */
+ uint8_t format_id; /*!< tbd_format_t enumeration values used */
+ uint8_t packing_key; /*!< Packing method; 0 = no packing */
+ uint16_t class_id; /*!< tbd_class_t enumeration values used */
+} __packed;
+
+struct tbd_data_record_header {
+ uint16_t next_offset;
+ uint16_t flags;
+ uint16_t data_offset;
+ uint16_t data_size;
+} __packed;
+
+#define TBD_CLASS_DRV_ID 2
+
+static int set_msr_configuration(struct i2c_client *client, uint8_t *bufptr,
+ unsigned int size)
+{
+ /* The configuration data contains any number of sequences where
+ * the first byte (that is, uint8_t) that marks the number of bytes
+ * in the sequence to follow, is indeed followed by the indicated
+ * number of bytes of actual data to be written to sensor.
+ * By convention, the first two bytes of actual data should be
+ * understood as an address in the sensor address space (hibyte
+ * followed by lobyte) where the remaining data in the sequence
+ * will be written. */
+
+ uint8_t *ptr = bufptr;
+ while (ptr < bufptr + size) {
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ };
+ int ret;
+
+ /* How many bytes */
+ msg.len = *ptr++;
+ /* Where the bytes are located */
+ msg.buf = ptr;
+ ptr += msg.len;
+
+ if (ptr > bufptr + size)
+ /* Accessing data beyond bounds is not tolerated */
+ return -EINVAL;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c write error: %d", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int parse_and_apply(struct i2c_client *client, uint8_t *buffer,
+ unsigned int size)
+{
+ uint8_t *endptr8 = buffer + size;
+ struct tbd_data_record_header *header =
+ (struct tbd_data_record_header *)buffer;
+
+ /* There may be any number of datasets present */
+ unsigned int dataset = 0;
+
+ do {
+ /* In below, four variables are read from buffer */
+ if ((uint8_t *)header + sizeof(*header) > endptr8)
+ return -EINVAL;
+
+ /* All data should be located within given buffer */
+ if ((uint8_t *)header + header->data_offset +
+ header->data_size > endptr8)
+ return -EINVAL;
+
+ /* We have a new valid dataset */
+ dataset++;
+ /* See whether there is MSR data */
+ /* If yes, update the reg info */
+ if (header->data_size && (header->flags & 1)) {
+ int ret;
+
+ dev_info(&client->dev,
+ "New MSR data for sensor driver (dataset %02d) size:%d\n",
+ dataset, header->data_size);
+ ret = set_msr_configuration(client,
+ buffer + header->data_offset,
+ header->data_size);
+ if (ret)
+ return ret;
+ }
+ header = (struct tbd_data_record_header *)(buffer +
+ header->next_offset);
+ } while (header->next_offset);
+
+ return 0;
+}
+
+int apply_msr_data(struct i2c_client *client, const struct firmware *fw)
+{
+ struct tbd_header *header;
+ struct tbd_record_header *record;
+
+ if (!fw) {
+ dev_warn(&client->dev, "Drv data is not loaded.\n");
+ return -EINVAL;
+ }
+
+ if (sizeof(*header) > fw->size)
+ return -EINVAL;
+
+ header = (struct tbd_header *)fw->data;
+ /* Check that we have drvb block. */
+ if (memcmp(&header->tag, "DRVB", 4))
+ return -EINVAL;
+
+ /* Check the size */
+ if (header->size != fw->size)
+ return -EINVAL;
+
+ if (sizeof(*header) + sizeof(*record) > fw->size)
+ return -EINVAL;
+
+ record = (struct tbd_record_header *)(header + 1);
+ /* Check that class id mathes tbd's drv id. */
+ if (record->class_id != TBD_CLASS_DRV_ID)
+ return -EINVAL;
+
+ /* Size 0 shall not be treated as an error */
+ if (!record->size)
+ return 0;
+
+ return parse_and_apply(client, (uint8_t *)(record + 1), record->size);
+}
+EXPORT_SYMBOL_GPL(apply_msr_data);
+
+int load_msr_list(struct i2c_client *client, char *name,
+ const struct firmware **fw)
+{
+ int ret = request_firmware(fw, name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "Error %d while requesting firmware %s\n",
+ ret, name);
+ return ret;
+ }
+ dev_info(&client->dev, "Received %lu bytes drv data\n",
+ (unsigned long)(*fw)->size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(load_msr_list);
+
+void release_msr_list(struct i2c_client *client, const struct firmware *fw)
+{
+ release_firmware(fw);
+}
+EXPORT_SYMBOL_GPL(release_msr_list);
+
+static int init_msrlisthelper(void)
+{
+ return 0;
+}
+
+static void exit_msrlisthelper(void)
+{
+}
+
+module_init(init_msrlisthelper);
+module_exit(exit_msrlisthelper);
+
+MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>");
+MODULE_LICENSE("GPL");