1 /* $NetBSD: anxedp.c,v 1.10 2026/04/19 10:54:32 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) 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.10 2026/04/19 10:54:32 jmcneill 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_atomic_state_helper.h> 51 #include <drm/drm_connector.h> 52 #include <drm/drm_crtc.h> 53 #include <drm/drm_crtc_helper.h> 54 #include <drm/drm_drv.h> 55 #include <drm/drm_edid.h> 56 #include <drm/drm_probe_helper.h> 57 58 #define ANX_DP_AUX_CH_CTL_1 0xe5 59 #define ANX_AUX_LENGTH __BITS(7,4) 60 #define ANX_AUX_TX_COMM __BITS(3,0) 61 #define ANX_AUX_TX_COMM_MOT 4 62 #define ANX_AUX_TX_COMM_READ 1 63 #define ANX_DP_AUX_ADDR(n) (0xe6 + (n)) 64 #define ANX_DP_AUX_CH_CTL_2 0xe9 65 #define ANX_ADDR_ONLY __BIT(1) 66 #define ANX_AUX_EN __BIT(0) 67 #define ANX_BUF_DATA(n) (0xf0 + (n)) 68 69 #define ANX_DP_INT_STA 0xf7 70 #define ANX_RPLY_RECEIV __BIT(1) 71 72 static const struct device_compatible_entry compat_data[] = { 73 { .compat = "analogix,anx6345" }, 74 DEVICE_COMPAT_EOL 75 }; 76 77 struct anxedp_softc; 78 79 struct anxedp_connector { 80 struct drm_connector base; 81 struct anxedp_softc *sc; 82 struct drm_encoder *encoder; 83 }; 84 85 struct anxedp_softc { 86 device_t sc_dev; 87 i2c_tag_t sc_i2c; 88 i2c_addr_t sc_addr; 89 int sc_phandle; 90 91 struct anxedp_connector sc_connector; 92 struct drm_bridge sc_bridge; 93 94 struct fdt_device_ports sc_ports; 95 struct drm_display_mode sc_curmode; 96 }; 97 98 #define to_anxedp_connector(x) container_of(x, struct anxedp_connector, base) 99 100 static uint8_t 101 anxedp_read(struct anxedp_softc *sc, u_int off, uint8_t reg) 102 { 103 uint8_t val; 104 105 if (iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr + off, reg, &val, 0) != 0) 106 val = 0xff; 107 108 return val; 109 } 110 111 static void 112 anxedp_write(struct anxedp_softc *sc, u_int off, uint8_t reg, uint8_t val) 113 { 114 (void)iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr + off, reg, val, 0); 115 } 116 117 static enum drm_connector_status 118 anxedp_connector_detect(struct drm_connector *connector, bool force) 119 { 120 return connector_status_connected; 121 } 122 123 static void 124 anxedp_connector_destroy(struct drm_connector *connector) 125 { 126 drm_connector_unregister(connector); 127 drm_connector_cleanup(connector); 128 } 129 130 static const struct drm_connector_funcs anxedp_connector_funcs = { 131 .detect = anxedp_connector_detect, 132 .fill_modes = drm_helper_probe_single_connector_modes, 133 .destroy = anxedp_connector_destroy, 134 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 135 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 136 .reset = drm_atomic_helper_connector_reset, 137 }; 138 139 static int 140 anxedp_aux_wait(struct anxedp_softc *sc) 141 { 142 uint8_t val; 143 int retry; 144 145 for (retry = 1000; retry > 0; retry--) { 146 val = anxedp_read(sc, 0, ANX_DP_AUX_CH_CTL_2); 147 if ((val & ANX_AUX_EN) == 0) 148 break; 149 delay(100); 150 } 151 if (retry == 0) { 152 device_printf(sc->sc_dev, "aux transfer timeout\n"); 153 return ETIMEDOUT; 154 } 155 156 for (retry = 1000; retry > 0; retry--) { 157 val = anxedp_read(sc, 1, ANX_DP_INT_STA); 158 if ((val & ANX_RPLY_RECEIV) != 0) { 159 anxedp_write(sc, 1, ANX_DP_INT_STA, val); 160 break; 161 } 162 delay(100); 163 } 164 if (retry == 0) { 165 device_printf(sc->sc_dev, "aux transfer timeout\n"); 166 return ETIMEDOUT; 167 } 168 169 return 0; 170 } 171 172 static int 173 anxedp_aux_transfer(struct anxedp_softc *sc, uint8_t comm, uint32_t addr, 174 uint8_t *buf, int buflen) 175 { 176 uint8_t ctrl[2]; 177 int n, error; 178 179 ctrl[0] = __SHIFTIN(comm, ANX_AUX_TX_COMM); 180 if (buflen > 0) 181 ctrl[0] |= __SHIFTIN(buflen - 1, ANX_AUX_LENGTH); 182 ctrl[1] = ANX_AUX_EN; 183 if (buflen == 0) 184 ctrl[1] |= ANX_ADDR_ONLY; 185 186 if (comm != ANX_AUX_TX_COMM_READ) { 187 for (n = 0; n < buflen; n++) 188 anxedp_write(sc, 0, ANX_BUF_DATA(n), buf[n]); 189 } 190 191 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(0), addr & 0xff); 192 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(1), (addr >> 8) & 0xff); 193 anxedp_write(sc, 0, ANX_DP_AUX_ADDR(2), (addr >> 16) & 0xf); 194 anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_1, ctrl[0]); 195 anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_2, ctrl[1]); 196 197 error = anxedp_aux_wait(sc); 198 if (error != 0) 199 return error; 200 201 if (comm == ANX_AUX_TX_COMM_READ) { 202 for (n = 0; n < buflen; n++) 203 buf[n] = anxedp_read(sc, 0, ANX_BUF_DATA(n)); 204 } 205 206 return 0; 207 } 208 209 static int 210 anxedp_read_edid(struct anxedp_softc *sc, uint8_t *edid, int edidlen) 211 { 212 int error; 213 uint8_t n; 214 215 for (n = 0; n < edidlen; n += 16) { 216 const int xferlen = MIN(edidlen - n, 16); 217 218 error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_MOT, DDC_ADDR, &n, 1); 219 if (error != 0) 220 return error; 221 222 error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_READ, DDC_ADDR, &edid[n], xferlen); 223 if (error != 0) 224 return error; 225 } 226 227 return 0; 228 } 229 230 static int 231 anxedp_connector_get_modes(struct drm_connector *connector) 232 { 233 struct anxedp_connector *anxedp_connector = to_anxedp_connector(connector); 234 struct anxedp_softc * const sc = anxedp_connector->sc; 235 char edid[EDID_LENGTH]; 236 struct edid *pedid = NULL; 237 int error; 238 239 iic_acquire_bus(sc->sc_i2c, 0); 240 error = anxedp_read_edid(sc, edid, sizeof(edid)); 241 iic_release_bus(sc->sc_i2c, 0); 242 if (error == 0) 243 pedid = (struct edid *)edid; 244 245 drm_connector_update_edid_property(connector, pedid); 246 if (pedid == NULL) 247 return 0; 248 249 return drm_add_edid_modes(connector, pedid); 250 } 251 252 static struct drm_encoder * 253 anxedp_connector_best_encoder(struct drm_connector *connector) 254 { 255 struct anxedp_connector *anxedp_connector = to_anxedp_connector(connector); 256 257 return anxedp_connector->encoder; 258 } 259 260 static const struct drm_connector_helper_funcs anxedp_connector_helper_funcs = { 261 .get_modes = anxedp_connector_get_modes, 262 .best_encoder = anxedp_connector_best_encoder, 263 }; 264 265 static int 266 anxedp_bridge_attach(struct drm_bridge *bridge) 267 { 268 struct anxedp_softc * const sc = bridge->driver_private; 269 struct anxedp_connector *anxedp_connector = &sc->sc_connector; 270 struct drm_connector *connector = &anxedp_connector->base; 271 int error; 272 273 anxedp_connector->sc = sc; 274 275 connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 276 connector->interlace_allowed = 0; 277 connector->doublescan_allowed = 0; 278 279 drm_connector_init(bridge->dev, connector, &anxedp_connector_funcs, 280 connector->connector_type); 281 drm_connector_helper_add(connector, &anxedp_connector_helper_funcs); 282 283 error = drm_connector_attach_encoder(connector, bridge->encoder); 284 if (error != 0) 285 return error; 286 287 return drm_connector_register(connector); 288 } 289 290 static void 291 anxedp_bridge_enable(struct drm_bridge *bridge) 292 { 293 } 294 295 static void 296 anxedp_bridge_pre_enable(struct drm_bridge *bridge) 297 { 298 } 299 300 static void 301 anxedp_bridge_disable(struct drm_bridge *bridge) 302 { 303 } 304 305 static void 306 anxedp_bridge_post_disable(struct drm_bridge *bridge) 307 { 308 } 309 310 static void 311 anxedp_bridge_mode_set(struct drm_bridge *bridge, 312 const struct drm_display_mode *mode, 313 const struct drm_display_mode *adjusted_mode) 314 { 315 struct anxedp_softc * const sc = bridge->driver_private; 316 317 sc->sc_curmode = *adjusted_mode; 318 } 319 320 static bool 321 anxedp_bridge_mode_fixup(struct drm_bridge *bridge, 322 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 323 { 324 return true; 325 } 326 327 static const struct drm_bridge_funcs anxedp_bridge_funcs = { 328 .attach = anxedp_bridge_attach, 329 .enable = anxedp_bridge_enable, 330 .pre_enable = anxedp_bridge_pre_enable, 331 .disable = anxedp_bridge_disable, 332 .post_disable = anxedp_bridge_post_disable, 333 .mode_set = anxedp_bridge_mode_set, 334 .mode_fixup = anxedp_bridge_mode_fixup, 335 }; 336 337 static int 338 anxedp_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) 339 { 340 struct anxedp_softc * const sc = device_private(dev); 341 struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep); 342 struct drm_encoder *encoder; 343 struct drm_bridge *bridge; 344 int error; 345 346 if (!activate) 347 return EINVAL; 348 349 if (fdt_endpoint_port_index(ep) != 0) 350 return EINVAL; 351 352 switch (fdt_endpoint_type(in_ep)) { 353 case EP_DRM_ENCODER: 354 encoder = fdt_endpoint_get_data(in_ep); 355 break; 356 case EP_DRM_BRIDGE: 357 bridge = fdt_endpoint_get_data(in_ep); 358 encoder = bridge->encoder; 359 break; 360 default: 361 encoder = NULL; 362 break; 363 } 364 365 if (encoder == NULL) 366 return EINVAL; 367 368 sc->sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP; 369 sc->sc_connector.encoder = encoder; 370 371 sc->sc_bridge.driver_private = sc; 372 sc->sc_bridge.funcs = &anxedp_bridge_funcs; 373 374 error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL); 375 if (error != 0) 376 return EIO; 377 378 return 0; 379 } 380 381 static void * 382 anxedp_ep_get_data(device_t dev, struct fdt_endpoint *ep) 383 { 384 struct anxedp_softc * const sc = device_private(dev); 385 386 return &sc->sc_bridge; 387 } 388 389 static int 390 anxedp_match(device_t parent, cfdata_t match, void *aux) 391 { 392 struct i2c_attach_args *ia = aux; 393 int match_result; 394 395 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 396 return match_result; 397 398 /* This device is direct-config only */ 399 400 return 0; 401 } 402 403 static void 404 anxedp_attach(device_t parent, device_t self, void *aux) 405 { 406 struct anxedp_softc * const sc = device_private(self); 407 struct i2c_attach_args * const ia = aux; 408 409 sc->sc_dev = self; 410 sc->sc_i2c = ia->ia_tag; 411 sc->sc_addr = ia->ia_addr; 412 sc->sc_phandle = devhandle_to_of(device_handle(self)); 413 414 aprint_naive("\n"); 415 aprint_normal(": eDP TX\n"); 416 417 sc->sc_ports.dp_ep_activate = anxedp_ep_activate; 418 sc->sc_ports.dp_ep_get_data = anxedp_ep_get_data; 419 fdt_ports_register(&sc->sc_ports, self, sc->sc_phandle, EP_DRM_BRIDGE); 420 } 421 422 CFATTACH_DECL_NEW(anxedp, sizeof(struct anxedp_softc), 423 anxedp_match, anxedp_attach, NULL, NULL); 424