2 * Copyright (C) 2015 Texas Instruments
3 * Author: Jyri Sarha <jsarha@ti.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
11 #include <linux/component.h>
12 #include <linux/of_graph.h>
14 #include "tilcdc_drv.h"
15 #include "tilcdc_external.h"
17 static const struct tilcdc_panel_info panel_info_tda998x
= {
30 static int tilcdc_external_mode_valid(struct drm_connector
*connector
,
31 struct drm_display_mode
*mode
)
33 struct tilcdc_drm_private
*priv
= connector
->dev
->dev_private
;
36 ret
= tilcdc_crtc_mode_valid(priv
->crtc
, mode
);
40 for (i
= 0; i
< priv
->num_connectors
&&
41 priv
->connectors
[i
] != connector
; i
++)
44 BUG_ON(priv
->connectors
[i
] != connector
);
45 BUG_ON(!priv
->connector_funcs
[i
]);
47 /* If the connector has its own mode_valid call it. */
48 if (!IS_ERR(priv
->connector_funcs
[i
]) &&
49 priv
->connector_funcs
[i
]->mode_valid
)
50 return priv
->connector_funcs
[i
]->mode_valid(connector
, mode
);
55 static int tilcdc_add_external_encoder(struct drm_device
*dev
, int *bpp
,
56 struct drm_connector
*connector
)
58 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
59 struct drm_connector_helper_funcs
*connector_funcs
;
61 priv
->connectors
[priv
->num_connectors
] = connector
;
62 priv
->encoders
[priv
->num_encoders
++] = connector
->encoder
;
64 /* Only tda998x is supported at the moment. */
65 tilcdc_crtc_set_simulate_vesa_sync(priv
->crtc
, true);
66 tilcdc_crtc_set_panel_info(priv
->crtc
, &panel_info_tda998x
);
67 *bpp
= panel_info_tda998x
.bpp
;
69 connector_funcs
= devm_kzalloc(dev
->dev
, sizeof(*connector_funcs
),
74 /* connector->helper_private contains always struct
75 * connector_helper_funcs pointer. For tilcdc crtc to have a
76 * say if a specific mode is Ok, we need to install our own
77 * helper functions. In our helper functions we copy
78 * everything else but use our own mode_valid() (above).
80 if (connector
->helper_private
) {
81 priv
->connector_funcs
[priv
->num_connectors
] =
82 connector
->helper_private
;
83 *connector_funcs
= *priv
->connector_funcs
[priv
->num_connectors
];
85 priv
->connector_funcs
[priv
->num_connectors
] = ERR_PTR(-ENOENT
);
87 connector_funcs
->mode_valid
= tilcdc_external_mode_valid
;
88 drm_connector_helper_add(connector
, connector_funcs
);
89 priv
->num_connectors
++;
91 dev_dbg(dev
->dev
, "External encoder '%s' connected\n",
92 connector
->encoder
->name
);
97 int tilcdc_add_external_encoders(struct drm_device
*dev
, int *bpp
)
99 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
100 struct drm_connector
*connector
;
101 int num_internal_connectors
= priv
->num_connectors
;
103 list_for_each_entry(connector
, &dev
->mode_config
.connector_list
, head
) {
107 for (i
= 0; i
< num_internal_connectors
; i
++)
108 if (connector
== priv
->connectors
[i
])
111 ret
= tilcdc_add_external_encoder(dev
, bpp
, connector
);
119 void tilcdc_remove_external_encoders(struct drm_device
*dev
)
121 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
124 /* Restore the original helper functions, if any. */
125 for (i
= 0; i
< priv
->num_connectors
; i
++)
126 if (IS_ERR(priv
->connector_funcs
[i
]))
127 drm_connector_helper_add(priv
->connectors
[i
], NULL
);
128 else if (priv
->connector_funcs
[i
])
129 drm_connector_helper_add(priv
->connectors
[i
],
130 priv
->connector_funcs
[i
]);
133 static int dev_match_of(struct device
*dev
, void *data
)
135 return dev
->of_node
== data
;
138 int tilcdc_get_external_components(struct device
*dev
,
139 struct component_match
**match
)
141 struct device_node
*ep
= NULL
;
144 while ((ep
= of_graph_get_next_endpoint(dev
->of_node
, ep
))) {
145 struct device_node
*node
;
147 node
= of_graph_get_remote_port_parent(ep
);
148 if (!node
&& !of_device_is_available(node
)) {
153 dev_dbg(dev
, "Subdevice node '%s' found\n", node
->name
);
155 component_match_add(dev
, match
, dev_match_of
, node
);
161 dev_err(dev
, "Only one external encoder is supported\n");