|
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