1/* $NetBSD: dvo_tfp410.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $ */ 2 3/* 4 * Copyright �� 2007 Dave Mueller 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 * IN THE SOFTWARE. 24 * 25 * Authors: 26 * Dave Mueller <dave.mueller@gmx.ch> 27 * 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: dvo_tfp410.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $"); 32 33#include "intel_display_types.h" 34#include "intel_dvo_dev.h" 35 36/* register definitions according to the TFP410 data sheet */ 37#define TFP410_VID 0x014C 38#define TFP410_DID 0x0410 39 40#define TFP410_VID_LO 0x00 41#define TFP410_VID_HI 0x01 42#define TFP410_DID_LO 0x02 43#define TFP410_DID_HI 0x03 44#define TFP410_REV 0x04 45 46#define TFP410_CTL_1 0x08 47#define TFP410_CTL_1_TDIS (1<<6) 48#define TFP410_CTL_1_VEN (1<<5) 49#define TFP410_CTL_1_HEN (1<<4) 50#define TFP410_CTL_1_DSEL (1<<3) 51#define TFP410_CTL_1_BSEL (1<<2) 52#define TFP410_CTL_1_EDGE (1<<1) 53#define TFP410_CTL_1_PD (1<<0) 54 55#define TFP410_CTL_2 0x09 56#define TFP410_CTL_2_VLOW (1<<7) 57#define TFP410_CTL_2_MSEL_MASK (0x7<<4) 58#define TFP410_CTL_2_MSEL (1<<4) 59#define TFP410_CTL_2_TSEL (1<<3) 60#define TFP410_CTL_2_RSEN (1<<2) 61#define TFP410_CTL_2_HTPLG (1<<1) 62#define TFP410_CTL_2_MDI (1<<0) 63 64#define TFP410_CTL_3 0x0A 65#define TFP410_CTL_3_DK_MASK (0x7<<5) 66#define TFP410_CTL_3_DK (1<<5) 67#define TFP410_CTL_3_DKEN (1<<4) 68#define TFP410_CTL_3_CTL_MASK (0x7<<1) 69#define TFP410_CTL_3_CTL (1<<1) 70 71#define TFP410_USERCFG 0x0B 72 73#define TFP410_DE_DLY 0x32 74 75#define TFP410_DE_CTL 0x33 76#define TFP410_DE_CTL_DEGEN (1<<6) 77#define TFP410_DE_CTL_VSPOL (1<<5) 78#define TFP410_DE_CTL_HSPOL (1<<4) 79#define TFP410_DE_CTL_DEDLY8 (1<<0) 80 81#define TFP410_DE_TOP 0x34 82 83#define TFP410_DE_CNT_LO 0x36 84#define TFP410_DE_CNT_HI 0x37 85 86#define TFP410_DE_LIN_LO 0x38 87#define TFP410_DE_LIN_HI 0x39 88 89#define TFP410_H_RES_LO 0x3A 90#define TFP410_H_RES_HI 0x3B 91 92#define TFP410_V_RES_LO 0x3C 93#define TFP410_V_RES_HI 0x3D 94 95struct tfp410_priv { 96 bool quiet; 97}; 98 99static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, u8 *ch) 100{ 101 struct tfp410_priv *tfp = dvo->dev_priv; 102 struct i2c_adapter *adapter = dvo->i2c_bus; 103 u8 out_buf[2]; 104 u8 in_buf[2]; 105 106 struct i2c_msg msgs[] = { 107 { 108 .addr = dvo->slave_addr, 109 .flags = 0, 110 .len = 1, 111 .buf = out_buf, 112 }, 113 { 114 .addr = dvo->slave_addr, 115 .flags = I2C_M_RD, 116 .len = 1, 117 .buf = in_buf, 118 } 119 }; 120 121 out_buf[0] = addr; 122 out_buf[1] = 0; 123 124 if (i2c_transfer(adapter, msgs, 2) == 2) { 125 *ch = in_buf[0]; 126 return true; 127 } 128 129 if (!tfp->quiet) { 130 DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", 131 addr, adapter->name, dvo->slave_addr); 132 } 133 return false; 134} 135 136static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, u8 ch) 137{ 138 struct tfp410_priv *tfp = dvo->dev_priv; 139 struct i2c_adapter *adapter = dvo->i2c_bus; 140 u8 out_buf[2]; 141 struct i2c_msg msg = { 142 .addr = dvo->slave_addr, 143 .flags = 0, 144 .len = 2, 145 .buf = out_buf, 146 }; 147 148 out_buf[0] = addr; 149 out_buf[1] = ch; 150 151 if (i2c_transfer(adapter, &msg, 1) == 1) 152 return true; 153 154 if (!tfp->quiet) { 155 DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", 156 addr, adapter->name, dvo->slave_addr); 157 } 158 159 return false; 160} 161 162static int tfp410_getid(struct intel_dvo_device *dvo, int addr) 163{ 164 u8 ch1, ch2; 165 166 if (tfp410_readb(dvo, addr+0, &ch1) && 167 tfp410_readb(dvo, addr+1, &ch2)) 168 return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF); 169 170 return -1; 171} 172 173/* Ti TFP410 driver for chip on i2c bus */ 174static bool tfp410_init(struct intel_dvo_device *dvo, 175 struct i2c_adapter *adapter) 176{ 177 /* this will detect the tfp410 chip on the specified i2c bus */ 178 struct tfp410_priv *tfp; 179 int id; 180 181 tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL); 182 if (tfp == NULL) 183 return false; 184 185 dvo->i2c_bus = adapter; 186 dvo->dev_priv = tfp; 187 tfp->quiet = true; 188 189 if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) { 190 DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s " 191 "Slave %d.\n", 192 id, adapter->name, dvo->slave_addr); 193 goto out; 194 } 195 196 if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) { 197 DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s " 198 "Slave %d.\n", 199 id, adapter->name, dvo->slave_addr); 200 goto out; 201 } 202 tfp->quiet = false; 203 return true; 204out: 205 kfree(tfp); 206 return false; 207} 208 209static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo) 210{ 211 enum drm_connector_status ret = connector_status_disconnected; 212 u8 ctl2; 213 214 if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) { 215 if (ctl2 & TFP410_CTL_2_RSEN) 216 ret = connector_status_connected; 217 else 218 ret = connector_status_disconnected; 219 } 220 221 return ret; 222} 223 224static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo, 225 struct drm_display_mode *mode) 226{ 227 return MODE_OK; 228} 229 230static void tfp410_mode_set(struct intel_dvo_device *dvo, 231 const struct drm_display_mode *mode, 232 const struct drm_display_mode *adjusted_mode) 233{ 234 /* As long as the basics are set up, since we don't have clock dependencies 235 * in the mode setup, we can just leave the registers alone and everything 236 * will work fine. 237 */ 238 /* don't do much */ 239 return; 240} 241 242/* set the tfp410 power state */ 243static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable) 244{ 245 u8 ctl1; 246 247 if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) 248 return; 249 250 if (enable) 251 ctl1 |= TFP410_CTL_1_PD; 252 else 253 ctl1 &= ~TFP410_CTL_1_PD; 254 255 tfp410_writeb(dvo, TFP410_CTL_1, ctl1); 256} 257 258static bool tfp410_get_hw_state(struct intel_dvo_device *dvo) 259{ 260 u8 ctl1; 261 262 if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) 263 return false; 264 265 if (ctl1 & TFP410_CTL_1_PD) 266 return true; 267 else 268 return false; 269} 270 271static void tfp410_dump_regs(struct intel_dvo_device *dvo) 272{ 273 u8 val, val2; 274 275 tfp410_readb(dvo, TFP410_REV, &val); 276 DRM_DEBUG_KMS("TFP410_REV: 0x%02X\n", val); 277 tfp410_readb(dvo, TFP410_CTL_1, &val); 278 DRM_DEBUG_KMS("TFP410_CTL1: 0x%02X\n", val); 279 tfp410_readb(dvo, TFP410_CTL_2, &val); 280 DRM_DEBUG_KMS("TFP410_CTL2: 0x%02X\n", val); 281 tfp410_readb(dvo, TFP410_CTL_3, &val); 282 DRM_DEBUG_KMS("TFP410_CTL3: 0x%02X\n", val); 283 tfp410_readb(dvo, TFP410_USERCFG, &val); 284 DRM_DEBUG_KMS("TFP410_USERCFG: 0x%02X\n", val); 285 tfp410_readb(dvo, TFP410_DE_DLY, &val); 286 DRM_DEBUG_KMS("TFP410_DE_DLY: 0x%02X\n", val); 287 tfp410_readb(dvo, TFP410_DE_CTL, &val); 288 DRM_DEBUG_KMS("TFP410_DE_CTL: 0x%02X\n", val); 289 tfp410_readb(dvo, TFP410_DE_TOP, &val); 290 DRM_DEBUG_KMS("TFP410_DE_TOP: 0x%02X\n", val); 291 tfp410_readb(dvo, TFP410_DE_CNT_LO, &val); 292 tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2); 293 DRM_DEBUG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val); 294 tfp410_readb(dvo, TFP410_DE_LIN_LO, &val); 295 tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2); 296 DRM_DEBUG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val); 297 tfp410_readb(dvo, TFP410_H_RES_LO, &val); 298 tfp410_readb(dvo, TFP410_H_RES_HI, &val2); 299 DRM_DEBUG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val); 300 tfp410_readb(dvo, TFP410_V_RES_LO, &val); 301 tfp410_readb(dvo, TFP410_V_RES_HI, &val2); 302 DRM_DEBUG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val); 303} 304 305static void tfp410_destroy(struct intel_dvo_device *dvo) 306{ 307 struct tfp410_priv *tfp = dvo->dev_priv; 308 309 if (tfp) { 310 kfree(tfp); 311 dvo->dev_priv = NULL; 312 } 313} 314 315const struct intel_dvo_dev_ops tfp410_ops = { 316 .init = tfp410_init, 317 .detect = tfp410_detect, 318 .mode_valid = tfp410_mode_valid, 319 .mode_set = tfp410_mode_set, 320 .dpms = tfp410_dpms, 321 .get_hw_state = tfp410_get_hw_state, 322 .dump_regs = tfp410_dump_regs, 323 .destroy = tfp410_destroy, 324}; 325