|
6 | 6 |
|
7 | 7 | #include <linux/export.h> |
8 | 8 |
|
| 9 | +#include <drm/drm_atomic_helper.h> |
| 10 | +#include <drm/drm_bridge.h> |
| 11 | +#include <drm/drm_bridge_connector.h> |
9 | 12 | #include <drm/drm_crtc.h> |
10 | 13 | #include <drm/drm_modeset_helper_vtables.h> |
11 | 14 | #include <drm/drm_panel.h> |
12 | 15 | #include <drm/drm_of.h> |
| 16 | +#include <drm/drm_simple_kms_helper.h> |
13 | 17 |
|
14 | 18 | #include "tidss_crtc.h" |
15 | 19 | #include "tidss_drv.h" |
16 | 20 | #include "tidss_encoder.h" |
17 | 21 |
|
18 | | -static int tidss_encoder_atomic_check(struct drm_encoder *encoder, |
19 | | - struct drm_crtc_state *crtc_state, |
20 | | - struct drm_connector_state *conn_state) |
| 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(bridge); |
| 40 | + |
| 41 | + return drm_bridge_attach(bridge->encoder, t_enc->next_bridge, |
| 42 | + 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) |
21 | 49 | { |
22 | | - struct drm_device *ddev = encoder->dev; |
| 50 | + struct tidss_encoder *t_enc = bridge_to_tidss_encoder(bridge); |
| 51 | + struct tidss_device *tidss = t_enc->tidss; |
23 | 52 | struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state); |
24 | 53 | struct drm_display_info *di = &conn_state->connector->display_info; |
25 | | - struct drm_bridge *bridge; |
26 | | - bool bus_flags_set = false; |
27 | | - |
28 | | - dev_dbg(ddev->dev, "%s\n", __func__); |
29 | | - |
30 | | - /* |
31 | | - * Take the bus_flags from the first bridge that defines |
32 | | - * bridge timings, or from the connector's display_info if no |
33 | | - * bridge defines the timings. |
34 | | - */ |
35 | | - drm_for_each_bridge_in_chain(encoder, bridge) { |
36 | | - if (!bridge->timings) |
37 | | - continue; |
38 | | - |
39 | | - tcrtc_state->bus_flags = bridge->timings->input_bus_flags; |
40 | | - bus_flags_set = true; |
41 | | - break; |
42 | | - } |
| 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(crtc_state->state, |
| 58 | + t_enc->next_bridge); |
43 | 59 |
|
44 | | - if (!di->bus_formats || di->num_bus_formats == 0) { |
45 | | - dev_err(ddev->dev, "%s: No bus_formats in connected display\n", |
| 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", |
46 | 68 | __func__); |
47 | 69 | return -EINVAL; |
48 | 70 | } |
49 | 71 |
|
50 | | - // XXX any cleaner way to set bus format and flags? |
51 | | - tcrtc_state->bus_format = di->bus_formats[0]; |
52 | | - if (!bus_flags_set) |
53 | | - tcrtc_state->bus_flags = di->bus_flags; |
54 | | - |
55 | 72 | return 0; |
56 | 73 | } |
57 | 74 |
|
58 | | -static void tidss_encoder_destroy(struct drm_encoder *encoder) |
59 | | -{ |
60 | | - drm_encoder_cleanup(encoder); |
61 | | - kfree(encoder); |
62 | | -} |
63 | | - |
64 | | -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { |
65 | | - .atomic_check = tidss_encoder_atomic_check, |
66 | | -}; |
67 | | - |
68 | | -static const struct drm_encoder_funcs encoder_funcs = { |
69 | | - .destroy = tidss_encoder_destroy, |
| 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, |
70 | 81 | }; |
71 | 82 |
|
72 | | -struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss, |
73 | | - u32 encoder_type, u32 possible_crtcs) |
| 83 | +int tidss_encoder_create(struct tidss_device *tidss, |
| 84 | + struct drm_bridge *next_bridge, |
| 85 | + u32 encoder_type, u32 possible_crtcs) |
74 | 86 | { |
| 87 | + struct tidss_encoder *t_enc; |
75 | 88 | struct drm_encoder *enc; |
| 89 | + struct drm_connector *connector; |
76 | 90 | int ret; |
77 | 91 |
|
78 | | - enc = kzalloc(sizeof(*enc), GFP_KERNEL); |
79 | | - if (!enc) |
80 | | - return ERR_PTR(-ENOMEM); |
| 92 | + t_enc = drmm_simple_encoder_alloc(&tidss->ddev, struct tidss_encoder, |
| 93 | + encoder, encoder_type); |
| 94 | + if (IS_ERR(t_enc)) |
| 95 | + return PTR_ERR(t_enc); |
| 96 | + |
| 97 | + t_enc->tidss = tidss; |
| 98 | + t_enc->next_bridge = next_bridge; |
| 99 | + t_enc->bridge.funcs = &tidss_bridge_funcs; |
81 | 100 |
|
| 101 | + enc = &t_enc->encoder; |
82 | 102 | enc->possible_crtcs = possible_crtcs; |
83 | 103 |
|
84 | | - ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs, |
85 | | - encoder_type, NULL); |
86 | | - if (ret < 0) { |
87 | | - kfree(enc); |
88 | | - return ERR_PTR(ret); |
| 104 | + /* Attaching first bridge to the encoder */ |
| 105 | + ret = drm_bridge_attach(enc, &t_enc->bridge, NULL, |
| 106 | + 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(&tidss->ddev, enc); |
| 114 | + if (IS_ERR(connector)) { |
| 115 | + dev_err(tidss->dev, "bridge_connector create failed\n"); |
| 116 | + return PTR_ERR(connector); |
| 117 | + } |
| 118 | + |
| 119 | + ret = drm_connector_attach_encoder(connector, enc); |
| 120 | + if (ret) { |
| 121 | + dev_err(tidss->dev, "attaching encoder to connector failed\n"); |
| 122 | + return ret; |
89 | 123 | } |
90 | 124 |
|
91 | | - drm_encoder_helper_add(enc, &encoder_helper_funcs); |
| 125 | + t_enc->connector = connector; |
92 | 126 |
|
93 | 127 | dev_dbg(tidss->dev, "Encoder create done\n"); |
94 | 128 |
|
95 | | - return enc; |
| 129 | + return ret; |
96 | 130 | } |
0 commit comments