|
@@ -15,6 +15,7 @@
|
|
|
#include <media/v4l2-dv-timings.h>
|
|
|
#include <linux/math64.h>
|
|
|
#include <linux/hdmi.h>
|
|
|
+#include <media/cec.h>
|
|
|
|
|
|
MODULE_AUTHOR("Hans Verkuil");
|
|
|
MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions");
|
|
@@ -981,3 +982,153 @@ v4l2_hdmi_rx_colorimetry(const struct hdmi_avi_infoframe *avi,
|
|
|
return c;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(v4l2_hdmi_rx_colorimetry);
|
|
|
+
|
|
|
+/**
|
|
|
+ * v4l2_get_edid_phys_addr() - find and return the physical address
|
|
|
+ *
|
|
|
+ * @edid: pointer to the EDID data
|
|
|
+ * @size: size in bytes of the EDID data
|
|
|
+ * @offset: If not %NULL then the location of the physical address
|
|
|
+ * bytes in the EDID will be returned here. This is set to 0
|
|
|
+ * if there is no physical address found.
|
|
|
+ *
|
|
|
+ * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none.
|
|
|
+ */
|
|
|
+u16 v4l2_get_edid_phys_addr(const u8 *edid, unsigned int size,
|
|
|
+ unsigned int *offset)
|
|
|
+{
|
|
|
+ unsigned int loc = cec_get_edid_spa_location(edid, size);
|
|
|
+
|
|
|
+ if (offset)
|
|
|
+ *offset = loc;
|
|
|
+ if (loc == 0)
|
|
|
+ return CEC_PHYS_ADDR_INVALID;
|
|
|
+ return (edid[loc] << 8) | edid[loc + 1];
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(v4l2_get_edid_phys_addr);
|
|
|
+
|
|
|
+/**
|
|
|
+ * v4l2_set_edid_phys_addr() - find and set the physical address
|
|
|
+ *
|
|
|
+ * @edid: pointer to the EDID data
|
|
|
+ * @size: size in bytes of the EDID data
|
|
|
+ * @phys_addr: the new physical address
|
|
|
+ *
|
|
|
+ * This function finds the location of the physical address in the EDID
|
|
|
+ * and fills in the given physical address and updates the checksum
|
|
|
+ * at the end of the EDID block. It does nothing if the EDID doesn't
|
|
|
+ * contain a physical address.
|
|
|
+ */
|
|
|
+void v4l2_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
|
|
|
+{
|
|
|
+ unsigned int loc = cec_get_edid_spa_location(edid, size);
|
|
|
+ u8 sum = 0;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ if (loc == 0)
|
|
|
+ return;
|
|
|
+ edid[loc] = phys_addr >> 8;
|
|
|
+ edid[loc + 1] = phys_addr & 0xff;
|
|
|
+ loc &= ~0x7f;
|
|
|
+
|
|
|
+ /* update the checksum */
|
|
|
+ for (i = loc; i < loc + 127; i++)
|
|
|
+ sum += edid[i];
|
|
|
+ edid[i] = 256 - sum;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(v4l2_set_edid_phys_addr);
|
|
|
+
|
|
|
+/**
|
|
|
+ * v4l2_phys_addr_for_input() - calculate the PA for an input
|
|
|
+ *
|
|
|
+ * @phys_addr: the physical address of the parent
|
|
|
+ * @input: the number of the input port, must be between 1 and 15
|
|
|
+ *
|
|
|
+ * This function calculates a new physical address based on the input
|
|
|
+ * port number. For example:
|
|
|
+ *
|
|
|
+ * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0
|
|
|
+ *
|
|
|
+ * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0
|
|
|
+ *
|
|
|
+ * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5
|
|
|
+ *
|
|
|
+ * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth.
|
|
|
+ *
|
|
|
+ * Return: the new physical address or CEC_PHYS_ADDR_INVALID.
|
|
|
+ */
|
|
|
+u16 v4l2_phys_addr_for_input(u16 phys_addr, u8 input)
|
|
|
+{
|
|
|
+ /* Check if input is sane */
|
|
|
+ if (WARN_ON(input == 0 || input > 0xf))
|
|
|
+ return CEC_PHYS_ADDR_INVALID;
|
|
|
+
|
|
|
+ if (phys_addr == 0)
|
|
|
+ return input << 12;
|
|
|
+
|
|
|
+ if ((phys_addr & 0x0fff) == 0)
|
|
|
+ return phys_addr | (input << 8);
|
|
|
+
|
|
|
+ if ((phys_addr & 0x00ff) == 0)
|
|
|
+ return phys_addr | (input << 4);
|
|
|
+
|
|
|
+ if ((phys_addr & 0x000f) == 0)
|
|
|
+ return phys_addr | input;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * All nibbles are used so no valid physical addresses can be assigned
|
|
|
+ * to the input.
|
|
|
+ */
|
|
|
+ return CEC_PHYS_ADDR_INVALID;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(v4l2_phys_addr_for_input);
|
|
|
+
|
|
|
+/**
|
|
|
+ * v4l2_phys_addr_validate() - validate a physical address from an EDID
|
|
|
+ *
|
|
|
+ * @phys_addr: the physical address to validate
|
|
|
+ * @parent: if not %NULL, then this is filled with the parents PA.
|
|
|
+ * @port: if not %NULL, then this is filled with the input port.
|
|
|
+ *
|
|
|
+ * This validates a physical address as read from an EDID. If the
|
|
|
+ * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end),
|
|
|
+ * then it will return -EINVAL.
|
|
|
+ *
|
|
|
+ * The parent PA is passed into %parent and the input port is passed into
|
|
|
+ * %port. For example:
|
|
|
+ *
|
|
|
+ * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0.
|
|
|
+ *
|
|
|
+ * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1.
|
|
|
+ *
|
|
|
+ * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2.
|
|
|
+ *
|
|
|
+ * PA = f.f.f.f: has parent f.f.f.f and input port 0.
|
|
|
+ *
|
|
|
+ * Return: 0 if the PA is valid, -EINVAL if not.
|
|
|
+ */
|
|
|
+int v4l2_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (parent)
|
|
|
+ *parent = phys_addr;
|
|
|
+ if (port)
|
|
|
+ *port = 0;
|
|
|
+ if (phys_addr == CEC_PHYS_ADDR_INVALID)
|
|
|
+ return 0;
|
|
|
+ for (i = 0; i < 16; i += 4)
|
|
|
+ if (phys_addr & (0xf << i))
|
|
|
+ break;
|
|
|
+ if (i == 16)
|
|
|
+ return 0;
|
|
|
+ if (parent)
|
|
|
+ *parent = phys_addr & (0xfff0 << i);
|
|
|
+ if (port)
|
|
|
+ *port = (phys_addr >> i) & 0xf;
|
|
|
+ for (i += 4; i < 16; i += 4)
|
|
|
+ if ((phys_addr & (0xf << i)) == 0)
|
|
|
+ return -EINVAL;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(v4l2_phys_addr_validate);
|