// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 Rockchip Electronics Co., Ltd. * * Author: Zhang Yubing * Author: Andy Yan */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" struct rockchip_dw_dp { struct dw_dp *base; struct device *dev; struct rockchip_encoder encoder; }; static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct drm_atomic_state *state = conn_state->state; struct drm_display_info *di = &conn_state->connector->display_info; struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); struct drm_bridge_state *bridge_state = drm_atomic_get_new_bridge_state(state, bridge); u32 bus_format = bridge_state->input_bus_cfg.format; switch (bus_format) { case MEDIA_BUS_FMT_UYYVYY10_0_5X30: case MEDIA_BUS_FMT_UYYVYY8_0_5X24: s->output_mode = ROCKCHIP_OUT_MODE_YUV420; break; case MEDIA_BUS_FMT_YUYV10_1X20: case MEDIA_BUS_FMT_YUYV8_1X16: s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY; break; case MEDIA_BUS_FMT_RGB101010_1X30: case MEDIA_BUS_FMT_RGB888_1X24: case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: case MEDIA_BUS_FMT_YUV10_1X30: case MEDIA_BUS_FMT_YUV8_1X24: default: s->output_mode = ROCKCHIP_OUT_MODE_AAAA; break; } s->output_type = DRM_MODE_CONNECTOR_DisplayPort; s->bus_format = bus_format; s->bus_flags = di->bus_flags; s->color_space = V4L2_COLORSPACE_DEFAULT; return 0; } static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = { .atomic_check = dw_dp_encoder_atomic_check, }; static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct dw_dp_plat_data plat_data; struct drm_device *drm_dev = data; struct rockchip_dw_dp *dp; struct drm_encoder *encoder; struct drm_connector *connector; int ret; dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); if (!dp) return -ENOMEM; dp->dev = dev; platform_set_drvdata(pdev, dp); plat_data.max_link_rate = 810000; encoder = &dp->encoder.encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node); rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0); ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL); if (ret) return ret; drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs); dp->base = dw_dp_bind(dev, encoder, &plat_data); if (IS_ERR(dp->base)) { ret = PTR_ERR(dp->base); return ret; } connector = drm_bridge_connector_init(drm_dev, encoder); if (IS_ERR(connector)) { ret = PTR_ERR(connector); return dev_err_probe(dev, ret, "Failed to init bridge connector"); } drm_connector_attach_encoder(connector, encoder); return 0; } static const struct component_ops dw_dp_rockchip_component_ops = { .bind = dw_dp_rockchip_bind, }; static int dw_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; return component_add(dev, &dw_dp_rockchip_component_ops); } static void dw_dp_remove(struct platform_device *pdev) { struct rockchip_dw_dp *dp = platform_get_drvdata(pdev); component_del(dp->dev, &dw_dp_rockchip_component_ops); } static const struct of_device_id dw_dp_of_match[] = { { .compatible = "rockchip,rk3588-dp", }, {} }; MODULE_DEVICE_TABLE(of, dw_dp_of_match); struct platform_driver dw_dp_driver = { .probe = dw_dp_probe, .remove = dw_dp_remove, .driver = { .name = "dw-dp", .of_match_table = dw_dp_of_match, }, };