| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
|---|
| 3 | 4 | * Author: Chris Zhong <zyw@rock-chips.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This software is licensed under the terms of the GNU General Public |
|---|
| 6 | | - * License version 2, as published by the Free Software Foundation, and |
|---|
| 7 | | - * may be copied, distributed, and modified under those terms. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | | - * GNU General Public License for more details. |
|---|
| 13 | 5 | */ |
|---|
| 14 | | - |
|---|
| 15 | | -#include <drm/drmP.h> |
|---|
| 16 | | -#include <drm/drm_atomic_helper.h> |
|---|
| 17 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 18 | | -#include <drm/drm_dp_helper.h> |
|---|
| 19 | | -#include <drm/drm_edid.h> |
|---|
| 20 | | -#include <drm/drm_of.h> |
|---|
| 21 | 6 | |
|---|
| 22 | 7 | #include <linux/clk.h> |
|---|
| 23 | 8 | #include <linux/component.h> |
|---|
| 24 | | -#include <linux/extcon.h> |
|---|
| 25 | 9 | #include <linux/firmware.h> |
|---|
| 26 | | -#include <linux/regmap.h> |
|---|
| 27 | | -#include <linux/reset.h> |
|---|
| 28 | 10 | #include <linux/mfd/syscon.h> |
|---|
| 29 | 11 | #include <linux/phy/phy.h> |
|---|
| 30 | | -#include <uapi/linux/videodev2.h> |
|---|
| 12 | +#include <linux/regmap.h> |
|---|
| 13 | +#include <linux/reset.h> |
|---|
| 31 | 14 | |
|---|
| 32 | 15 | #include <sound/hdmi-codec.h> |
|---|
| 16 | + |
|---|
| 17 | +#include <drm/drm_atomic_helper.h> |
|---|
| 18 | +#include <drm/drm_dp_helper.h> |
|---|
| 19 | +#include <drm/drm_edid.h> |
|---|
| 20 | +#include <drm/drm_of.h> |
|---|
| 21 | +#include <drm/drm_probe_helper.h> |
|---|
| 22 | +#include <drm/drm_simple_kms_helper.h> |
|---|
| 33 | 23 | |
|---|
| 34 | 24 | #include "cdn-dp-core.h" |
|---|
| 35 | 25 | #include "cdn-dp-reg.h" |
|---|
| .. | .. |
|---|
| 152 | 142 | |
|---|
| 153 | 143 | static int cdn_dp_get_port_lanes(struct cdn_dp_port *port) |
|---|
| 154 | 144 | { |
|---|
| 155 | | - struct extcon_dev *edev = port->extcon; |
|---|
| 156 | | - union extcon_property_value property; |
|---|
| 157 | | - int dptx; |
|---|
| 158 | | - u8 lanes; |
|---|
| 159 | | - |
|---|
| 160 | | - dptx = extcon_get_state(edev, EXTCON_DISP_DP); |
|---|
| 161 | | - if (dptx > 0) { |
|---|
| 162 | | - extcon_get_property(edev, EXTCON_DISP_DP, |
|---|
| 163 | | - EXTCON_PROP_USB_SS, &property); |
|---|
| 164 | | - if (property.intval) |
|---|
| 165 | | - lanes = 2; |
|---|
| 166 | | - else |
|---|
| 167 | | - lanes = 4; |
|---|
| 168 | | - } else { |
|---|
| 169 | | - lanes = 0; |
|---|
| 170 | | - } |
|---|
| 171 | | - |
|---|
| 172 | | - return lanes; |
|---|
| 145 | + return phy_get_bus_width(port->phy); |
|---|
| 173 | 146 | } |
|---|
| 174 | 147 | |
|---|
| 175 | 148 | static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) |
|---|
| .. | .. |
|---|
| 203 | 176 | static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) |
|---|
| 204 | 177 | { |
|---|
| 205 | 178 | unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS); |
|---|
| 206 | | - struct cdn_dp_port *port; |
|---|
| 207 | 179 | u8 sink_count = 0; |
|---|
| 208 | 180 | |
|---|
| 209 | 181 | if (dp->active_port < 0 || dp->active_port >= dp->ports) { |
|---|
| 210 | 182 | DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n"); |
|---|
| 211 | 183 | return false; |
|---|
| 212 | 184 | } |
|---|
| 213 | | - |
|---|
| 214 | | - port = dp->port[dp->active_port]; |
|---|
| 215 | 185 | |
|---|
| 216 | 186 | /* |
|---|
| 217 | 187 | * Attempt to read sink count, retry in case the sink may not be ready. |
|---|
| .. | .. |
|---|
| 220 | 190 | * some docks need more time to power up. |
|---|
| 221 | 191 | */ |
|---|
| 222 | 192 | while (time_before(jiffies, timeout)) { |
|---|
| 223 | | - if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) |
|---|
| 224 | | - return false; |
|---|
| 225 | | - |
|---|
| 226 | 193 | if (!cdn_dp_get_sink_count(dp, &sink_count)) |
|---|
| 227 | 194 | return sink_count ? true : false; |
|---|
| 228 | 195 | |
|---|
| .. | .. |
|---|
| 253 | 220 | drm_connector_cleanup(connector); |
|---|
| 254 | 221 | } |
|---|
| 255 | 222 | |
|---|
| 256 | | -static int |
|---|
| 257 | | -cdn_dp_atomic_connector_get_property(struct drm_connector *connector, |
|---|
| 258 | | - const struct drm_connector_state *state, |
|---|
| 259 | | - struct drm_property *property, |
|---|
| 260 | | - uint64_t *val) |
|---|
| 223 | +static void cdn_dp_oob_hotplug_event(struct drm_connector *connector) |
|---|
| 261 | 224 | { |
|---|
| 262 | 225 | struct cdn_dp_device *dp = connector_to_dp(connector); |
|---|
| 263 | | - struct rockchip_drm_private *private = connector->dev->dev_private; |
|---|
| 264 | 226 | |
|---|
| 265 | | - if (property == private->connector_id_prop) { |
|---|
| 266 | | - *val = dp->id; |
|---|
| 267 | | - return 0; |
|---|
| 268 | | - } |
|---|
| 269 | | - |
|---|
| 270 | | - DRM_ERROR("failed to get rockchip CDN DP property\n"); |
|---|
| 271 | | - return -EINVAL; |
|---|
| 227 | + schedule_delayed_work(&dp->event_work, msecs_to_jiffies(100)); |
|---|
| 272 | 228 | } |
|---|
| 273 | 229 | |
|---|
| 274 | 230 | static const struct drm_connector_funcs cdn_dp_atomic_connector_funcs = { |
|---|
| .. | .. |
|---|
| 278 | 234 | .reset = drm_atomic_helper_connector_reset, |
|---|
| 279 | 235 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
|---|
| 280 | 236 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
|---|
| 281 | | - .atomic_get_property = cdn_dp_atomic_connector_get_property, |
|---|
| 282 | 237 | }; |
|---|
| 283 | 238 | |
|---|
| 284 | 239 | static int cdn_dp_connector_get_modes(struct drm_connector *connector) |
|---|
| .. | .. |
|---|
| 298 | 253 | if (ret) |
|---|
| 299 | 254 | drm_connector_update_edid_property(connector, |
|---|
| 300 | 255 | edid); |
|---|
| 301 | | - } else { |
|---|
| 302 | | - ret = rockchip_drm_add_modes_noedid(connector); |
|---|
| 303 | | - |
|---|
| 304 | | - dev_info(dp->dev, "failed to get edid\n"); |
|---|
| 305 | 256 | } |
|---|
| 306 | 257 | mutex_unlock(&dp->lock); |
|---|
| 307 | 258 | |
|---|
| 308 | 259 | return ret; |
|---|
| 309 | 260 | } |
|---|
| 310 | 261 | |
|---|
| 311 | | -static int cdn_dp_connector_mode_valid(struct drm_connector *connector, |
|---|
| 312 | | - struct drm_display_mode *mode) |
|---|
| 262 | +static enum drm_mode_status |
|---|
| 263 | +cdn_dp_connector_mode_valid(struct drm_connector *connector, |
|---|
| 264 | + struct drm_display_mode *mode) |
|---|
| 313 | 265 | { |
|---|
| 314 | 266 | struct cdn_dp_device *dp = connector_to_dp(connector); |
|---|
| 315 | 267 | struct drm_display_info *display_info = &dp->connector.display_info; |
|---|
| 316 | 268 | u32 requested, actual, rate, sink_max, source_max = 0; |
|---|
| 317 | | - struct drm_encoder *encoder = connector->encoder; |
|---|
| 318 | | - enum drm_mode_status status = MODE_OK; |
|---|
| 319 | | - struct drm_device *dev = connector->dev; |
|---|
| 320 | | - struct rockchip_drm_private *priv = dev->dev_private; |
|---|
| 321 | | - struct drm_crtc *crtc; |
|---|
| 322 | 269 | u8 lanes, bpc; |
|---|
| 323 | 270 | |
|---|
| 324 | 271 | /* If DP is disconnected, every mode is invalid */ |
|---|
| .. | .. |
|---|
| 336 | 283 | bpc = 8; |
|---|
| 337 | 284 | break; |
|---|
| 338 | 285 | } |
|---|
| 339 | | - |
|---|
| 340 | | - if (!IS_ALIGNED(mode->hdisplay * bpc, 8)) |
|---|
| 341 | | - return MODE_H_ILLEGAL; |
|---|
| 342 | 286 | |
|---|
| 343 | 287 | requested = mode->clock * bpc * 3 / 1000; |
|---|
| 344 | 288 | |
|---|
| .. | .. |
|---|
| 360 | 304 | "requested=%d, actual=%d, clock=%d\n", |
|---|
| 361 | 305 | requested, actual, mode->clock); |
|---|
| 362 | 306 | return MODE_CLOCK_HIGH; |
|---|
| 363 | | - } |
|---|
| 364 | | - |
|---|
| 365 | | - if (!encoder) { |
|---|
| 366 | | - const struct drm_connector_helper_funcs *funcs; |
|---|
| 367 | | - |
|---|
| 368 | | - funcs = connector->helper_private; |
|---|
| 369 | | - if (funcs->atomic_best_encoder) |
|---|
| 370 | | - encoder = funcs->atomic_best_encoder(connector, |
|---|
| 371 | | - connector->state); |
|---|
| 372 | | - else if (funcs->best_encoder) |
|---|
| 373 | | - encoder = funcs->best_encoder(connector); |
|---|
| 374 | | - else |
|---|
| 375 | | - encoder = drm_atomic_helper_best_encoder(connector); |
|---|
| 376 | | - } |
|---|
| 377 | | - |
|---|
| 378 | | - if (!encoder || !encoder->possible_crtcs) |
|---|
| 379 | | - return MODE_BAD; |
|---|
| 380 | | - /* |
|---|
| 381 | | - * ensure all drm display mode can work, if someone want support more |
|---|
| 382 | | - * resolutions, please limit the possible_crtc, only connect to |
|---|
| 383 | | - * needed crtc. |
|---|
| 384 | | - */ |
|---|
| 385 | | - drm_for_each_crtc(crtc, connector->dev) { |
|---|
| 386 | | - int pipe = drm_crtc_index(crtc); |
|---|
| 387 | | - const struct rockchip_crtc_funcs *funcs = |
|---|
| 388 | | - priv->crtc_funcs[pipe]; |
|---|
| 389 | | - |
|---|
| 390 | | - if (!(encoder->possible_crtcs & drm_crtc_mask(crtc))) |
|---|
| 391 | | - continue; |
|---|
| 392 | | - if (!funcs || !funcs->mode_valid) |
|---|
| 393 | | - continue; |
|---|
| 394 | | - |
|---|
| 395 | | - status = funcs->mode_valid(crtc, mode, |
|---|
| 396 | | - DRM_MODE_CONNECTOR_HDMIA); |
|---|
| 397 | | - if (status != MODE_OK) |
|---|
| 398 | | - return status; |
|---|
| 399 | 307 | } |
|---|
| 400 | 308 | |
|---|
| 401 | 309 | return MODE_OK; |
|---|
| .. | .. |
|---|
| 458 | 366 | |
|---|
| 459 | 367 | static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) |
|---|
| 460 | 368 | { |
|---|
| 461 | | - union extcon_property_value property; |
|---|
| 462 | 369 | int ret; |
|---|
| 463 | 370 | |
|---|
| 464 | 371 | if (!port->phy_enabled) { |
|---|
| .. | .. |
|---|
| 485 | 392 | goto err_power_on; |
|---|
| 486 | 393 | } |
|---|
| 487 | 394 | |
|---|
| 488 | | - ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, |
|---|
| 489 | | - EXTCON_PROP_USB_TYPEC_POLARITY, &property); |
|---|
| 490 | | - if (ret) { |
|---|
| 491 | | - DRM_DEV_ERROR(dp->dev, "get property failed\n"); |
|---|
| 492 | | - goto err_power_on; |
|---|
| 493 | | - } |
|---|
| 494 | | - |
|---|
| 495 | 395 | port->lanes = cdn_dp_get_port_lanes(port); |
|---|
| 496 | | - ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval); |
|---|
| 396 | + ret = cdn_dp_set_host_cap(dp, port->lanes, 0); |
|---|
| 497 | 397 | if (ret) { |
|---|
| 498 | 398 | DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n", |
|---|
| 499 | 399 | ret); |
|---|
| .. | .. |
|---|
| 555 | 455 | cdn_dp_set_firmware_active(dp, false); |
|---|
| 556 | 456 | cdn_dp_clk_disable(dp); |
|---|
| 557 | 457 | dp->active = false; |
|---|
| 558 | | - dp->link.rate = 0; |
|---|
| 559 | | - dp->link.num_lanes = 0; |
|---|
| 458 | + dp->max_lanes = 0; |
|---|
| 459 | + dp->max_rate = 0; |
|---|
| 560 | 460 | if (!dp->connected) { |
|---|
| 561 | 461 | kfree(dp->edid); |
|---|
| 562 | 462 | dp->edid = NULL; |
|---|
| .. | .. |
|---|
| 639 | 539 | video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); |
|---|
| 640 | 540 | video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); |
|---|
| 641 | 541 | |
|---|
| 642 | | - memcpy(&dp->mode, adjusted, sizeof(*mode)); |
|---|
| 542 | + drm_mode_copy(&dp->mode, adjusted); |
|---|
| 643 | 543 | } |
|---|
| 644 | 544 | |
|---|
| 645 | 545 | static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) |
|---|
| .. | .. |
|---|
| 648 | 548 | struct cdn_dp_port *port = cdn_dp_connected_port(dp); |
|---|
| 649 | 549 | u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd); |
|---|
| 650 | 550 | |
|---|
| 651 | | - if (!port || !dp->link.rate || !dp->link.num_lanes) |
|---|
| 551 | + if (!port || !dp->max_rate || !dp->max_lanes) |
|---|
| 652 | 552 | return false; |
|---|
| 653 | 553 | |
|---|
| 654 | 554 | if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) != |
|---|
| .. | .. |
|---|
| 751 | 651 | * run the event_work to re-connect it. |
|---|
| 752 | 652 | */ |
|---|
| 753 | 653 | if (!dp->connected && cdn_dp_connected_port(dp)) |
|---|
| 754 | | - schedule_work(&dp->event_work); |
|---|
| 654 | + schedule_delayed_work(&dp->event_work, 0); |
|---|
| 755 | 655 | } |
|---|
| 756 | 656 | |
|---|
| 757 | 657 | static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder, |
|---|
| 758 | 658 | struct drm_crtc_state *crtc_state, |
|---|
| 759 | 659 | struct drm_connector_state *conn_state) |
|---|
| 760 | 660 | { |
|---|
| 761 | | - struct cdn_dp_device *dp = encoder_to_dp(encoder); |
|---|
| 762 | | - struct drm_display_info *di = &dp->connector.display_info; |
|---|
| 763 | 661 | struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); |
|---|
| 764 | | - |
|---|
| 765 | | - switch (di->bpc) { |
|---|
| 766 | | - case 6: |
|---|
| 767 | | - s->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; |
|---|
| 768 | | - break; |
|---|
| 769 | | - case 8: |
|---|
| 770 | | - default: |
|---|
| 771 | | - s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
|---|
| 772 | | - break; |
|---|
| 773 | | - } |
|---|
| 774 | 662 | |
|---|
| 775 | 663 | s->output_mode = ROCKCHIP_OUT_MODE_AAAA; |
|---|
| 776 | 664 | s->output_type = DRM_MODE_CONNECTOR_DisplayPort; |
|---|
| 777 | 665 | s->tv_state = &conn_state->tv; |
|---|
| 778 | | - s->eotf = TRADITIONAL_GAMMA_SDR; |
|---|
| 779 | | - s->color_space = V4L2_COLORSPACE_DEFAULT; |
|---|
| 780 | 666 | |
|---|
| 781 | 667 | return 0; |
|---|
| 782 | 668 | } |
|---|
| .. | .. |
|---|
| 786 | 672 | .enable = cdn_dp_encoder_enable, |
|---|
| 787 | 673 | .disable = cdn_dp_encoder_disable, |
|---|
| 788 | 674 | .atomic_check = cdn_dp_encoder_atomic_check, |
|---|
| 789 | | -}; |
|---|
| 790 | | - |
|---|
| 791 | | -static const struct drm_encoder_funcs cdn_dp_encoder_funcs = { |
|---|
| 792 | | - .destroy = drm_encoder_cleanup, |
|---|
| 793 | 675 | }; |
|---|
| 794 | 676 | |
|---|
| 795 | 677 | static int cdn_dp_parse_dt(struct cdn_dp_device *dp) |
|---|
| .. | .. |
|---|
| 863 | 745 | return 0; |
|---|
| 864 | 746 | } |
|---|
| 865 | 747 | |
|---|
| 866 | | -struct dp_sdp { |
|---|
| 867 | | - struct dp_sdp_header sdp_header; |
|---|
| 868 | | - u8 db[28]; |
|---|
| 869 | | -} __packed; |
|---|
| 870 | | - |
|---|
| 871 | | -static int cdn_dp_setup_audio_infoframe(struct cdn_dp_device *dp) |
|---|
| 872 | | -{ |
|---|
| 873 | | - struct dp_sdp infoframe_sdp; |
|---|
| 874 | | - struct hdmi_audio_infoframe frame; |
|---|
| 875 | | - u8 buffer[14]; |
|---|
| 876 | | - ssize_t err; |
|---|
| 877 | | - |
|---|
| 878 | | - /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ |
|---|
| 879 | | - memset(&infoframe_sdp, 0, sizeof(infoframe_sdp)); |
|---|
| 880 | | - infoframe_sdp.sdp_header.HB0 = 0; |
|---|
| 881 | | - infoframe_sdp.sdp_header.HB1 = HDMI_INFOFRAME_TYPE_AUDIO; |
|---|
| 882 | | - infoframe_sdp.sdp_header.HB2 = 0x1b; |
|---|
| 883 | | - infoframe_sdp.sdp_header.HB3 = 0x48; |
|---|
| 884 | | - |
|---|
| 885 | | - err = hdmi_audio_infoframe_init(&frame); |
|---|
| 886 | | - if (err < 0) { |
|---|
| 887 | | - DRM_DEV_ERROR(dp->dev, "Failed to setup audio infoframe: %zd\n", |
|---|
| 888 | | - err); |
|---|
| 889 | | - return err; |
|---|
| 890 | | - } |
|---|
| 891 | | - |
|---|
| 892 | | - frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; |
|---|
| 893 | | - frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; |
|---|
| 894 | | - frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; |
|---|
| 895 | | - frame.channels = 0; |
|---|
| 896 | | - |
|---|
| 897 | | - err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
|---|
| 898 | | - if (err < 0) { |
|---|
| 899 | | - DRM_DEV_ERROR(dp->dev, "Failed to pack audio infoframe: %zd\n", |
|---|
| 900 | | - err); |
|---|
| 901 | | - return err; |
|---|
| 902 | | - } |
|---|
| 903 | | - |
|---|
| 904 | | - memcpy(&infoframe_sdp.db[0], &buffer[HDMI_INFOFRAME_HEADER_SIZE], |
|---|
| 905 | | - sizeof(buffer) - HDMI_INFOFRAME_HEADER_SIZE); |
|---|
| 906 | | - |
|---|
| 907 | | - cdn_dp_infoframe_set(dp, 0, (u8 *)&infoframe_sdp, |
|---|
| 908 | | - sizeof(infoframe_sdp), 0x84); |
|---|
| 909 | | - |
|---|
| 910 | | - return 0; |
|---|
| 911 | | -} |
|---|
| 912 | | - |
|---|
| 913 | 748 | static int cdn_dp_audio_hw_params(struct device *dev, void *data, |
|---|
| 914 | 749 | struct hdmi_codec_daifmt *daifmt, |
|---|
| 915 | 750 | struct hdmi_codec_params *params) |
|---|
| .. | .. |
|---|
| 924 | 759 | |
|---|
| 925 | 760 | mutex_lock(&dp->lock); |
|---|
| 926 | 761 | if (!dp->active) { |
|---|
| 927 | | - ret = 0; |
|---|
| 762 | + ret = -ENODEV; |
|---|
| 928 | 763 | goto out; |
|---|
| 929 | 764 | } |
|---|
| 930 | 765 | |
|---|
| .. | .. |
|---|
| 940 | 775 | ret = -EINVAL; |
|---|
| 941 | 776 | goto out; |
|---|
| 942 | 777 | } |
|---|
| 943 | | - |
|---|
| 944 | | - ret = cdn_dp_setup_audio_infoframe(dp); |
|---|
| 945 | | - if (ret) |
|---|
| 946 | | - goto out; |
|---|
| 947 | 778 | |
|---|
| 948 | 779 | ret = cdn_dp_audio_config(dp, &audio); |
|---|
| 949 | 780 | if (!ret) |
|---|
| .. | .. |
|---|
| 970 | 801 | mutex_unlock(&dp->lock); |
|---|
| 971 | 802 | } |
|---|
| 972 | 803 | |
|---|
| 973 | | -static int cdn_dp_audio_digital_mute(struct device *dev, void *data, |
|---|
| 974 | | - bool enable) |
|---|
| 804 | +static int cdn_dp_audio_mute_stream(struct device *dev, void *data, |
|---|
| 805 | + bool enable, int direction) |
|---|
| 975 | 806 | { |
|---|
| 976 | 807 | struct cdn_dp_device *dp = dev_get_drvdata(dev); |
|---|
| 977 | 808 | int ret; |
|---|
| 978 | 809 | |
|---|
| 979 | 810 | mutex_lock(&dp->lock); |
|---|
| 980 | 811 | if (!dp->active) { |
|---|
| 981 | | - ret = 0; |
|---|
| 812 | + ret = -ENODEV; |
|---|
| 982 | 813 | goto out; |
|---|
| 983 | 814 | } |
|---|
| 984 | 815 | |
|---|
| .. | .. |
|---|
| 1002 | 833 | static const struct hdmi_codec_ops audio_codec_ops = { |
|---|
| 1003 | 834 | .hw_params = cdn_dp_audio_hw_params, |
|---|
| 1004 | 835 | .audio_shutdown = cdn_dp_audio_shutdown, |
|---|
| 1005 | | - .digital_mute = cdn_dp_audio_digital_mute, |
|---|
| 836 | + .mute_stream = cdn_dp_audio_mute_stream, |
|---|
| 1006 | 837 | .get_eld = cdn_dp_audio_get_eld, |
|---|
| 838 | + .no_capture_mute = 1, |
|---|
| 1007 | 839 | }; |
|---|
| 1008 | 840 | |
|---|
| 1009 | 841 | static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp, |
|---|
| .. | .. |
|---|
| 1038 | 870 | mutex_unlock(&dp->lock); |
|---|
| 1039 | 871 | |
|---|
| 1040 | 872 | while (time_before(jiffies, timeout)) { |
|---|
| 1041 | | - ret = request_firmware_direct(&dp->fw, CDN_DP_FIRMWARE, |
|---|
| 1042 | | - dp->dev); |
|---|
| 873 | + ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev); |
|---|
| 1043 | 874 | if (ret == -ENOENT) { |
|---|
| 1044 | 875 | msleep(sleep); |
|---|
| 1045 | 876 | sleep *= 2; |
|---|
| .. | .. |
|---|
| 1062 | 893 | return ret; |
|---|
| 1063 | 894 | } |
|---|
| 1064 | 895 | |
|---|
| 1065 | | -static bool cdn_dp_needs_link_retrain(struct cdn_dp_device *dp) |
|---|
| 1066 | | -{ |
|---|
| 1067 | | - u8 link_status[DP_LINK_STATUS_SIZE]; |
|---|
| 1068 | | - |
|---|
| 1069 | | - /* |
|---|
| 1070 | | - * Validate the cached values of link parameters before attempting to |
|---|
| 1071 | | - * retrain. |
|---|
| 1072 | | - */ |
|---|
| 1073 | | - if (!dp->link.rate || !dp->link.num_lanes) |
|---|
| 1074 | | - return false; |
|---|
| 1075 | | - |
|---|
| 1076 | | - if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) < 0) |
|---|
| 1077 | | - return false; |
|---|
| 1078 | | - |
|---|
| 1079 | | - /* Retrain if Channel EQ or CR not ok */ |
|---|
| 1080 | | - return !drm_dp_channel_eq_ok(link_status, dp->link.num_lanes); |
|---|
| 1081 | | -} |
|---|
| 1082 | | - |
|---|
| 1083 | 896 | static void cdn_dp_pd_event_work(struct work_struct *work) |
|---|
| 1084 | 897 | { |
|---|
| 1085 | | - struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device, |
|---|
| 898 | + struct cdn_dp_device *dp = container_of(to_delayed_work(work), struct cdn_dp_device, |
|---|
| 1086 | 899 | event_work); |
|---|
| 1087 | 900 | struct drm_connector *connector = &dp->connector; |
|---|
| 1088 | 901 | enum drm_connector_status old_status; |
|---|
| .. | .. |
|---|
| 1120 | 933 | dp->connected = false; |
|---|
| 1121 | 934 | |
|---|
| 1122 | 935 | /* Enabled and connected with a sink, re-train if requested */ |
|---|
| 1123 | | - } else if (cdn_dp_needs_link_retrain(dp)) { |
|---|
| 1124 | | - unsigned int rate = dp->link.rate; |
|---|
| 1125 | | - unsigned int lanes = dp->link.num_lanes; |
|---|
| 936 | + } else if (!cdn_dp_check_link_status(dp)) { |
|---|
| 937 | + unsigned int rate = dp->max_rate; |
|---|
| 938 | + unsigned int lanes = dp->max_lanes; |
|---|
| 1126 | 939 | struct drm_display_mode *mode = &dp->mode; |
|---|
| 1127 | 940 | |
|---|
| 1128 | 941 | DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n"); |
|---|
| .. | .. |
|---|
| 1135 | 948 | |
|---|
| 1136 | 949 | /* If training result is changed, update the video config */ |
|---|
| 1137 | 950 | if (mode->clock && |
|---|
| 1138 | | - (rate != dp->link.rate || lanes != dp->link.num_lanes)) { |
|---|
| 951 | + (rate != dp->max_rate || lanes != dp->max_lanes)) { |
|---|
| 1139 | 952 | ret = cdn_dp_config_video(dp); |
|---|
| 1140 | 953 | if (ret) { |
|---|
| 1141 | 954 | dp->connected = false; |
|---|
| .. | .. |
|---|
| 1153 | 966 | connector->status = connector->funcs->detect(connector, false); |
|---|
| 1154 | 967 | if (old_status != connector->status) |
|---|
| 1155 | 968 | drm_kms_helper_hotplug_event(dp->drm_dev); |
|---|
| 1156 | | -} |
|---|
| 1157 | | - |
|---|
| 1158 | | -static int cdn_dp_pd_event(struct notifier_block *nb, |
|---|
| 1159 | | - unsigned long event, void *priv) |
|---|
| 1160 | | -{ |
|---|
| 1161 | | - struct cdn_dp_port *port = container_of(nb, struct cdn_dp_port, |
|---|
| 1162 | | - event_nb); |
|---|
| 1163 | | - struct cdn_dp_device *dp = port->dp; |
|---|
| 1164 | | - |
|---|
| 1165 | | - /* |
|---|
| 1166 | | - * It would be nice to be able to just do the work inline right here. |
|---|
| 1167 | | - * However, we need to make a bunch of calls that might sleep in order |
|---|
| 1168 | | - * to turn on the block/phy, so use a worker instead. |
|---|
| 1169 | | - */ |
|---|
| 1170 | | - schedule_work(&dp->event_work); |
|---|
| 1171 | | - |
|---|
| 1172 | | - return NOTIFY_DONE; |
|---|
| 1173 | 969 | } |
|---|
| 1174 | 970 | |
|---|
| 1175 | 971 | static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, |
|---|
| .. | .. |
|---|
| 1211 | 1007 | struct cdn_dp_device *dp = dev_get_drvdata(dev); |
|---|
| 1212 | 1008 | struct drm_encoder *encoder; |
|---|
| 1213 | 1009 | struct drm_connector *connector; |
|---|
| 1214 | | - struct cdn_dp_port *port; |
|---|
| 1215 | 1010 | struct drm_device *drm_dev = data; |
|---|
| 1216 | | - struct rockchip_drm_private *private = drm_dev->dev_private; |
|---|
| 1217 | | - int ret, i; |
|---|
| 1011 | + int ret; |
|---|
| 1218 | 1012 | |
|---|
| 1219 | 1013 | ret = cdn_dp_parse_dt(dp); |
|---|
| 1220 | 1014 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 1233 | 1027 | if (ret) |
|---|
| 1234 | 1028 | return ret; |
|---|
| 1235 | 1029 | |
|---|
| 1236 | | - INIT_WORK(&dp->event_work, cdn_dp_pd_event_work); |
|---|
| 1030 | + INIT_DELAYED_WORK(&dp->event_work, cdn_dp_pd_event_work); |
|---|
| 1237 | 1031 | |
|---|
| 1238 | 1032 | encoder = &dp->encoder; |
|---|
| 1239 | 1033 | |
|---|
| 1240 | | - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, |
|---|
| 1241 | | - dev->of_node); |
|---|
| 1034 | + encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev, |
|---|
| 1035 | + dev->of_node); |
|---|
| 1242 | 1036 | DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); |
|---|
| 1243 | 1037 | |
|---|
| 1244 | | - ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs, |
|---|
| 1245 | | - DRM_MODE_ENCODER_TMDS, NULL); |
|---|
| 1038 | + ret = drm_simple_encoder_init(drm_dev, encoder, |
|---|
| 1039 | + DRM_MODE_ENCODER_TMDS); |
|---|
| 1246 | 1040 | if (ret) { |
|---|
| 1247 | 1041 | DRM_ERROR("failed to initialize encoder with drm\n"); |
|---|
| 1248 | 1042 | return ret; |
|---|
| .. | .. |
|---|
| 1269 | 1063 | DRM_ERROR("failed to attach connector and encoder\n"); |
|---|
| 1270 | 1064 | goto err_free_connector; |
|---|
| 1271 | 1065 | } |
|---|
| 1272 | | - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); |
|---|
| 1273 | 1066 | |
|---|
| 1274 | | - for (i = 0; i < dp->ports; i++) { |
|---|
| 1275 | | - port = dp->port[i]; |
|---|
| 1276 | | - |
|---|
| 1277 | | - port->event_nb.notifier_call = cdn_dp_pd_event; |
|---|
| 1278 | | - ret = devm_extcon_register_notifier(dp->dev, port->extcon, |
|---|
| 1279 | | - EXTCON_DISP_DP, |
|---|
| 1280 | | - &port->event_nb); |
|---|
| 1281 | | - if (ret) { |
|---|
| 1282 | | - DRM_DEV_ERROR(dev, |
|---|
| 1283 | | - "register EXTCON_DISP_DP notifier err\n"); |
|---|
| 1284 | | - goto err_free_connector; |
|---|
| 1285 | | - } |
|---|
| 1286 | | - } |
|---|
| 1067 | + dp->sub_dev.connector = &dp->connector; |
|---|
| 1068 | + dp->sub_dev.of_node = dev->of_node; |
|---|
| 1069 | + dp->sub_dev.oob_hotplug_event = cdn_dp_oob_hotplug_event; |
|---|
| 1070 | + rockchip_drm_register_sub_dev(&dp->sub_dev); |
|---|
| 1287 | 1071 | |
|---|
| 1288 | 1072 | pm_runtime_enable(dev); |
|---|
| 1289 | 1073 | |
|---|
| 1290 | | - schedule_work(&dp->event_work); |
|---|
| 1074 | + schedule_delayed_work(&dp->event_work, 0); |
|---|
| 1291 | 1075 | |
|---|
| 1292 | 1076 | return 0; |
|---|
| 1293 | 1077 | |
|---|
| .. | .. |
|---|
| 1304 | 1088 | struct drm_encoder *encoder = &dp->encoder; |
|---|
| 1305 | 1089 | struct drm_connector *connector = &dp->connector; |
|---|
| 1306 | 1090 | |
|---|
| 1307 | | - cancel_work_sync(&dp->event_work); |
|---|
| 1091 | + cancel_delayed_work_sync(&dp->event_work); |
|---|
| 1308 | 1092 | cdn_dp_encoder_disable(encoder); |
|---|
| 1309 | 1093 | encoder->funcs->destroy(encoder); |
|---|
| 1310 | 1094 | connector->funcs->destroy(connector); |
|---|
| .. | .. |
|---|
| 1335 | 1119 | return ret; |
|---|
| 1336 | 1120 | } |
|---|
| 1337 | 1121 | |
|---|
| 1338 | | -static int cdn_dp_resume(struct device *dev) |
|---|
| 1122 | +static __maybe_unused int cdn_dp_resume(struct device *dev) |
|---|
| 1339 | 1123 | { |
|---|
| 1340 | 1124 | struct cdn_dp_device *dp = dev_get_drvdata(dev); |
|---|
| 1341 | 1125 | |
|---|
| 1342 | 1126 | mutex_lock(&dp->lock); |
|---|
| 1343 | 1127 | dp->suspended = false; |
|---|
| 1344 | 1128 | if (dp->fw_loaded) |
|---|
| 1345 | | - schedule_work(&dp->event_work); |
|---|
| 1129 | + schedule_delayed_work(&dp->event_work, 0); |
|---|
| 1346 | 1130 | mutex_unlock(&dp->lock); |
|---|
| 1347 | 1131 | |
|---|
| 1348 | 1132 | return 0; |
|---|
| .. | .. |
|---|
| 1355 | 1139 | struct cdn_dp_data *dp_data; |
|---|
| 1356 | 1140 | struct cdn_dp_port *port; |
|---|
| 1357 | 1141 | struct cdn_dp_device *dp; |
|---|
| 1358 | | - struct extcon_dev *extcon; |
|---|
| 1359 | 1142 | struct phy *phy; |
|---|
| 1360 | | - int i, id; |
|---|
| 1143 | + int i; |
|---|
| 1361 | 1144 | |
|---|
| 1362 | 1145 | dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); |
|---|
| 1363 | 1146 | if (!dp) |
|---|
| 1364 | 1147 | return -ENOMEM; |
|---|
| 1365 | | - id = of_alias_get_id(dev->of_node, "dp"); |
|---|
| 1366 | | - if (id < 0) |
|---|
| 1367 | | - id = 0; |
|---|
| 1368 | | - |
|---|
| 1369 | | - dp->id = id; |
|---|
| 1370 | 1148 | dp->dev = dev; |
|---|
| 1371 | 1149 | |
|---|
| 1372 | 1150 | match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node); |
|---|
| 1373 | 1151 | dp_data = (struct cdn_dp_data *)match->data; |
|---|
| 1374 | 1152 | |
|---|
| 1375 | 1153 | for (i = 0; i < dp_data->max_phy; i++) { |
|---|
| 1376 | | - extcon = extcon_get_edev_by_phandle(dev, i); |
|---|
| 1377 | 1154 | phy = devm_of_phy_get_by_index(dev, dev->of_node, i); |
|---|
| 1378 | 1155 | |
|---|
| 1379 | | - if (PTR_ERR(extcon) == -EPROBE_DEFER || |
|---|
| 1380 | | - PTR_ERR(phy) == -EPROBE_DEFER) |
|---|
| 1156 | + if (PTR_ERR(phy) == -EPROBE_DEFER) |
|---|
| 1381 | 1157 | return -EPROBE_DEFER; |
|---|
| 1382 | 1158 | |
|---|
| 1383 | | - if (IS_ERR(extcon) || IS_ERR(phy)) |
|---|
| 1159 | + if (IS_ERR(phy)) |
|---|
| 1384 | 1160 | continue; |
|---|
| 1385 | 1161 | |
|---|
| 1386 | 1162 | port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); |
|---|
| 1387 | 1163 | if (!port) |
|---|
| 1388 | 1164 | return -ENOMEM; |
|---|
| 1389 | 1165 | |
|---|
| 1390 | | - port->extcon = extcon; |
|---|
| 1391 | 1166 | port->phy = phy; |
|---|
| 1392 | 1167 | port->dp = dp; |
|---|
| 1393 | 1168 | port->id = i; |
|---|
| .. | .. |
|---|
| 1395 | 1170 | } |
|---|
| 1396 | 1171 | |
|---|
| 1397 | 1172 | if (!dp->ports) { |
|---|
| 1398 | | - DRM_DEV_ERROR(dev, "missing extcon or phy\n"); |
|---|
| 1173 | + DRM_DEV_ERROR(dev, "missing phy\n"); |
|---|
| 1399 | 1174 | return -EINVAL; |
|---|
| 1400 | 1175 | } |
|---|
| 1401 | 1176 | |
|---|