1/* $NetBSD: anxedp.c,v 1.8 2021/12/19 11:01:10 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: anxedp.c,v 1.8 2021/12/19 11:01:10 riastradh Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34#include <sys/conf.h> 35#include <sys/device.h> 36#include <sys/intr.h> 37#include <sys/kernel.h> 38#include <sys/systm.h> 39 40#include <dev/ic/dw_hdmi.h> 41 42#include <dev/i2c/ddcreg.h> 43#include <dev/i2c/ddcvar.h> 44#include <dev/i2c/i2cvar.h> 45#include <dev/videomode/edidvar.h> 46 47#include <dev/fdt/fdt_port.h> 48#include <dev/fdt/fdtvar.h> 49 50#include <drm/drm_connector.h> 51#include <drm/drm_crtc.h> 52#include <drm/drm_crtc_helper.h> 53#include <drm/drm_drv.h> 54#include <drm/drm_edid.h> 55#include <drm/drm_probe_helper.h> 56 57#define ANX_DP_AUX_CH_CTL_1 0xe5 58#define ANX_AUX_LENGTH __BITS(7,4) 59#define ANX_AUX_TX_COMM __BITS(3,0) 60#define ANX_AUX_TX_COMM_MOT 4 61#define ANX_AUX_TX_COMM_READ 1 62#define ANX_DP_AUX_ADDR(n) (0xe6 + (n)) 63#define ANX_DP_AUX_CH_CTL_2 0xe9 64#define ANX_ADDR_ONLY __BIT(1) 65#define ANX_AUX_EN __BIT(0) 66#define ANX_BUF_DATA(n) (0xf0 + (n)) 67 68#define ANX_DP_INT_STA 0xf7 69#define ANX_RPLY_RECEIV __BIT(1) 70 71static const struct device_compatible_entry compat_data[] = { 72 { .compat = "analogix,anx6345" }, 73 DEVICE_COMPAT_EOL 74}; 75 76struct anxedp_softc; 77 78struct anxedp_connector { 79 struct drm_connector base; 80 struct anxedp_softc *sc; 81}; 82 83struct anxedp_softc { 84 device_t sc_dev; 85 i2c_tag_t sc_i2c; 86 i2c_addr_t sc_addr; 87 int sc_phandle; 88 89 struct anxedp_connector sc_connector; 90 struct drm_bridge sc_bridge; 91 92 struct fdt_device_ports sc_ports; 93 struct drm_display_mode sc_curmode; 94}; 95 96#define to_anxedp_connector(x) container_of(x, struct anxedp_connector, base) 97 98static uint8_t 99anxedp_read(struct anxedp_softc *sc, u_int off, uint8_t reg) 100{ 101 uint8_t val; 102 103 if (iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr + off, reg, &val, 0) != 0) 104 val = 0xff; 105 106 return val; 107} 108 109static void 110anxedp_write(struct anxedp_softc *sc, u_int off, uint8_t reg, uint8_t val) 111{ 112 (void)iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr + off, reg, val, 0); 113} 114 115static int 116anxedp_connector_dpms(struct drm_connector *connector, int mode) 117{ 118 int error; 119 120 if (mode != DRM_MODE_DPMS_ON) 121 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 122 123 error = drm_helper_connector_dpms(connector, mode); 124 125 if (mode == DRM_MODE_DPMS_ON) 126 pmf_event_inject(NULL, PMFE_DISPLAY_ON); 127 128 return error; 129} 130 131static enum drm_connector_status 132anxedp_connector_detect(struct drm_connector *connector, bool force) 133{ 134 return connector_status_connected; 135} 136 137static void 138anxedp_connector_destroy(struct drm_connector *connector) 139{ 140 drm_connector_unregister(connector); 141 drm_connector_cleanup(connector); 142} 143 144static const struct drm_connector_funcs anxedp_connector_funcs = { 145 .dpms = anxedp_connector_dpms, 146 .detect = anxedp_connector_detect, 147 .fill_modes = drm_helper_probe_single_connector_modes, 148 .destroy = anxedp_connector_destroy, 149}; 150 151static int 152anxedp_aux_wait(struct anxedp_softc *sc) 153{ 154 uint8_t val; 155 int retry; 156 157 for (retry = 1000; retry > 0; retry--) { 158 val = anxedp_read(sc, 0, ANX_DP_AUX_CH_CTL_2); 159 if ((val & ANX_AUX_EN) == 0) 160 break; 161 delay(100); 162 } 163 if (retry == 0) { 164 device_printf(sc->sc_dev, "aux transfer timeout\n"); 165 return ETIMEDOUT; 166 } 167 168 for (retry = 1000; retry > 0; retry--) { 169 val = anxedp_read(sc, 1, ANX_DP_INT_STA); 170 if ((val & ANX_RPLY_RECEIV) != 0) { 171 anxedp_write(sc, 1, ANX_DP_INT_STA, val); 172 break; 173 } 174 delay(100); 175 } 176 if (retry == 0) { 177 device_printf(sc->sc_dev, "aux transfer timeout\n"); 178 return ETIMEDOUT; 179 } 180 181 return 0; 182} 183 184static int 185anxedp_aux_transfer(struct anxedp_softc *sc, uint8_t comm, uint32_t addr, 186 uint8_t *buf, int buflen) 187{ 188 uint8_t ctrl[2]; 189 int n, error; 190 191 ctrl[0] = __SHIFTIN(comm, ANX_AUX_TX_COMM); 192 if (buflen > 0) 193 ctrl[0] |= __SHIFTIN(buflen - 1, ANX_AUX_LENGTH); 194 ctrl[1] = ANX_AUX_EN; 195 if (buflen == 0) 196 ctrl[1] |= ANX_ADDR_ONLY; 197 198 if (comm != ANX_AUX_TX_COMM_READ) { 199 for (n = 0; n < buflen; n++) 200 anxedp_write(sc, 0, ANX_BUF_DATA(n), buf[n]); 201 } 202 203 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(0), addr & 0xff); 204 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(1), (addr >> 8) & 0xff); 205 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(2), (addr >> 16) & 0xf); 206 anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_1, ctrl[0]); 207 anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_2, ctrl[1]); 208 209 error = anxedp_aux_wait(sc); 210 if (error != 0) 211 return error; 212 213 if (comm == ANX_AUX_TX_COMM_READ) { 214 for (n = 0; n < buflen; n++) 215 buf[n] = anxedp_read(sc, 0, ANX_BUF_DATA(n)); 216 } 217 218 return 0; 219} 220 221static int 222anxedp_read_edid(struct anxedp_softc *sc, uint8_t *edid, int edidlen) 223{ 224 int error; 225 uint8_t n; 226 227 for (n = 0; n < edidlen; n += 16) { 228 const int xferlen = MIN(edidlen - n, 16); 229 230 error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_MOT, DDC_ADDR, &n, 1); 231 if (error != 0) 232 return error; 233 234 error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_READ, DDC_ADDR, &edid[n], xferlen); 235 if (error != 0) 236 return error; 237 } 238 239 return 0; 240} 241 242static int 243anxedp_connector_get_modes(struct drm_connector *connector) 244{ 245 struct anxedp_connector *anxedp_connector = to_anxedp_connector(connector); 246 struct anxedp_softc * const sc = anxedp_connector->sc; 247 char edid[EDID_LENGTH]; 248 struct edid *pedid = NULL; 249 int error; 250 251 iic_acquire_bus(sc->sc_i2c, 0); 252 error = anxedp_read_edid(sc, edid, sizeof(edid)); 253 iic_release_bus(sc->sc_i2c, 0); 254 if (error == 0) 255 pedid = (struct edid *)edid; 256 257 drm_connector_update_edid_property(connector, pedid); 258 if (pedid == NULL) 259 return 0; 260 261 return drm_add_edid_modes(connector, pedid); 262} 263 264static const struct drm_connector_helper_funcs anxedp_connector_helper_funcs = { 265 .get_modes = anxedp_connector_get_modes, 266}; 267 268static int 269anxedp_bridge_attach(struct drm_bridge *bridge) 270{ 271 struct anxedp_softc * const sc = bridge->driver_private; 272 struct anxedp_connector *anxedp_connector = &sc->sc_connector; 273 struct drm_connector *connector = &anxedp_connector->base; 274 int error; 275 276 anxedp_connector->sc = sc; 277 278 connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 279 connector->interlace_allowed = 0; 280 connector->doublescan_allowed = 0; 281 282 drm_connector_init(bridge->dev, connector, &anxedp_connector_funcs, 283 connector->connector_type); 284 drm_connector_helper_add(connector, &anxedp_connector_helper_funcs); 285 286 error = drm_connector_attach_encoder(connector, bridge->encoder); 287 if (error != 0) 288 return error; 289 290 return drm_connector_register(connector); 291} 292 293static void 294anxedp_bridge_enable(struct drm_bridge *bridge) 295{ 296} 297 298static void 299anxedp_bridge_pre_enable(struct drm_bridge *bridge) 300{ 301} 302 303static void 304anxedp_bridge_disable(struct drm_bridge *bridge) 305{ 306} 307 308static void 309anxedp_bridge_post_disable(struct drm_bridge *bridge) 310{ 311} 312 313static void 314anxedp_bridge_mode_set(struct drm_bridge *bridge, 315 const struct drm_display_mode *mode, 316 const struct drm_display_mode *adjusted_mode) 317{ 318 struct anxedp_softc * const sc = bridge->driver_private; 319 320 sc->sc_curmode = *adjusted_mode; 321} 322 323static bool 324anxedp_bridge_mode_fixup(struct drm_bridge *bridge, 325 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 326{ 327 return true; 328} 329 330static const struct drm_bridge_funcs anxedp_bridge_funcs = { 331 .attach = anxedp_bridge_attach, 332 .enable = anxedp_bridge_enable, 333 .pre_enable = anxedp_bridge_pre_enable, 334 .disable = anxedp_bridge_disable, 335 .post_disable = anxedp_bridge_post_disable, 336 .mode_set = anxedp_bridge_mode_set, 337 .mode_fixup = anxedp_bridge_mode_fixup, 338}; 339 340static int 341anxedp_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) 342{ 343 struct anxedp_softc * const sc = device_private(dev); 344 struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep); 345 struct drm_encoder *encoder; 346 struct drm_bridge *bridge; 347 int error; 348 349 if (!activate) 350 return EINVAL; 351 352 if (fdt_endpoint_port_index(ep) != 0) 353 return EINVAL; 354 355 switch (fdt_endpoint_type(in_ep)) { 356 case EP_DRM_ENCODER: 357 encoder = fdt_endpoint_get_data(in_ep); 358 break; 359 case EP_DRM_BRIDGE: 360 bridge = fdt_endpoint_get_data(in_ep); 361 encoder = bridge->encoder; 362 break; 363 default: 364 encoder = NULL; 365 break; 366 } 367 368 if (encoder == NULL) 369 return EINVAL; 370 371 sc->sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP; 372 373 sc->sc_bridge.driver_private = sc; 374 sc->sc_bridge.funcs = &anxedp_bridge_funcs; 375 sc->sc_bridge.encoder = encoder; 376 377 error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL); 378 if (error != 0) 379 return EIO; 380 381 return 0; 382} 383 384static void * 385anxedp_ep_get_data(device_t dev, struct fdt_endpoint *ep) 386{ 387 struct anxedp_softc * const sc = device_private(dev); 388 389 return &sc->sc_bridge; 390} 391 392static int 393anxedp_match(device_t parent, cfdata_t match, void *aux) 394{ 395 struct i2c_attach_args *ia = aux; 396 int match_result; 397 398 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 399 return match_result; 400 401 /* This device is direct-config only */ 402 403 return 0; 404} 405 406static void 407anxedp_attach(device_t parent, device_t self, void *aux) 408{ 409 struct anxedp_softc * const sc = device_private(self); 410 struct i2c_attach_args * const ia = aux; 411 412 sc->sc_dev = self; 413 sc->sc_i2c = ia->ia_tag; 414 sc->sc_addr = ia->ia_addr; 415 sc->sc_phandle = ia->ia_cookie; 416 417 aprint_naive("\n"); 418 aprint_normal(": eDP TX\n"); 419 420 sc->sc_ports.dp_ep_activate = anxedp_ep_activate; 421 sc->sc_ports.dp_ep_get_data = anxedp_ep_get_data; 422 fdt_ports_register(&sc->sc_ports, self, sc->sc_phandle, EP_DRM_BRIDGE); 423} 424 425CFATTACH_DECL_NEW(anxedp, sizeof(struct anxedp_softc), 426 anxedp_match, anxedp_attach, NULL, NULL); 427