1 1.1 riastrad /* $NetBSD: dvo_ch7xxx.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /************************************************************************** 4 1.1 riastrad 5 1.1 riastrad Copyright 2006 Dave Airlie 6 1.1 riastrad 7 1.1 riastrad All Rights Reserved. 8 1.1 riastrad 9 1.1 riastrad Permission is hereby granted, free of charge, to any person obtaining a 10 1.1 riastrad copy of this software and associated documentation files (the 11 1.1 riastrad "Software"), to deal in the Software without restriction, including 12 1.1 riastrad without limitation the rights to use, copy, modify, merge, publish, 13 1.1 riastrad distribute, sub license, and/or sell copies of the Software, and to 14 1.1 riastrad permit persons to whom the Software is furnished to do so, subject to 15 1.1 riastrad the following conditions: 16 1.1 riastrad 17 1.1 riastrad The above copyright notice and this permission notice (including the 18 1.1 riastrad next paragraph) shall be included in all copies or substantial portions 19 1.1 riastrad of the Software. 20 1.1 riastrad 21 1.1 riastrad THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 1.1 riastrad OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 1.1 riastrad MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 24 1.1 riastrad IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 25 1.1 riastrad ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 1.1 riastrad TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 1.1 riastrad SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 1.1 riastrad 29 1.1 riastrad **************************************************************************/ 30 1.1 riastrad 31 1.1 riastrad #include <sys/cdefs.h> 32 1.1 riastrad __KERNEL_RCSID(0, "$NetBSD: dvo_ch7xxx.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $"); 33 1.1 riastrad 34 1.1 riastrad #include "intel_display_types.h" 35 1.1 riastrad #include "intel_dvo_dev.h" 36 1.1 riastrad 37 1.1 riastrad #define CH7xxx_REG_VID 0x4a 38 1.1 riastrad #define CH7xxx_REG_DID 0x4b 39 1.1 riastrad 40 1.1 riastrad #define CH7011_VID 0x83 /* 7010 as well */ 41 1.1 riastrad #define CH7010B_VID 0x05 42 1.1 riastrad #define CH7009A_VID 0x84 43 1.1 riastrad #define CH7009B_VID 0x85 44 1.1 riastrad #define CH7301_VID 0x95 45 1.1 riastrad 46 1.1 riastrad #define CH7xxx_VID 0x84 47 1.1 riastrad #define CH7xxx_DID 0x17 48 1.1 riastrad #define CH7010_DID 0x16 49 1.1 riastrad 50 1.1 riastrad #define CH7xxx_NUM_REGS 0x4c 51 1.1 riastrad 52 1.1 riastrad #define CH7xxx_CM 0x1c 53 1.1 riastrad #define CH7xxx_CM_XCM (1<<0) 54 1.1 riastrad #define CH7xxx_CM_MCP (1<<2) 55 1.1 riastrad #define CH7xxx_INPUT_CLOCK 0x1d 56 1.1 riastrad #define CH7xxx_GPIO 0x1e 57 1.1 riastrad #define CH7xxx_GPIO_HPIR (1<<3) 58 1.1 riastrad #define CH7xxx_IDF 0x1f 59 1.1 riastrad 60 1.1 riastrad #define CH7xxx_IDF_HSP (1<<3) 61 1.1 riastrad #define CH7xxx_IDF_VSP (1<<4) 62 1.1 riastrad 63 1.1 riastrad #define CH7xxx_CONNECTION_DETECT 0x20 64 1.1 riastrad #define CH7xxx_CDET_DVI (1<<5) 65 1.1 riastrad 66 1.1 riastrad #define CH7301_DAC_CNTL 0x21 67 1.1 riastrad #define CH7301_HOTPLUG 0x23 68 1.1 riastrad #define CH7xxx_TCTL 0x31 69 1.1 riastrad #define CH7xxx_TVCO 0x32 70 1.1 riastrad #define CH7xxx_TPCP 0x33 71 1.1 riastrad #define CH7xxx_TPD 0x34 72 1.1 riastrad #define CH7xxx_TPVT 0x35 73 1.1 riastrad #define CH7xxx_TLPF 0x36 74 1.1 riastrad #define CH7xxx_TCT 0x37 75 1.1 riastrad #define CH7301_TEST_PATTERN 0x48 76 1.1 riastrad 77 1.1 riastrad #define CH7xxx_PM 0x49 78 1.1 riastrad #define CH7xxx_PM_FPD (1<<0) 79 1.1 riastrad #define CH7301_PM_DACPD0 (1<<1) 80 1.1 riastrad #define CH7301_PM_DACPD1 (1<<2) 81 1.1 riastrad #define CH7301_PM_DACPD2 (1<<3) 82 1.1 riastrad #define CH7xxx_PM_DVIL (1<<6) 83 1.1 riastrad #define CH7xxx_PM_DVIP (1<<7) 84 1.1 riastrad 85 1.1 riastrad #define CH7301_SYNC_POLARITY 0x56 86 1.1 riastrad #define CH7301_SYNC_RGB_YUV (1<<0) 87 1.1 riastrad #define CH7301_SYNC_POL_DVI (1<<5) 88 1.1 riastrad 89 1.1 riastrad /** @file 90 1.1 riastrad * driver for the Chrontel 7xxx DVI chip over DVO. 91 1.1 riastrad */ 92 1.1 riastrad 93 1.1 riastrad static struct ch7xxx_id_struct { 94 1.1 riastrad u8 vid; 95 1.2 riastrad const char *name; 96 1.1 riastrad } ch7xxx_ids[] = { 97 1.1 riastrad { CH7011_VID, "CH7011" }, 98 1.1 riastrad { CH7010B_VID, "CH7010B" }, 99 1.1 riastrad { CH7009A_VID, "CH7009A" }, 100 1.1 riastrad { CH7009B_VID, "CH7009B" }, 101 1.1 riastrad { CH7301_VID, "CH7301" }, 102 1.1 riastrad }; 103 1.1 riastrad 104 1.1 riastrad static struct ch7xxx_did_struct { 105 1.1 riastrad u8 did; 106 1.2 riastrad const char *name; 107 1.1 riastrad } ch7xxx_dids[] = { 108 1.1 riastrad { CH7xxx_DID, "CH7XXX" }, 109 1.1 riastrad { CH7010_DID, "CH7010B" }, 110 1.1 riastrad }; 111 1.1 riastrad 112 1.1 riastrad struct ch7xxx_priv { 113 1.1 riastrad bool quiet; 114 1.1 riastrad }; 115 1.1 riastrad 116 1.2 riastrad static const char *ch7xxx_get_id(u8 vid) 117 1.1 riastrad { 118 1.1 riastrad int i; 119 1.1 riastrad 120 1.1 riastrad for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) { 121 1.1 riastrad if (ch7xxx_ids[i].vid == vid) 122 1.1 riastrad return ch7xxx_ids[i].name; 123 1.1 riastrad } 124 1.1 riastrad 125 1.1 riastrad return NULL; 126 1.1 riastrad } 127 1.1 riastrad 128 1.2 riastrad static const char *ch7xxx_get_did(u8 did) 129 1.1 riastrad { 130 1.1 riastrad int i; 131 1.1 riastrad 132 1.1 riastrad for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) { 133 1.1 riastrad if (ch7xxx_dids[i].did == did) 134 1.1 riastrad return ch7xxx_dids[i].name; 135 1.1 riastrad } 136 1.1 riastrad 137 1.1 riastrad return NULL; 138 1.1 riastrad } 139 1.1 riastrad 140 1.1 riastrad /** Reads an 8 bit register */ 141 1.1 riastrad static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, u8 *ch) 142 1.1 riastrad { 143 1.1 riastrad struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 144 1.1 riastrad struct i2c_adapter *adapter = dvo->i2c_bus; 145 1.1 riastrad u8 out_buf[2]; 146 1.1 riastrad u8 in_buf[2]; 147 1.1 riastrad 148 1.1 riastrad struct i2c_msg msgs[] = { 149 1.1 riastrad { 150 1.1 riastrad .addr = dvo->slave_addr, 151 1.1 riastrad .flags = 0, 152 1.1 riastrad .len = 1, 153 1.1 riastrad .buf = out_buf, 154 1.1 riastrad }, 155 1.1 riastrad { 156 1.1 riastrad .addr = dvo->slave_addr, 157 1.1 riastrad .flags = I2C_M_RD, 158 1.1 riastrad .len = 1, 159 1.1 riastrad .buf = in_buf, 160 1.1 riastrad } 161 1.1 riastrad }; 162 1.1 riastrad 163 1.1 riastrad out_buf[0] = addr; 164 1.1 riastrad out_buf[1] = 0; 165 1.1 riastrad 166 1.1 riastrad if (i2c_transfer(adapter, msgs, 2) == 2) { 167 1.1 riastrad *ch = in_buf[0]; 168 1.1 riastrad return true; 169 1.1 riastrad } 170 1.1 riastrad 171 1.1 riastrad if (!ch7xxx->quiet) { 172 1.1 riastrad DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", 173 1.1 riastrad addr, adapter->name, dvo->slave_addr); 174 1.1 riastrad } 175 1.1 riastrad return false; 176 1.1 riastrad } 177 1.1 riastrad 178 1.1 riastrad /** Writes an 8 bit register */ 179 1.1 riastrad static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, u8 ch) 180 1.1 riastrad { 181 1.1 riastrad struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 182 1.1 riastrad struct i2c_adapter *adapter = dvo->i2c_bus; 183 1.1 riastrad u8 out_buf[2]; 184 1.1 riastrad struct i2c_msg msg = { 185 1.1 riastrad .addr = dvo->slave_addr, 186 1.1 riastrad .flags = 0, 187 1.1 riastrad .len = 2, 188 1.1 riastrad .buf = out_buf, 189 1.1 riastrad }; 190 1.1 riastrad 191 1.1 riastrad out_buf[0] = addr; 192 1.1 riastrad out_buf[1] = ch; 193 1.1 riastrad 194 1.1 riastrad if (i2c_transfer(adapter, &msg, 1) == 1) 195 1.1 riastrad return true; 196 1.1 riastrad 197 1.1 riastrad if (!ch7xxx->quiet) { 198 1.1 riastrad DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", 199 1.1 riastrad addr, adapter->name, dvo->slave_addr); 200 1.1 riastrad } 201 1.1 riastrad 202 1.1 riastrad return false; 203 1.1 riastrad } 204 1.1 riastrad 205 1.1 riastrad static bool ch7xxx_init(struct intel_dvo_device *dvo, 206 1.1 riastrad struct i2c_adapter *adapter) 207 1.1 riastrad { 208 1.1 riastrad /* this will detect the CH7xxx chip on the specified i2c bus */ 209 1.1 riastrad struct ch7xxx_priv *ch7xxx; 210 1.1 riastrad u8 vendor, device; 211 1.2 riastrad const char *name, *devid; 212 1.1 riastrad 213 1.1 riastrad ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); 214 1.1 riastrad if (ch7xxx == NULL) 215 1.1 riastrad return false; 216 1.1 riastrad 217 1.1 riastrad dvo->i2c_bus = adapter; 218 1.1 riastrad dvo->dev_priv = ch7xxx; 219 1.1 riastrad ch7xxx->quiet = true; 220 1.1 riastrad 221 1.1 riastrad if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor)) 222 1.1 riastrad goto out; 223 1.1 riastrad 224 1.1 riastrad name = ch7xxx_get_id(vendor); 225 1.1 riastrad if (!name) { 226 1.1 riastrad DRM_DEBUG_KMS("ch7xxx not detected; got VID 0x%02x from %s slave %d.\n", 227 1.1 riastrad vendor, adapter->name, dvo->slave_addr); 228 1.1 riastrad goto out; 229 1.1 riastrad } 230 1.1 riastrad 231 1.1 riastrad 232 1.1 riastrad if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) 233 1.1 riastrad goto out; 234 1.1 riastrad 235 1.1 riastrad devid = ch7xxx_get_did(device); 236 1.1 riastrad if (!devid) { 237 1.1 riastrad DRM_DEBUG_KMS("ch7xxx not detected; got DID 0x%02x from %s slave %d.\n", 238 1.1 riastrad device, adapter->name, dvo->slave_addr); 239 1.1 riastrad goto out; 240 1.1 riastrad } 241 1.1 riastrad 242 1.1 riastrad ch7xxx->quiet = false; 243 1.1 riastrad DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", 244 1.1 riastrad name, vendor, device); 245 1.1 riastrad return true; 246 1.1 riastrad out: 247 1.1 riastrad kfree(ch7xxx); 248 1.1 riastrad return false; 249 1.1 riastrad } 250 1.1 riastrad 251 1.1 riastrad static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo) 252 1.1 riastrad { 253 1.1 riastrad u8 cdet, orig_pm, pm; 254 1.1 riastrad 255 1.1 riastrad ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm); 256 1.1 riastrad 257 1.1 riastrad pm = orig_pm; 258 1.1 riastrad pm &= ~CH7xxx_PM_FPD; 259 1.1 riastrad pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; 260 1.1 riastrad 261 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_PM, pm); 262 1.1 riastrad 263 1.1 riastrad ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet); 264 1.1 riastrad 265 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm); 266 1.1 riastrad 267 1.1 riastrad if (cdet & CH7xxx_CDET_DVI) 268 1.1 riastrad return connector_status_connected; 269 1.1 riastrad return connector_status_disconnected; 270 1.1 riastrad } 271 1.1 riastrad 272 1.1 riastrad static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo, 273 1.1 riastrad struct drm_display_mode *mode) 274 1.1 riastrad { 275 1.1 riastrad if (mode->clock > 165000) 276 1.1 riastrad return MODE_CLOCK_HIGH; 277 1.1 riastrad 278 1.1 riastrad return MODE_OK; 279 1.1 riastrad } 280 1.1 riastrad 281 1.1 riastrad static void ch7xxx_mode_set(struct intel_dvo_device *dvo, 282 1.1 riastrad const struct drm_display_mode *mode, 283 1.1 riastrad const struct drm_display_mode *adjusted_mode) 284 1.1 riastrad { 285 1.1 riastrad u8 tvco, tpcp, tpd, tlpf, idf; 286 1.1 riastrad 287 1.1 riastrad if (mode->clock <= 65000) { 288 1.1 riastrad tvco = 0x23; 289 1.1 riastrad tpcp = 0x08; 290 1.1 riastrad tpd = 0x16; 291 1.1 riastrad tlpf = 0x60; 292 1.1 riastrad } else { 293 1.1 riastrad tvco = 0x2d; 294 1.1 riastrad tpcp = 0x06; 295 1.1 riastrad tpd = 0x26; 296 1.1 riastrad tlpf = 0xa0; 297 1.1 riastrad } 298 1.1 riastrad 299 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00); 300 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco); 301 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp); 302 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TPD, tpd); 303 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30); 304 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf); 305 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00); 306 1.1 riastrad 307 1.1 riastrad ch7xxx_readb(dvo, CH7xxx_IDF, &idf); 308 1.1 riastrad 309 1.1 riastrad idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); 310 1.1 riastrad if (mode->flags & DRM_MODE_FLAG_PHSYNC) 311 1.1 riastrad idf |= CH7xxx_IDF_HSP; 312 1.1 riastrad 313 1.1 riastrad if (mode->flags & DRM_MODE_FLAG_PVSYNC) 314 1.1 riastrad idf |= CH7xxx_IDF_VSP; 315 1.1 riastrad 316 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_IDF, idf); 317 1.1 riastrad } 318 1.1 riastrad 319 1.1 riastrad /* set the CH7xxx power state */ 320 1.1 riastrad static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) 321 1.1 riastrad { 322 1.1 riastrad if (enable) 323 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); 324 1.1 riastrad else 325 1.1 riastrad ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); 326 1.1 riastrad } 327 1.1 riastrad 328 1.1 riastrad static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo) 329 1.1 riastrad { 330 1.1 riastrad u8 val; 331 1.1 riastrad 332 1.1 riastrad ch7xxx_readb(dvo, CH7xxx_PM, &val); 333 1.1 riastrad 334 1.1 riastrad if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP)) 335 1.1 riastrad return true; 336 1.1 riastrad else 337 1.1 riastrad return false; 338 1.1 riastrad } 339 1.1 riastrad 340 1.1 riastrad static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) 341 1.1 riastrad { 342 1.1 riastrad int i; 343 1.1 riastrad 344 1.1 riastrad for (i = 0; i < CH7xxx_NUM_REGS; i++) { 345 1.1 riastrad u8 val; 346 1.1 riastrad if ((i % 8) == 0) 347 1.1 riastrad DRM_DEBUG_KMS("\n %02X: ", i); 348 1.1 riastrad ch7xxx_readb(dvo, i, &val); 349 1.1 riastrad DRM_DEBUG_KMS("%02X ", val); 350 1.1 riastrad } 351 1.1 riastrad } 352 1.1 riastrad 353 1.1 riastrad static void ch7xxx_destroy(struct intel_dvo_device *dvo) 354 1.1 riastrad { 355 1.1 riastrad struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 356 1.1 riastrad 357 1.1 riastrad if (ch7xxx) { 358 1.1 riastrad kfree(ch7xxx); 359 1.1 riastrad dvo->dev_priv = NULL; 360 1.1 riastrad } 361 1.1 riastrad } 362 1.1 riastrad 363 1.1 riastrad const struct intel_dvo_dev_ops ch7xxx_ops = { 364 1.1 riastrad .init = ch7xxx_init, 365 1.1 riastrad .detect = ch7xxx_detect, 366 1.1 riastrad .mode_valid = ch7xxx_mode_valid, 367 1.1 riastrad .mode_set = ch7xxx_mode_set, 368 1.1 riastrad .dpms = ch7xxx_dpms, 369 1.1 riastrad .get_hw_state = ch7xxx_get_hw_state, 370 1.1 riastrad .dump_regs = ch7xxx_dump_regs, 371 1.1 riastrad .destroy = ch7xxx_destroy, 372 1.1 riastrad }; 373