1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ |
4 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> |
5 | */ |
6 | |
7 | #include <linux/export.h> |
8 | |
9 | #include <drm/drm_atomic_helper.h> |
10 | #include <drm/drm_bridge.h> |
11 | #include <drm/drm_bridge_connector.h> |
12 | #include <drm/drm_crtc.h> |
13 | #include <drm/drm_modeset_helper_vtables.h> |
14 | #include <drm/drm_panel.h> |
15 | #include <drm/drm_of.h> |
16 | #include <drm/drm_simple_kms_helper.h> |
17 | |
18 | #include "tidss_crtc.h" |
19 | #include "tidss_drv.h" |
20 | #include "tidss_encoder.h" |
21 | |
22 | struct tidss_encoder { |
23 | struct drm_bridge bridge; |
24 | struct drm_encoder encoder; |
25 | struct drm_connector *connector; |
26 | struct drm_bridge *next_bridge; |
27 | struct tidss_device *tidss; |
28 | }; |
29 | |
30 | static inline struct tidss_encoder |
31 | *bridge_to_tidss_encoder(struct drm_bridge *b) |
32 | { |
33 | return container_of(b, struct tidss_encoder, bridge); |
34 | } |
35 | |
36 | static int tidss_bridge_attach(struct drm_bridge *bridge, |
37 | enum drm_bridge_attach_flags flags) |
38 | { |
39 | struct tidss_encoder *t_enc = bridge_to_tidss_encoder(b: bridge); |
40 | |
41 | return drm_bridge_attach(encoder: bridge->encoder, bridge: t_enc->next_bridge, |
42 | previous: bridge, flags); |
43 | } |
44 | |
45 | static int tidss_bridge_atomic_check(struct drm_bridge *bridge, |
46 | struct drm_bridge_state *bridge_state, |
47 | struct drm_crtc_state *crtc_state, |
48 | struct drm_connector_state *conn_state) |
49 | { |
50 | struct tidss_encoder *t_enc = bridge_to_tidss_encoder(b: bridge); |
51 | struct tidss_device *tidss = t_enc->tidss; |
52 | struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state); |
53 | struct drm_display_info *di = &conn_state->connector->display_info; |
54 | struct drm_bridge_state *next_bridge_state = NULL; |
55 | |
56 | if (t_enc->next_bridge) |
57 | next_bridge_state = drm_atomic_get_new_bridge_state(state: crtc_state->state, |
58 | bridge: t_enc->next_bridge); |
59 | |
60 | if (next_bridge_state) { |
61 | tcrtc_state->bus_flags = next_bridge_state->input_bus_cfg.flags; |
62 | tcrtc_state->bus_format = next_bridge_state->input_bus_cfg.format; |
63 | } else if (di->num_bus_formats) { |
64 | tcrtc_state->bus_format = di->bus_formats[0]; |
65 | tcrtc_state->bus_flags = di->bus_flags; |
66 | } else { |
67 | dev_err(tidss->dev, "%s: No bus_formats in connected display\n" , |
68 | __func__); |
69 | return -EINVAL; |
70 | } |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static const struct drm_bridge_funcs tidss_bridge_funcs = { |
76 | .attach = tidss_bridge_attach, |
77 | .atomic_check = tidss_bridge_atomic_check, |
78 | .atomic_reset = drm_atomic_helper_bridge_reset, |
79 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
80 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
81 | }; |
82 | |
83 | int tidss_encoder_create(struct tidss_device *tidss, |
84 | struct drm_bridge *next_bridge, |
85 | u32 encoder_type, u32 possible_crtcs) |
86 | { |
87 | struct tidss_encoder *t_enc; |
88 | struct drm_encoder *enc; |
89 | struct drm_connector *connector; |
90 | int ret; |
91 | |
92 | t_enc = drmm_simple_encoder_alloc(&tidss->ddev, struct tidss_encoder, |
93 | encoder, encoder_type); |
94 | if (IS_ERR(ptr: t_enc)) |
95 | return PTR_ERR(ptr: t_enc); |
96 | |
97 | t_enc->tidss = tidss; |
98 | t_enc->next_bridge = next_bridge; |
99 | t_enc->bridge.funcs = &tidss_bridge_funcs; |
100 | |
101 | enc = &t_enc->encoder; |
102 | enc->possible_crtcs = possible_crtcs; |
103 | |
104 | /* Attaching first bridge to the encoder */ |
105 | ret = drm_bridge_attach(encoder: enc, bridge: &t_enc->bridge, NULL, |
106 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
107 | if (ret) { |
108 | dev_err(tidss->dev, "bridge attach failed: %d\n" , ret); |
109 | return ret; |
110 | } |
111 | |
112 | /* Initializing the connector at the end of bridge-chain */ |
113 | connector = drm_bridge_connector_init(drm: &tidss->ddev, encoder: enc); |
114 | if (IS_ERR(ptr: connector)) { |
115 | dev_err(tidss->dev, "bridge_connector create failed\n" ); |
116 | return PTR_ERR(ptr: connector); |
117 | } |
118 | |
119 | ret = drm_connector_attach_encoder(connector, encoder: enc); |
120 | if (ret) { |
121 | dev_err(tidss->dev, "attaching encoder to connector failed\n" ); |
122 | return ret; |
123 | } |
124 | |
125 | t_enc->connector = connector; |
126 | |
127 | dev_dbg(tidss->dev, "Encoder create done\n" ); |
128 | |
129 | return ret; |
130 | } |
131 | |