diff options
| -rw-r--r-- | Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt | 2 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/i2c/sii8334-i2c.txt | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_edid.c | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_mhl.h | 27 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 82 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.h | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_util.c | 12 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_util.h | 3 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mhl_msc.c | 22 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mhl_sii8334.c | 87 |
10 files changed, 217 insertions, 24 deletions
diff --git a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt index 2a07f7f75337..ceb95dcac50a 100644 --- a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt +++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt @@ -30,7 +30,7 @@ Required properties: - compatible : "msm-hdmi-audio-codec-rx"; Example: - qcom,hdmi_tx@fd922100 { + mdss_hdmi_tx: qcom,hdmi_tx@fd922100 { cell-index = <0>; compatible = "qcom,hdmi-tx"; reg = <0xfd922100 0x35C>, diff --git a/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt b/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt index ed451924e6a6..27a2149d9c86 100644 --- a/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt +++ b/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt @@ -7,6 +7,7 @@ Required properties: - mhl-pwr-gpio: MHL power gpio required for power rails - mhl-rst-gpio: MHL reset gpio going into sii8334 for toggling reset pin - <supply-name>-supply: phandle to the regulator device tree node. +- qcom,hdmi-tx-map: phandle to the hdmi tx device tree node. Example: i2c@f9967000 { @@ -22,5 +23,6 @@ Example: avcc_12-supply = <&pm8941_l2>; smps3a-supply = <&pm8941_s3>; vdda-supply = <&pm8941_l12>; + qcom,hdmi-tx-map = <&mdss_hdmi_tx>; }; }; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 1aae22e893ca..08be33773d4c 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -240,6 +240,8 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev, if (edid_ctrl->sink_data.num_of_elements) { u32 *video_mode = edid_ctrl->sink_data.disp_mode_list; for (i = 0; i < edid_ctrl->sink_data.num_of_elements; ++i) { + if (!hdmi_get_supported_mode(*video_mode)) + continue; if (ret > 0) ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%d", *video_mode++ + 1); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_mhl.h b/drivers/video/fbdev/msm/mdss_hdmi_mhl.h new file mode 100644 index 000000000000..8fef63e89deb --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_hdmi_mhl.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2013, 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 __MDSS_HDMI_MHL_H__ +#define __MDSS_HDMI_MHL_H__ + +#include <linux/platform_device.h> + +struct msm_hdmi_mhl_ops { + u8 (*tmds_enabled)(struct platform_device *pdev); + int (*set_mhl_max_pclk)(struct platform_device *pdev, u32 max_val); +}; + +int msm_hdmi_register_mhl(struct platform_device *pdev, + struct msm_hdmi_mhl_ops *ops); + +#endif /* __MDSS_HDMI_MHL_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index a3d7f98c9c89..1d49c2481ce2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -30,6 +30,7 @@ #include "mdss_hdmi_hdcp.h" #include "mdss.h" #include "mdss_panel.h" +#include "mdss_hdmi_mhl.h" #define DRV_NAME "hdmi-tx" #define COMPATIBLE_NAME "qcom,hdmi-tx" @@ -629,6 +630,30 @@ static void hdmi_tx_setup_video_mode_lut(void) hdmi_set_supported_mode(HDMI_VFRMT_4096x2160p24_16_9); } /* hdmi_tx_setup_video_mode_lut */ +/* Table tuned to indicate video formats supported by the MHL Tx */ +/* Valid pclk rates (Mhz): 25.2, 27, 27.03, 74.25 */ +static void hdmi_tx_setup_mhl_video_mode_lut(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + u32 i; + struct hdmi_disp_mode_timing_type *temp_timing; + + if (!hdmi_ctrl->mhl_max_pclk) { + DEV_WARN("%s: mhl max pclk not set!\n", __func__); + return; + } + DEV_DBG("%s: max mode set to [%u]\n", + __func__, hdmi_ctrl->mhl_max_pclk); + for (i = 0; i < HDMI_VFRMT_MAX; i++) { + temp_timing = + (struct hdmi_disp_mode_timing_type *)hdmi_get_supported_mode(i); + if (!temp_timing) + continue; + /* formats that exceed max mhl line clk bw */ + if (temp_timing->pixel_freq > hdmi_ctrl->mhl_max_pclk) + hdmi_del_supported_mode(i); + } +} /* hdmi_tx_setup_mhl_video_mode_lut */ + static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) { int status; @@ -1758,12 +1783,65 @@ static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev, hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID], blk); } /* hdmi_tx_get_audio_edid_blk */ +static u8 hdmi_tx_tmds_enabled(struct platform_device *pdev) +{ + struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return -ENODEV; + } + + /* status of tmds */ + return (hdmi_ctrl->timing_gen_on == true); +} + +static int hdmi_tx_set_mhl_max_pclk(struct platform_device *pdev, u32 max_val) +{ + struct hdmi_tx_ctrl *hdmi_ctrl = NULL; + + hdmi_ctrl = platform_get_drvdata(pdev); + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return -ENODEV; + } + if (max_val) { + hdmi_ctrl->mhl_max_pclk = max_val; + hdmi_tx_setup_mhl_video_mode_lut(hdmi_ctrl); + } else { + DEV_ERR("%s: invalid max pclk val\n", __func__); + return -EINVAL; + } + return 0; +} + +int msm_hdmi_register_mhl(struct platform_device *pdev, + struct msm_hdmi_mhl_ops *ops) +{ + struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid pdev\n", __func__); + return -ENODEV; + } + + if (!ops) { + DEV_ERR("%s: invalid ops\n", __func__); + return -EINVAL; + } + + ops->tmds_enabled = hdmi_tx_tmds_enabled; + ops->set_mhl_max_pclk = hdmi_tx_set_mhl_max_pclk; + return 0; +} + int msm_hdmi_register_audio_codec(struct platform_device *pdev, struct msm_hdmi_audio_codec_ops *ops) { struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); - if (!hdmi_ctrl) { + if (!hdmi_ctrl || !ops) { DEV_ERR("%s: invalid input\n", __func__); return -ENODEV; } @@ -2436,6 +2514,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, DEV_ERR("%s: hdcp auth failed. rc=%d\n", __func__, rc); } + hdmi_ctrl->timing_gen_on = true; break; case MDSS_EVENT_SUSPEND: @@ -2463,6 +2542,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_TIMEGEN_OFF: + hdmi_ctrl->timing_gen_on = false; break; case MDSS_EVENT_CLOSE: diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index ba5ee5bc58bf..06ae42766593 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -58,6 +58,8 @@ struct hdmi_tx_ctrl { u32 hpd_off_pending; u32 hpd_feature_on; u32 hpd_initialized; + u8 timing_gen_on; + u32 mhl_max_pclk; struct completion hpd_done; struct work_struct hpd_int_work; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 13e2c9b744bf..07c2336b5206 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2013, 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 @@ -34,6 +34,16 @@ void hdmi_init_supported_video_timings(void) } } /* hdmi_init_supported_video_timings */ +void hdmi_del_supported_mode(u32 mode) +{ + struct hdmi_disp_mode_timing_type *ret = NULL; + DEV_DBG("%s: removing %s\n", __func__, + hdmi_get_video_fmt_2string(mode)); + ret = &hdmi_supported_video_mode_lut[mode]; + if (ret != NULL && ret->supported) + ret->supported = false; +} + const struct hdmi_disp_mode_timing_type *hdmi_get_supported_mode(u32 mode) { const struct hdmi_disp_mode_timing_type *ret = NULL; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h index d6216168d5c4..914aac1a96c6 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2013, 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 @@ -430,6 +430,7 @@ void hdmi_init_supported_video_timings(void); int hdmi_get_video_id_code(struct hdmi_disp_mode_timing_type *timing_in); const struct hdmi_disp_mode_timing_type *hdmi_get_supported_mode(u32 mode); void hdmi_set_supported_mode(u32 mode); +void hdmi_del_supported_mode(u32 mode); const char *hdmi_get_video_fmt_2string(u32 format); ssize_t hdmi_get_video_3d_fmt_2string(u32 format, char *buf); diff --git a/drivers/video/fbdev/msm/mhl_msc.c b/drivers/video/fbdev/msm/mhl_msc.c index 3d3fff973752..add65acd1eb3 100644 --- a/drivers/video/fbdev/msm/mhl_msc.c +++ b/drivers/video/fbdev/msm/mhl_msc.c @@ -16,6 +16,7 @@ #include <linux/vmalloc.h> #include <linux/input.h> #include "mhl_msc.h" +#include "mdss_hdmi_mhl.h" static struct mhl_tx_ctrl *mhl_ctrl; static DEFINE_MUTEX(msc_send_workqueue_mutex); @@ -39,6 +40,18 @@ const char *devcap_reg_name[] = { "Reserved ", }; +static bool mhl_check_tmds_enabled(struct mhl_tx_ctrl *mhl_ctrl) +{ + if (mhl_ctrl && mhl_ctrl->hdmi_mhl_ops) { + struct msm_hdmi_mhl_ops *ops = mhl_ctrl->hdmi_mhl_ops; + struct platform_device *pdev = mhl_ctrl->pdata->hdmi_pdev; + return (ops->tmds_enabled(pdev) == true); + } else { + pr_err("%s: invalid input\n", __func__); + return false; + } +} + static void mhl_print_devcap(u8 offset, u8 devcap) { switch (offset) { @@ -398,10 +411,12 @@ static int mhl_rap_action(struct mhl_tx_ctrl *mhl_ctrl, u8 action_code) static int mhl_rap_recv(struct mhl_tx_ctrl *mhl_ctrl, u8 action_code) { u8 error_code; + bool tmds_en; switch (action_code) { case MHL_RAP_POLL: - if (mhl_ctrl->tmds_enabled()) + tmds_en = mhl_check_tmds_enabled(mhl_ctrl); + if (tmds_en) error_code = MHL_RAPK_NO_ERROR; else error_code = MHL_RAPK_UNSUPPORTED_ACTION_CODE; @@ -513,6 +528,8 @@ int mhl_msc_recv_set_int(struct mhl_tx_ctrl *mhl_ctrl, int mhl_msc_recv_write_stat(struct mhl_tx_ctrl *mhl_ctrl, u8 offset, u8 value) { + bool tmds_en; + if (offset >= 2) return -EFAULT; @@ -543,10 +560,11 @@ int mhl_msc_recv_write_stat(struct mhl_tx_ctrl *mhl_ctrl, * changed and PATH ENABLED * bit set */ + tmds_en = mhl_check_tmds_enabled(mhl_ctrl); if ((value ^ mhl_ctrl->path_en_state) & MHL_STATUS_PATH_ENABLED) { if (value & MHL_STATUS_PATH_ENABLED) { - if (mhl_ctrl->tmds_enabled() && + if (tmds_en && (mhl_ctrl->devcap[offset] & MHL_FEATURE_RAP_SUPPORT)) { mhl_msc_send_msc_msg( diff --git a/drivers/video/fbdev/msm/mhl_sii8334.c b/drivers/video/fbdev/msm/mhl_sii8334.c index 4d6af156fe7c..30dd47153863 100644 --- a/drivers/video/fbdev/msm/mhl_sii8334.c +++ b/drivers/video/fbdev/msm/mhl_sii8334.c @@ -30,6 +30,7 @@ #include "mdss_panel.h" #include "mdss_io_util.h" #include "mhl_msc.h" +#include "mdss_hdmi_mhl.h" #define MHL_DRIVER_NAME "sii8334" #define COMPATIBLE_NAME "qcom,mhl-sii8334" @@ -193,8 +194,6 @@ static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, static void mhl_init_reg_settings(struct mhl_tx_ctrl *mhl_ctrl, bool mhl_disc_en); -static uint8_t store_tmds_state; - int mhl_i2c_reg_read(struct i2c_client *client, uint8_t slave_addr_index, uint8_t reg_offset) { @@ -239,6 +238,8 @@ static int mhl_tx_get_dt_data(struct device *dev, int i, rc = 0; struct device_node *of_node = NULL; struct dss_gpio *temp_gpio = NULL; + struct platform_device *hdmi_pdev = NULL; + struct device_node *hdmi_tx_node = NULL; int dt_gpio; i = 0; @@ -316,6 +317,23 @@ static int mhl_tx_get_dt_data(struct device *dev, temp_gpio->gpio); pdata->gpios[MHL_TX_INTR_GPIO] = temp_gpio; + /* parse phandle for hdmi tx */ + hdmi_tx_node = of_parse_phandle(of_node, "qcom,hdmi-tx-map", 0); + if (!hdmi_tx_node) { + pr_err("%s: can't find hdmi phandle\n", __func__); + goto error; + } + + hdmi_pdev = of_find_device_by_node(hdmi_tx_node); + if (!hdmi_pdev) { + pr_err("%s: can't find the device by node\n", __func__); + goto error; + } + pr_debug("%s: hdmi_pdev [0X%x] to pdata->pdev\n", + __func__, (unsigned int)hdmi_pdev); + + pdata->hdmi_pdev = hdmi_pdev; + return 0; error: pr_err("%s: ret due to err\n", __func__); @@ -719,10 +737,6 @@ static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode) } } -uint8_t check_tmds_enabled(void) -{ - return store_tmds_state; -} void mhl_tmds_ctrl(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on) { @@ -730,16 +744,8 @@ void mhl_tmds_ctrl(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on) if (on) { MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4); mhl_drive_hpd(mhl_ctrl, HPD_UP); - /* - * store the state to be used - * before responding to RAP msgs - * this needs to be obtained from - * hdmi driver - */ - store_tmds_state = 1; } else { MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00); - store_tmds_state = 0; } } @@ -1007,7 +1013,10 @@ static void mhl_hpd_stat_isr(struct mhl_tx_ctrl *mhl_ctrl) */ cbus_stat = MHL_SII_CBUS_RD(0x0D); if (BIT6 & cbus_stat) - mhl_tmds_ctrl(mhl_ctrl, TMDS_ENABLE); + mhl_drive_hpd(mhl_ctrl, HPD_UP); + else + mhl_drive_hpd(mhl_ctrl, HPD_DOWN); + } } @@ -1636,6 +1645,7 @@ static int mhl_i2c_probe(struct i2c_client *client, struct mhl_tx_platform_data *pdata = NULL; struct mhl_tx_ctrl *mhl_ctrl; struct usb_ext_notification *mhl_info = NULL; + struct msm_hdmi_mhl_ops *hdmi_mhl_ops = NULL; mhl_ctrl = devm_kzalloc(&client->dev, sizeof(*mhl_ctrl), GFP_KERNEL); if (!mhl_ctrl) { @@ -1784,25 +1794,63 @@ static int mhl_i2c_probe(struct i2c_client *client, goto failed_probe; } + hdmi_mhl_ops = devm_kzalloc(&client->dev, + sizeof(struct msm_hdmi_mhl_ops), + GFP_KERNEL); + if (!hdmi_mhl_ops) { + pr_err("%s: alloc hdmi mhl ops failed\n", __func__); + rc = -ENOMEM; + goto failed_probe_pwr; + } + pr_debug("%s: i2c client addr is [%x]\n", __func__, client->addr); + if (mhl_ctrl->pdata->hdmi_pdev) { + rc = msm_hdmi_register_mhl(mhl_ctrl->pdata->hdmi_pdev, + hdmi_mhl_ops); + if (rc) { + pr_err("%s: register with hdmi failed\n", __func__); + rc = -EPROBE_DEFER; + goto failed_probe_pwr; + } + } + + if (!hdmi_mhl_ops || !hdmi_mhl_ops->tmds_enabled || + !hdmi_mhl_ops->set_mhl_max_pclk) { + pr_err("%s: func ptr is NULL\n", __func__); + rc = -EINVAL; + goto failed_probe_pwr; + } + mhl_ctrl->hdmi_mhl_ops = hdmi_mhl_ops; + + rc = hdmi_mhl_ops->set_mhl_max_pclk( + mhl_ctrl->pdata->hdmi_pdev, MAX_MHL_PCLK); + if (rc) { + pr_err("%s: can't set max mhl pclk\n", __func__); + goto failed_probe_pwr; + } mhl_info = devm_kzalloc(&client->dev, sizeof(*mhl_info), GFP_KERNEL); if (!mhl_info) { pr_err("%s: alloc mhl info failed\n", __func__); - goto failed_probe; + rc = -ENOMEM; + goto failed_probe_pwr; } mhl_info->ctxt = mhl_ctrl; mhl_info->notify = mhl_sii_device_discovery; if (msm_register_usb_ext_notification(mhl_info)) { pr_err("%s: register for usb notifcn failed\n", __func__); - goto failed_probe; + rc = -EPROBE_DEFER; + goto failed_probe_pwr; } mhl_ctrl->mhl_info = mhl_info; mhl_register_msc(mhl_ctrl); - mhl_ctrl->tmds_enabled = check_tmds_enabled; return 0; + +failed_probe_pwr: + power_supply_unregister(&mhl_ctrl->mhl_psy); failed_probe: + free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); mhl_gpio_config(mhl_ctrl, 0); mhl_vreg_config(mhl_ctrl, 0); /* do not deep-free */ @@ -1814,6 +1862,9 @@ failed_dt_data: failed_no_mem: if (mhl_ctrl) devm_kfree(&client->dev, mhl_ctrl); + mhl_info = NULL; + pdata = NULL; + mhl_ctrl = NULL; pr_err("%s: PROBE FAILED, rc=%d\n", __func__, rc); return rc; } |
