From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpu/drm/rockchip/rk3066_hdmi.c | 574 ++++++++++++++------------------------------------------ 1 files changed, 148 insertions(+), 426 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/kernel/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 8a23f60..4668393 100644 --- a/kernel/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/kernel/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -1,54 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd * Zheng Yang <zhengyang@rock-chips.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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/irq.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + #include <linux/clk.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/hdmi.h> #include <linux/mfd/syscon.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/regmap.h> -#include <drm/drm_of.h> -#include <drm/drmP.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_edid.h> - -#include <sound/hdmi-codec.h> +#include "rk3066_hdmi.h" #include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" -#include "rk3066_hdmi.h" - -#define DEFAULT_PLLA_RATE 30000000 - -struct audio_info { - int sample_rate; - int channels; - int sample_width; -}; +#define DEFAULT_PLLA_RATE 30000000 struct hdmi_data_info { - int vic; + int vic; /* The CEA Video ID (VIC) of the current drm display mode. */ bool sink_is_hdmi; - bool sink_has_audio; - unsigned int enc_in_format; unsigned int enc_out_format; unsigned int colorimetry; }; @@ -60,39 +34,31 @@ u8 segment_addr; u8 stat; - struct mutex lock; /*for i2c operation*/ - struct completion cmp; + struct mutex i2c_lock; /* For i2c operation. */ + struct completion cmpltn; }; struct rk3066_hdmi { struct device *dev; struct drm_device *drm_dev; - struct regmap *regmap; + struct regmap *grf_regmap; int irq; struct clk *hclk; void __iomem *regs; - struct drm_connector connector; - struct drm_encoder encoder; + struct drm_connector connector; + struct drm_encoder encoder; struct rk3066_hdmi_i2c *i2c; struct i2c_adapter *ddc; unsigned int tmdsclk; - unsigned int powermode; - struct platform_device *audio_pdev; - bool audio_enable; - - struct hdmi_data_info hdmi_data; - struct audio_info audio; + struct hdmi_data_info hdmi_data; struct drm_display_mode previous_mode; }; -#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x) - -static int -rk3066_hdmi_config_audio(struct rk3066_hdmi *hdmi, struct audio_info *audio); +#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x) static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset) { @@ -122,7 +88,7 @@ hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); - /* Clear the EDID interrupt flag and mute the interrupt */ + /* Clear the EDID interrupt flag and mute the interrupt. */ hdmi_modb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_EDID_MASK, 0); hdmi_writeb(hdmi, HDMI_INTR_STATUS1, HDMI_INTR_EDID_MASK); } @@ -134,18 +100,29 @@ static void rk3066_hdmi_set_power_mode(struct rk3066_hdmi *hdmi, int mode) { - u8 previous_mode, next_mode; + u8 current_mode, next_mode; + u8 i = 0; - previous_mode = rk3066_hdmi_get_power_mode(hdmi); + current_mode = rk3066_hdmi_get_power_mode(hdmi); - if (previous_mode == mode) + DRM_DEV_DEBUG(hdmi->dev, "mode :%d\n", mode); + DRM_DEV_DEBUG(hdmi->dev, "current_mode :%d\n", current_mode); + + if (current_mode == mode) return; do { - if (previous_mode > mode) - next_mode = previous_mode / 2; - else - next_mode = previous_mode * 2; + if (current_mode > mode) { + next_mode = current_mode / 2; + } else { + if (current_mode < HDMI_SYS_POWER_MODE_A) + next_mode = HDMI_SYS_POWER_MODE_A; + else + next_mode = current_mode * 2; + } + + DRM_DEV_DEBUG(hdmi->dev, "%d: next_mode :%d\n", i, next_mode); + if (next_mode != HDMI_SYS_POWER_MODE_D) { hdmi_modb(hdmi, HDMI_SYS_CTRL, HDMI_SYS_POWER_MODE_MASK, next_mode); @@ -161,13 +138,14 @@ hdmi_writeb(hdmi, HDMI_SYS_CTRL, HDMI_SYS_POWER_MODE_D); } - previous_mode = next_mode; - } while (next_mode != mode); + current_mode = next_mode; + i = i + 1; + } while ((next_mode != mode) && (i < 5)); /* - * When IP controller haven't configured to an accurate video - * timing, DDC_CLK is equal to PLLA freq which is 30MHz,so we - * need to init the TMDS rate to PCLK rate, and reconfigure + * When the IP controller isn't configured with accurate video timing, + * DDC_CLK should be equal to the PLLA frequency, which is 30MHz, + * so we need to init the TMDS rate to the PCLK rate and reconfigure * the DDC clock. */ if (mode < HDMI_SYS_POWER_MODE_D) @@ -210,7 +188,8 @@ union hdmi_infoframe frame; int rc; - rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false); + rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, + &hdmi->connector, mode); if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) frame.avi.colorspace = HDMI_COLORSPACE_YUV444; @@ -226,26 +205,12 @@ HDMI_INFOFRAME_AVI, 0, 0, 0); } -static int rk3066_hdmi_config_aai(struct rk3066_hdmi *hdmi, - struct audio_info *audio) -{ - struct hdmi_audio_infoframe *faudio; - union hdmi_infoframe frame; - int rc; - - rc = hdmi_audio_infoframe_init(&frame.audio); - faudio = (struct hdmi_audio_infoframe *)&frame; - - return rk3066_hdmi_upload_frame(hdmi, rc, &frame, - HDMI_INFOFRAME_AAI, 0, 0, 0); -} - static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi, struct drm_display_mode *mode) { int value, vsync_offset; - /* Set detail external video timing polarity and interlace mode */ + /* Set the details for the external polarity and interlace mode. */ value = HDMI_EXT_VIDEO_SET_EN; value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? HDMI_VIDEO_HSYNC_ACTIVE_HIGH : HDMI_VIDEO_HSYNC_ACTIVE_LOW; @@ -253,14 +218,16 @@ HDMI_VIDEO_VSYNC_ACTIVE_HIGH : HDMI_VIDEO_VSYNC_ACTIVE_LOW; value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? HDMI_VIDEO_MODE_INTERLACE : HDMI_VIDEO_MODE_PROGRESSIVE; + if (hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3) vsync_offset = 6; else vsync_offset = 0; - value |= vsync_offset << 4; + + value |= vsync_offset << HDMI_VIDEO_VSYNC_OFFSET_SHIFT; hdmi_writeb(hdmi, HDMI_EXT_VIDEO_PARA, value); - /* Set detail external video timing */ + /* Set the details for the external video timing. */ value = mode->htotal; hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_L, value & 0xFF); hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_H, (value >> 8) & 0xFF); @@ -306,8 +273,14 @@ static void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi) { - /* tmds frequency same as input dclk */ + /* TMDS uses the same frequency as dclk. */ hdmi_writeb(hdmi, HDMI_DEEP_COLOR_MODE, 0x22); + + /* + * The semi-public documentation does not describe the hdmi registers + * used by the function rk3066_hdmi_phy_write(), so we keep using + * these magic values for now. + */ if (hdmi->tmdsclk > 100000000) { rk3066_hdmi_phy_write(hdmi, 0x158, 0x0E); rk3066_hdmi_phy_write(hdmi, 0x15c, 0x00); @@ -345,8 +318,6 @@ struct drm_display_mode *mode) { hdmi->hdmi_data.vic = drm_match_cea_mode(mode); - - hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB; hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB; if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 || @@ -359,15 +330,15 @@ hdmi->tmdsclk = mode->clock * 1000; - /* Mute video and audio output */ + /* Mute video and audio output. */ hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_VIDEO_AUDIO_DISABLE_MASK, HDMI_AUDIO_DISABLE | HDMI_VIDEO_DISABLE); - /* Set power state to mode b */ + /* Set power state to mode B. */ if (rk3066_hdmi_get_power_mode(hdmi) != HDMI_SYS_POWER_MODE_B) rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_B); - /* Input video mode is RGB24bit, Data enable signal from external */ + /* Input video mode is RGB 24 bit. Use external data enable signal. */ hdmi_modb(hdmi, HDMI_AV_CTRL1, HDMI_VIDEO_DE_MASK, HDMI_VIDEO_EXTERNAL_DE); hdmi_writeb(hdmi, HDMI_VIDEO_CTRL1, @@ -382,7 +353,6 @@ hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, HDMI_VIDEO_MODE_HDMI); rk3066_hdmi_config_avi(hdmi, mode); - rk3066_hdmi_config_audio(hdmi, &hdmi->audio); } else { hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, 0); } @@ -392,26 +362,16 @@ rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_E); /* - * When IP controller have configured to an accurate video - * timing, then the TMDS clock source would be switched to - * DCLK_LCDC, so we need to init the TMDS rate to mode pixel - * clock rate, and reconfigure the DDC clock. + * When the IP controller is configured with accurate video + * timing, the TMDS clock source should be switched to + * DCLK_LCDC, so we need to init the TMDS rate to the pixel mode + * clock rate and reconfigure the DDC clock. */ rk3066_hdmi_i2c_init(hdmi); - /* Unmute video and audio output */ + /* Unmute video output. */ hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_VIDEO_AUDIO_DISABLE_MASK, HDMI_AUDIO_DISABLE); - if (hdmi->audio_enable) { - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_AUDIO_DISABLE, 0); - /* Reset Audio cature logic */ - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, - HDMI_AUDIO_CP_LOGIC_RESET_MASK, - HDMI_AUDIO_CP_LOGIC_RESET); - usleep_range(900, 1000); - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, - HDMI_AUDIO_CP_LOGIC_RESET_MASK, 0); - } return 0; } @@ -422,13 +382,25 @@ { struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); - /* Store the display mode for plugin/DPMS poweron events */ - memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode)); + /* Store the display mode for plugin/DPMS poweron events. */ + drm_mode_copy(&hdmi->previous_mode, adj_mode); } static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) { struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); + int mux, val; + + mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); + if (mux) + val = (HDMI_VIDEO_SEL << 16) | HDMI_VIDEO_SEL; + else + val = HDMI_VIDEO_SEL << 16; + + regmap_write(hdmi->grf_regmap, GRF_SOC_CON0, val); + + DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder enable select: vop%s\n", + (mux) ? "1" : "0"); rk3066_hdmi_setup(hdmi, &hdmi->previous_mode); } @@ -436,6 +408,8 @@ static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder) { struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder); + + DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n"); if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_E) { hdmi_writeb(hdmi, HDMI_VIDEO_CTRL2, @@ -465,22 +439,17 @@ s->output_mode = ROCKCHIP_OUT_MODE_P888; s->output_type = DRM_MODE_CONNECTOR_HDMIA; - s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; return 0; } static const struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { - .enable = rk3066_hdmi_encoder_enable, - .disable = rk3066_hdmi_encoder_disable, - .mode_fixup = rk3066_hdmi_encoder_mode_fixup, - .mode_set = rk3066_hdmi_encoder_mode_set, + .enable = rk3066_hdmi_encoder_enable, + .disable = rk3066_hdmi_encoder_disable, + .mode_fixup = rk3066_hdmi_encoder_mode_fixup, + .mode_set = rk3066_hdmi_encoder_mode_set, .atomic_check = rk3066_hdmi_encoder_atomic_check, -}; - -static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs = { - .destroy = drm_encoder_cleanup, }; static enum drm_connector_status @@ -496,7 +465,6 @@ { struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector); struct edid *edid; - struct drm_display_info *info = &connector->display_info; int ret = 0; if (!hdmi->ddc) @@ -505,19 +473,9 @@ edid = drm_get_edid(connector, hdmi->ddc); if (edid) { hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid); - hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid); - drm_mode_connector_update_edid_property(connector, edid); + drm_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); kfree(edid); - } else { - hdmi->hdmi_data.sink_is_hdmi = true; - hdmi->hdmi_data.sink_has_audio = true; - ret = rockchip_drm_add_modes_noedid(connector); - info->edid_hdmi_dc_modes = 0; - info->hdmi.y420_dc_modes = 0; - info->color_formats = 0; - - dev_info(hdmi->dev, "failed to get edid\n"); } return ret; @@ -547,7 +505,12 @@ rk3066_hdmi_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY) { - return drm_helper_probe_single_connector_modes(connector, 1920, 1080); + if (maxX > 1920) + maxX = 1920; + if (maxY > 1080) + maxY = 1080; + + return drm_helper_probe_single_connector_modes(connector, maxX, maxY); } static void rk3066_hdmi_connector_destroy(struct drm_connector *connector) @@ -557,7 +520,6 @@ } static const struct drm_connector_funcs rk3066_hdmi_connector_funcs = { - .dpms = drm_atomic_helper_connector_dpms, .fill_modes = rk3066_hdmi_probe_single_connector_modes, .detect = rk3066_hdmi_connector_detect, .destroy = rk3066_hdmi_connector_destroy, @@ -574,231 +536,13 @@ }; static int -rk3066_hdmi_config_audio(struct rk3066_hdmi *hdmi, struct audio_info *audio) -{ - u32 rate, channel, word_length, N, CTS; - u64 tmp; - - if (audio->channels < 3) - channel = HDMI_AUDIO_I2S_CHANNEL_1_2; - else if (audio->channels < 5) - channel = HDMI_AUDIO_I2S_CHANNEL_3_4; - else if (audio->channels < 7) - channel = HDMI_AUDIO_I2S_CHANNEL_5_6; - else - channel = HDMI_AUDIO_I2S_CHANNEL_7_8; - - switch (audio->sample_rate) { - case 32000: - rate = HDMI_AUDIO_SAMPLE_FRE_32000; - N = N_32K; - break; - case 44100: - rate = HDMI_AUDIO_SAMPLE_FRE_44100; - N = N_441K; - break; - case 48000: - rate = HDMI_AUDIO_SAMPLE_FRE_48000; - N = N_48K; - break; - case 88200: - rate = HDMI_AUDIO_SAMPLE_FRE_88200; - N = N_882K; - break; - case 96000: - rate = HDMI_AUDIO_SAMPLE_FRE_96000; - N = N_96K; - break; - case 176400: - rate = HDMI_AUDIO_SAMPLE_FRE_176400; - N = N_1764K; - break; - case 192000: - rate = HDMI_AUDIO_SAMPLE_FRE_192000; - N = N_192K; - break; - default: - dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", - __func__, audio->sample_rate); - return -ENOENT; - } - - switch (audio->sample_width) { - case 16: - word_length = 0x02; - break; - case 20: - word_length = 0x0a; - break; - case 24: - word_length = 0x0b; - break; - default: - dev_err(hdmi->dev, "[%s] not support such word length %d\n", - __func__, audio->sample_width); - return -ENOENT; - } - - tmp = (u64)hdmi->tmdsclk * N; - do_div(tmp, 128 * audio->sample_rate); - CTS = tmp; - - /* set_audio source I2S */ - hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x00); - hdmi_writeb(hdmi, HDMI_AUDIO_CTRL2, 0x40); - hdmi_writeb(hdmi, HDMI_I2S_AUDIO_CTRL, - HDMI_AUDIO_I2S_FORMAT_STANDARD | channel); - hdmi_writeb(hdmi, HDMI_I2S_SWAP, 0x00); - hdmi_modb(hdmi, HDMI_AV_CTRL1, HDMI_AUDIO_SAMPLE_FRE_MASK, rate); - hdmi_writeb(hdmi, HDMI_AUDIO_SRC_NUM_AND_LENGTH, word_length); - - /* Set N value */ - hdmi_modb(hdmi, HDMI_LR_SWAP_N3, - HDMI_AUDIO_N_19_16_MASK, (N >> 16) & 0x0F); - hdmi_writeb(hdmi, HDMI_N2, (N >> 8) & 0xFF); - hdmi_writeb(hdmi, HDMI_N1, N & 0xFF); - - /* Set CTS value */ - hdmi_writeb(hdmi, HDMI_CTS_EXT1, CTS & 0xff); - hdmi_writeb(hdmi, HDMI_CTS_EXT2, (CTS >> 8) & 0xff); - hdmi_writeb(hdmi, HDMI_CTS_EXT3, (CTS >> 16) & 0xff); - - if (audio->channels > 2) - hdmi_modb(hdmi, HDMI_LR_SWAP_N3, - HDMI_AUDIO_LR_SWAP_MASK, - HDMI_AUDIO_LR_SWAP_SUBPACKET1); - rate = (~(rate >> 4)) & 0x0f; - hdmi_writeb(hdmi, HDMI_AUDIO_STA_BIT_CTRL1, rate); - hdmi_writeb(hdmi, HDMI_AUDIO_STA_BIT_CTRL2, 0); - - return rk3066_hdmi_config_aai(hdmi, audio); -} - -static int rk3066_hdmi_audio_hw_params(struct device *dev, void *d, - struct hdmi_codec_daifmt *daifmt, - struct hdmi_codec_params *params) -{ - struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - - if (!hdmi->hdmi_data.sink_has_audio) { - dev_err(hdmi->dev, "Sink do not support audio!\n"); - return -ENODEV; - } - - if (!hdmi->encoder.crtc) - return -ENODEV; - - switch (daifmt->fmt) { - case HDMI_I2S: - break; - default: - dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt); - return -EINVAL; - } - - hdmi->audio.sample_width = params->sample_width; - hdmi->audio.sample_rate = params->sample_rate; - hdmi->audio.channels = params->channels; - - return rk3066_hdmi_config_audio(hdmi, &hdmi->audio); -} - -static void rk3066_hdmi_audio_shutdown(struct device *dev, void *d) -{ - /* do nothing */ -} - -static int -rk3066_hdmi_audio_digital_mute(struct device *dev, void *d, bool mute) -{ - struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - - if (!hdmi->hdmi_data.sink_has_audio) { - dev_err(hdmi->dev, "Sink do not support audio!\n"); - return -ENODEV; - } - - hdmi->audio_enable = !mute; - - if (mute) - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, - HDMI_AUDIO_DISABLE, HDMI_AUDIO_DISABLE); - else - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_AUDIO_DISABLE, 0); - - /* - * Under power mode e, we need to reset audio capture logic to - * make audio setting update. - */ - if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_E) { - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, - HDMI_AUDIO_CP_LOGIC_RESET_MASK, - HDMI_AUDIO_CP_LOGIC_RESET); - usleep_range(900, 1000); - hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, - HDMI_AUDIO_CP_LOGIC_RESET_MASK, 0); - } - - return 0; -} - -static int rk3066_hdmi_audio_get_eld(struct device *dev, void *d, - uint8_t *buf, size_t len) -{ - struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); - struct drm_mode_config *config = &hdmi->encoder.dev->mode_config; - struct drm_connector *connector; - int ret = -ENODEV; - - mutex_lock(&config->mutex); - list_for_each_entry(connector, &config->connector_list, head) { - if (&hdmi->encoder == connector->encoder) { - memcpy(buf, connector->eld, - min(sizeof(connector->eld), len)); - ret = 0; - } - } - mutex_unlock(&config->mutex); - - return ret; -} - -static const struct hdmi_codec_ops audio_codec_ops = { - .hw_params = rk3066_hdmi_audio_hw_params, - .audio_shutdown = rk3066_hdmi_audio_shutdown, - .digital_mute = rk3066_hdmi_audio_digital_mute, - .get_eld = rk3066_hdmi_audio_get_eld, -}; - -static int rk3066_hdmi_audio_codec_init(struct rk3066_hdmi *hdmi, - struct device *dev) -{ - struct hdmi_codec_pdata codec_data = { - .i2s = 1, - .ops = &audio_codec_ops, - .max_i2s_channels = 8, - }; - hdmi->audio.channels = 2; - hdmi->audio.sample_rate = 48000; - hdmi->audio_enable = false; - hdmi->audio_pdev = - platform_device_register_data(dev, - HDMI_CODEC_DRV_NAME, - PLATFORM_DEVID_NONE, - &codec_data, - sizeof(codec_data)); - - return PTR_ERR_OR_ZERO(hdmi->audio_pdev); -} - -static int rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) { struct drm_encoder *encoder = &hdmi->encoder; struct device *dev = hdmi->dev; encoder->possible_crtcs = - drm_of_find_possible_crtcs(drm, dev->of_node); + rockchip_drm_of_find_possible_crtcs(drm, dev->of_node); /* * If we failed to find the CRTC(s) which this encoder is @@ -810,37 +554,20 @@ return -EPROBE_DEFER; drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs); - drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; - hdmi->connector.port = dev->of_node; drm_connector_helper_add(&hdmi->connector, &rk3066_hdmi_connector_helper_funcs); - drm_connector_init(drm, &hdmi->connector, - &rk3066_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + drm_connector_init_with_ddc(drm, &hdmi->connector, + &rk3066_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc); - drm_mode_connector_attach_encoder(&hdmi->connector, encoder); - - rk3066_hdmi_audio_codec_init(hdmi, dev); + drm_connector_attach_encoder(&hdmi->connector, encoder); return 0; -} - -static irqreturn_t rk3066_hdmi_i2c_irq(struct rk3066_hdmi *hdmi, u8 stat) -{ - struct rk3066_hdmi_i2c *i2c = hdmi->i2c; - - if (!(stat & HDMI_INTR_EDID_MASK)) - return IRQ_NONE; - - i2c->stat = stat; - - complete(&i2c->cmp); - - return IRQ_HANDLED; } static irqreturn_t rk3066_hdmi_hardirq(int irq, void *dev_id) @@ -856,8 +583,10 @@ if (interrupt) hdmi_writeb(hdmi, HDMI_INTR_STATUS1, interrupt); - if (hdmi->i2c) - ret = rk3066_hdmi_i2c_irq(hdmi, interrupt); + if (interrupt & HDMI_INTR_EDID_MASK) { + hdmi->i2c->stat = interrupt; + complete(&hdmi->i2c->cmpltn); + } if (interrupt & (HDMI_INTR_HOTPLUG | HDMI_INTR_MSENS)) ret = IRQ_WAKE_THREAD; @@ -880,7 +609,7 @@ u8 *buf = msgs->buf; int ret; - ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10); + ret = wait_for_completion_timeout(&hdmi->i2c->cmpltn, HZ / 10); if (!ret || hdmi->i2c->stat & HDMI_INTR_EDID_ERR) return -EAGAIN; @@ -893,25 +622,28 @@ static int rk3066_hdmi_i2c_write(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) { /* - * The DDC module only support read EDID message, so + * The DDC module only supports read EDID message, so * we assume that each word write to this i2c adapter - * should be the offset of EDID word address. + * should be the offset of the EDID word address. */ if (msgs->len != 1 || (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) return -EINVAL; - reinit_completion(&hdmi->i2c->cmp); + reinit_completion(&hdmi->i2c->cmpltn); if (msgs->addr == DDC_SEGMENT_ADDR) hdmi->i2c->segment_addr = msgs->buf[0]; if (msgs->addr == DDC_ADDR) hdmi->i2c->ddc_addr = msgs->buf[0]; - /* Set edid word address 0x00/0x80 */ + /* Set edid fifo first address. */ + hdmi_writeb(hdmi, HDMI_EDID_FIFO_ADDR, 0x00); + + /* Set edid word address 0x00/0x80. */ hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); - /* Set edid segment pointer */ + /* Set edid segment pointer. */ hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr); return 0; @@ -924,18 +656,19 @@ struct rk3066_hdmi_i2c *i2c = hdmi->i2c; int i, ret = 0; - mutex_lock(&i2c->lock); + mutex_lock(&i2c->i2c_lock); rk3066_hdmi_i2c_init(hdmi); - /* Unmute the interrupt */ + /* Unmute HDMI EDID interrupt. */ hdmi_modb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_EDID_MASK, HDMI_INTR_EDID_MASK); i2c->stat = 0; for (i = 0; i < num; i++) { - dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", - i + 1, num, msgs[i].len, msgs[i].flags); + DRM_DEV_DEBUG(hdmi->dev, + "xfer: num: %d/%d, len: %d, flags: %#x\n", + i + 1, num, msgs[i].len, msgs[i].flags); if (msgs[i].flags & I2C_M_RD) ret = rk3066_hdmi_i2c_read(hdmi, &msgs[i]); @@ -949,10 +682,10 @@ if (!ret) ret = num; - /* Mute HDMI EDID interrupt */ + /* Mute HDMI EDID interrupt. */ hdmi_modb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_EDID_MASK, 0); - mutex_unlock(&i2c->lock); + mutex_unlock(&i2c->i2c_lock); return ret; } @@ -963,8 +696,8 @@ } static const struct i2c_algorithm rk3066_hdmi_algorithm = { - .master_xfer = rk3066_hdmi_i2c_xfer, - .functionality = rk3066_hdmi_i2c_func, + .master_xfer = rk3066_hdmi_i2c_xfer, + .functionality = rk3066_hdmi_i2c_func, }; static struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi) @@ -977,8 +710,8 @@ if (!i2c) return ERR_PTR(-ENOMEM); - mutex_init(&i2c->lock); - init_completion(&i2c->cmp); + mutex_init(&i2c->i2c_lock); + init_completion(&i2c->cmpltn); adap = &i2c->adap; adap->class = I2C_CLASS_DDC; @@ -991,14 +724,15 @@ ret = i2c_add_adapter(adap); if (ret) { - dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); + DRM_DEV_ERROR(hdmi->dev, "cannot add %s I2C adapter\n", + adap->name); devm_kfree(hdmi->dev, i2c); return ERR_PTR(ret); } hdmi->i2c = i2c; - dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); + DRM_DEV_DEBUG(hdmi->dev, "registered %s I2C bus driver\n", adap->name); return adap; } @@ -1009,7 +743,6 @@ struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = data; struct rk3066_hdmi *hdmi; - struct resource *iores; int irq; int ret; @@ -1019,12 +752,7 @@ hdmi->dev = dev; hdmi->drm_dev = drm; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - - hdmi->regs = devm_ioremap_resource(dev, iores); + hdmi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); @@ -1032,24 +760,23 @@ if (irq < 0) return irq; - hdmi->hclk = devm_clk_get(hdmi->dev, "hclk"); + hdmi->hclk = devm_clk_get(dev, "hclk"); if (IS_ERR(hdmi->hclk)) { - dev_err(hdmi->dev, "Unable to get HDMI hclk clk\n"); + DRM_DEV_ERROR(dev, "unable to get HDMI hclk clock\n"); return PTR_ERR(hdmi->hclk); } ret = clk_prepare_enable(hdmi->hclk); if (ret) { - dev_err(hdmi->dev, "Cannot enable HDMI hclk clock: %d\n", ret); + DRM_DEV_ERROR(dev, "cannot enable HDMI hclk clock: %d\n", ret); return ret; } - hdmi->regmap = - syscon_regmap_lookup_by_phandle(hdmi->dev->of_node, - "rockchip,grf"); - if (IS_ERR(hdmi->regmap)) { - dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); - ret = PTR_ERR(hdmi->regmap); + hdmi->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(hdmi->grf_regmap)) { + DRM_DEV_ERROR(dev, "unable to get rockchip,grf\n"); + ret = PTR_ERR(hdmi->grf_regmap); goto err_disable_hclk; } @@ -1063,7 +790,6 @@ goto err_disable_hclk; } - hdmi->powermode = HDMI_SYS_POWER_MODE_A; rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_B); usleep_range(999, 1000); hdmi_writeb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_HOTPLUG); @@ -1074,7 +800,7 @@ ret = rk3066_hdmi_register(drm, hdmi); if (ret) - goto err_disable_hclk; + goto err_disable_i2c; dev_set_drvdata(dev, hdmi); @@ -1082,13 +808,17 @@ rk3066_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); if (ret) { - dev_err(hdmi->dev, - "failed to request hdmi irq: %d\n", ret); - goto err_disable_hclk; + DRM_DEV_ERROR(dev, "failed to request hdmi irq: %d\n", ret); + goto err_cleanup_hdmi; } return 0; +err_cleanup_hdmi: + hdmi->connector.funcs->destroy(&hdmi->connector); + hdmi->encoder.funcs->destroy(&hdmi->encoder); +err_disable_i2c: + i2c_put_adapter(hdmi->ddc); err_disable_hclk: clk_disable_unprepare(hdmi->hclk); @@ -1103,13 +833,13 @@ hdmi->connector.funcs->destroy(&hdmi->connector); hdmi->encoder.funcs->destroy(&hdmi->encoder); - clk_disable_unprepare(hdmi->hclk); i2c_put_adapter(hdmi->ddc); + clk_disable_unprepare(hdmi->hclk); } static const struct component_ops rk3066_hdmi_ops = { - .bind = rk3066_hdmi_bind, - .unbind = rk3066_hdmi_unbind, + .bind = rk3066_hdmi_bind, + .unbind = rk3066_hdmi_unbind, }; static int rk3066_hdmi_probe(struct platform_device *pdev) @@ -1125,24 +855,16 @@ } static const struct of_device_id rk3066_hdmi_dt_ids[] = { - { .compatible = "rockchip,rk3066-hdmi", - }, - {}, + { .compatible = "rockchip,rk3066-hdmi" }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rk3066_hdmi_dt_ids); -static struct platform_driver rk3066_hdmi_driver = { +struct platform_driver rk3066_hdmi_driver = { .probe = rk3066_hdmi_probe, .remove = rk3066_hdmi_remove, .driver = { - .name = "rk3066hdmi-rockchip", + .name = "rockchip-rk3066-hdmi", .of_match_table = rk3066_hdmi_dt_ids, }, }; - -module_platform_driver(rk3066_hdmi_driver); - -MODULE_AUTHOR("Zheng Yang <zhengyang@rock-chips.com>"); -MODULE_DESCRIPTION("Rockchip Specific RK3066-HDMI Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:rk3066hdmi-rockchip"); -- Gitblit v1.6.2