diff --git a/Documentation/devicetree/bindings/arm/msm/adv7481.txt b/Documentation/devicetree/bindings/arm/msm/adv7481.txt index 974c0877ac30..d09a83cc0d35 100644 --- a/Documentation/devicetree/bindings/arm/msm/adv7481.txt +++ b/Documentation/devicetree/bindings/arm/msm/adv7481.txt @@ -15,6 +15,12 @@ Required properties interrupt 1, interrupt 2 and interrupt 3. - cam_vdig-supply: Should contain regulator to be used for the digital vdd. +- tx-lanes: Should contain array of csi transmission lanes required + to select csi lane by adv7481 driver. +- settle-count: Should contain array of csi settle count required + to select settle count by adv7481 driver. +- res-array: Should contain array of resolution supported by + adv7481 driver. - cam_vio-supply: Should contain regulator to be used for the IO vdd. - cam_vana-supply: Should contain regulator from which analog voltage is supplied. @@ -35,6 +41,9 @@ Example: compatible = "qcom,adv7481"; reg = <0x70 0xff>; cam_vdig-supply = <&vph_pwr_vreg>; + tx-lanes = <4 2 1>; + settle-count = <16 16 16>; + res-array = "RES_1080P", "RES_720P", "RES_576P_480P"; /* Cameras powered by PMIC: */ cam_vio-supply = <&pm8994_lvs1>; cam_vana-supply = <&pm8994_l17>; diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi index eb7654c26452..83f0bbe86410 100644 --- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi @@ -1137,9 +1137,9 @@ compatible = "qcom,adv7481"; reg = <0x70 0xff>; cam_vdig-supply = <&pm8994_s3>; - tx_lanes = <4 2 1>; - settle_count = <16 16 16>; - res_array = "RES_1080P", "RES_720P", "RES_576P_480P"; + tx-lanes = <4 2 1>; + settle-count = <16 16 16>; + res-array = "RES_1080P", "RES_720P", "RES_576P_480P"; /* Cameras powered by PMIC: */ cam_vio-supply = <&pm8994_lvs1>; cam_vana-supply = <&pm8994_l17>; diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 19c237e8a286..a14f13c44a36 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -75,6 +75,19 @@ enum adv7481_gpio_t { ADV7481_GPIO_MAX, }; +enum adv7481_resolution { + RES_1080P = 0, + RES_720P, + RES_576P_480P, + RES_MAX, +}; + +struct resolution_config { + uint32_t lane_cnt; + uint32_t settle_cnt; + char resolution[20]; +}; + struct adv7481_state { struct device *dev; @@ -125,6 +138,9 @@ struct adv7481_state { int csib_src; int mode; + /* resolution configuration */ + struct resolution_config res_configs[RES_MAX]; + /* CSI configuration data */ int tx_auto_params; enum adv7481_mipi_lane tx_lanes; @@ -241,6 +257,13 @@ const uint8_t adv7481_default_edid_data[] = { static u32 adv7481_inp_to_ba(u32 adv_input); static bool adv7481_is_timing_locked(struct adv7481_state *state); +static int adv7481_get_hdmi_timings(struct adv7481_state *state, + struct adv7481_vid_params *vid_params, + struct adv7481_hdmi_params *hdmi_params); +static int get_lane_cnt(struct resolution_config *configs, + enum adv7481_resolution size, int w, int h); +static int get_settle_cnt(struct resolution_config *configs, + enum adv7481_resolution size, int w, int h); static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) { @@ -1005,11 +1028,18 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct adv7481_state *state = to_state(sd); int *ret_val = arg; + struct msm_ba_v4l2_ioctl_t adv_arg = *(struct msm_ba_v4l2_ioctl_t *)arg; long ret = 0; int param = 0; + struct csi_ctrl_params user_csi; + struct adv7481_vid_params vid_params; + struct adv7481_hdmi_params hdmi_params; pr_debug("Enter %s with command: 0x%x", __func__, cmd); + memset(&vid_params, 0, sizeof(struct adv7481_vid_params)); + memset(&hdmi_params, 0, sizeof(struct adv7481_hdmi_params)); + if (!sd) return -EINVAL; @@ -1039,6 +1069,28 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case VIDIOC_HDMI_RX_CEC_S_ENABLE: ret = adv7481_cec_powerup(state, arg); break; + case VIDIOC_G_CSI_PARAMS: { + if (state->csia_src == ADV7481_IP_HDMI) { + ret = adv7481_get_hdmi_timings(state, + &vid_params, &hdmi_params); + if (ret) { + pr_err("%s:Error in adv7481_get_hdmi_timings\n", + __func__); + return -EINVAL; + } + } + user_csi.settle_count = get_settle_cnt(state->res_configs, + RES_MAX, vid_params.act_pix, vid_params.act_lines); + user_csi.lane_count = get_lane_cnt(state->res_configs, + RES_MAX, vid_params.act_pix, vid_params.act_lines); + + if (copy_to_user((void __user *)adv_arg.ptr, + (void *)&user_csi, sizeof(struct csi_ctrl_params))) { + pr_err("%s: Failed to copy CSI params\n", __func__); + return -EINVAL; + } + break; + } default: pr_err("Not a typewriter! Command: 0x%x", cmd); ret = -ENOTTY; @@ -1541,6 +1593,65 @@ static bool adv7481_is_timing_locked(struct adv7481_state *state) return ret; } +static int get_settle_cnt(struct resolution_config *configs, + enum adv7481_resolution size, int w, int h) +{ + int i; + int ret = -EINVAL; + char res_type[20] = "RES_MAX"; + + if (w == 1920 && h == 1080) { + strlcpy(res_type, "RES_1080P", sizeof(res_type)); + } else if (w == 1280 && h == 720) { + strlcpy(res_type, "RES_720P", sizeof(res_type)); + } else if ((w == 720 && h == 576) || (w == 720 && h == 480)) { + strlcpy(res_type, "RES_576P_480P", sizeof(res_type)); + } else { + pr_err("%s: Resolution not supported\n", __func__); + return ret; + } + + for (i = 0; i < size; i++) { + if (strcmp(configs[i].resolution, res_type) == 0) { + pr_debug("%s: settle count is set to %d\n", + __func__, configs[i].settle_cnt); + ret = configs[i].settle_cnt; + break; + } + } + return ret; +} + + +static int get_lane_cnt(struct resolution_config *configs, + enum adv7481_resolution size, int w, int h) +{ + int i; + int ret = -EINVAL; + char res_type[20] = "RES_MAX"; + + if (w == 1920 && h == 1080) { + strlcpy(res_type, "RES_1080P", sizeof(res_type)); + } else if (w == 1280 && h == 720) { + strlcpy(res_type, "RES_720P", sizeof(res_type)); + } else if ((w == 720 && h == 576) || (w == 720 && h == 480)) { + strlcpy(res_type, "RES_576P_480P", sizeof(res_type)); + } else { + pr_err("%s: Resolution not supported\n", __func__); + return ret; + } + + for (i = 0; i < size; i++) { + if (strcmp(configs[i].resolution, res_type) == 0) { + pr_debug("%s: lane count is set to %d\n", + __func__, configs[i].lane_cnt); + ret = configs[i].lane_cnt; + break; + } + } + return ret; +} + static int adv7481_get_hdmi_timings(struct adv7481_state *state, struct adv7481_vid_params *vid_params, struct adv7481_hdmi_params *hdmi_params) @@ -2032,12 +2143,30 @@ static int adv7481_csi_powerup(struct adv7481_state *state, static int adv7481_set_op_stream(struct adv7481_state *state, bool on) { int ret = 0; + struct adv7481_vid_params vid_params; + struct adv7481_hdmi_params hdmi_params; pr_debug("Enter %s: on: %d, a src: %d, b src: %d\n", __func__, on, state->csia_src, state->csib_src); + memset(&vid_params, 0, sizeof(struct adv7481_vid_params)); + memset(&hdmi_params, 0, sizeof(struct adv7481_hdmi_params)); + if (on && state->csia_src != ADV7481_IP_NONE) - if (ADV7481_IP_HDMI == state->csia_src) { - state->tx_lanes = ADV7481_MIPI_4LANE; + if (state->csia_src == ADV7481_IP_HDMI) { + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (ret) { + pr_err("%s: Error %d in adv7481_get_hdmi_timings\n", + __func__, ret); + return -EINVAL; + } + state->tx_lanes = get_lane_cnt(state->res_configs, + RES_MAX, vid_params.act_pix, vid_params.act_lines); + + if (state->tx_lanes < 0) { + pr_err("%s: Invalid lane count\n", __func__); + return -EINVAL; + } ret = adv7481_set_audio_spdif(state, on); ret |= adv7481_csi_powerup(state, ADV7481_OP_CSIA); } else { @@ -2245,6 +2374,9 @@ static int adv7481_parse_dt(struct platform_device *pdev, { struct device_node *np = state->dev->of_node; uint32_t i = 0; + uint32_t lane_count[RES_MAX]; + uint32_t settle_count[RES_MAX]; + static const char *resolution_array[RES_MAX]; int gpio_count = 0; struct resource *adv_addr_res = NULL; int ret = 0; @@ -2258,6 +2390,36 @@ static int adv7481_parse_dt(struct platform_device *pdev, goto exit; } pr_debug("%s: cci_master: 0x%x\n", __func__, state->cci_master); + /* read CSI data line */ + ret = of_property_read_u32_array(np, "tx-lanes", + lane_count, RES_MAX); + if (ret < 0) { + pr_err("%s: failed to read data lane array . ret %d\n", + __func__, ret); + goto exit; + } + /* read settle count */ + ret = of_property_read_u32_array(np, "settle-count", + settle_count, RES_MAX); + if (ret < 0) { + pr_err("%s: failed to read settle count . ret %d\n", + __func__, ret); + goto exit; + } + /* read resolution array */ + ret = of_property_read_string_array(np, "res-array", + resolution_array, RES_MAX); + if (ret < 0) { + pr_err("%s: failed to read resolution array . ret %d\n", + __func__, ret); + goto exit; + } + for (i = 0; i < RES_MAX; i++) { + state->res_configs[i].lane_cnt = (uint32_t)lane_count[i]; + state->res_configs[i].settle_cnt = (uint32_t)settle_count[i]; + strlcpy(state->res_configs[i].resolution, resolution_array[i], + sizeof(state->res_configs[i].resolution)); + } adv_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!adv_addr_res) { pr_err("%s: failed to read adv7481 resource.\n", __func__); diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c index 4200b8f20073..566cb634ae8f 100644 --- a/drivers/video/msm/ba/msm_ba.c +++ b/drivers/video/msm/ba/msm_ba.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "msm_ba_internal.h" #include "msm_ba_debug.h" @@ -555,6 +556,24 @@ long msm_ba_private_ioctl(void *instance, int cmd, void *arg) } } break; + case VIDIOC_G_CSI_PARAMS: { + dprintk(BA_DBG, "VIDIOC_G_CSI_PARAMS"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (arg) { + rc = v4l2_subdev_call(sd, core, ioctl, cmd, arg); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } else { + dprintk(BA_ERR, "%s: NULL argument provided", __func__); + rc = -EINVAL; + } + } + break; default: dprintk(BA_WARN, "Not a typewriter! Command: 0x%x", cmd); rc = -ENOTTY; diff --git a/drivers/video/msm/ba/msm_v4l2_ba.c b/drivers/video/msm/ba/msm_v4l2_ba.c index 89fc08dd3c33..c50d02292398 100644 --- a/drivers/video/msm/ba/msm_v4l2_ba.c +++ b/drivers/video/msm/ba/msm_v4l2_ba.c @@ -227,6 +227,14 @@ static int msm_ba_v4l2_g_parm(struct file *file, void *fh, return 0; } +static long msm_ba_v4l2_private_ioctl(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_private_ioctl((void *)ba_inst, cmd, (void *)arg); +} + static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = { .vidioc_querycap = msm_ba_v4l2_querycap, .vidioc_enum_fmt_vid_cap = msm_ba_v4l2_enum_fmt, @@ -250,6 +258,7 @@ static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = { .vidioc_enum_output = msm_ba_v4l2_enum_output, .vidioc_g_output = msm_ba_v4l2_g_output, .vidioc_s_output = msm_ba_v4l2_s_output, + .vidioc_default = msm_ba_v4l2_private_ioctl, }; static unsigned int msm_ba_v4l2_poll(struct file *filp, diff --git a/include/media/adv7481.h b/include/media/adv7481.h index 80b8ee879ea4..fa5466197889 100644 --- a/include/media/adv7481.h +++ b/include/media/adv7481.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. 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 and @@ -14,6 +14,7 @@ #ifndef __ADV7481_H__ #define __ADV7481_H__ +#include /** * adv7481_platform_data * structure to pass board specific information to the ADV7481 driver diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild index 421c65d8a901..640002326ace 100644 --- a/include/uapi/media/Kbuild +++ b/include/uapi/media/Kbuild @@ -20,3 +20,4 @@ header-y += msmb_ispif.h header-y += msmb_pproc.h header-y += radio-iris.h header-y += radio-iris-commands.h +header-y += msm_ba.h diff --git a/include/uapi/media/msm_ba.h b/include/uapi/media/msm_ba.h new file mode 100644 index 000000000000..587d14652f3f --- /dev/null +++ b/include/uapi/media/msm_ba.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2017, The Linux Foundation. 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 and + * only 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. + * + */ + +#ifndef __UAPI_MSM_BA_H__ +#define __UAPI_MSM_BA_H__ + +#include +#include + +/* CSI control params */ +struct csi_ctrl_params { + uint32_t settle_count; + uint32_t lane_count; +}; + +/* private ioctl structure */ +struct msm_ba_v4l2_ioctl_t { + size_t len; + void __user *ptr; +}; + +/* ADV7481 private ioctls for CSI control params */ +#define VIDIOC_G_CSI_PARAMS \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_ba_v4l2_ioctl_t) +#endif