1/* 2 * Copyright 2014 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24#include "outpdp.h" 25#include "conn.h" 26#include "dport.h" 27#include "priv.h" 28 29#include <subdev/i2c.h> 30 31#include <nvif/event.h> 32 33int 34nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait) 35{ 36 struct nvkm_output_dp *outp = (void *)base; 37 bool retrain = true; 38 u8 link[2], stat[3]; 39 u32 linkrate; 40 int ret, i; 41 42 /* check that the link is trained at a high enough rate */ 43 ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2); 44 if (ret) { 45 DBG("failed to read link config, assuming no sink\n"); 46 goto done; 47 } 48 49 linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); 50 linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */ 51 datarate = (datarate + 9) / 10; /* -> decakilobits */ 52 if (linkrate < datarate) { 53 DBG("link not trained at sufficient rate\n"); 54 goto done; 55 } 56 57 /* check that link is still trained */ 58 ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3); 59 if (ret) { 60 DBG("failed to read link status, assuming no sink\n"); 61 goto done; 62 } 63 64 if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { 65 for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) { 66 u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; 67 if (!(lane & DPCD_LS02_LANE0_CR_DONE) || 68 !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || 69 !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { 70 DBG("lane %d not equalised\n", lane); 71 goto done; 72 } 73 } 74 retrain = false; 75 } else { 76 DBG("no inter-lane alignment\n"); 77 } 78 79done: 80 if (retrain || !atomic_read(&outp->lt.done)) { 81 /* no sink, but still need to configure source */ 82 if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { 83 outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = 84 outp->base.info.dpconf.link_bw; 85 outp->dpcd[DPCD_RC02] = 86 outp->base.info.dpconf.link_nr; 87 } 88 atomic_set(&outp->lt.done, 0); 89 schedule_work(&outp->lt.work); 90 } else { 91 nvkm_notify_get(&outp->irq); 92 } 93 94 if (wait) { 95 if (!wait_event_timeout(outp->lt.wait, 96 atomic_read(&outp->lt.done), 97 msecs_to_jiffies(2000))) 98 ret = -ETIMEDOUT; 99 } 100 101 return ret; 102} 103 104static void 105nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present) 106{ 107 struct nvkm_i2c_port *port = outp->base.edid; 108 if (present) { 109 if (!outp->present) { 110 nvkm_i2c(port)->acquire_pad(port, 0); 111 DBG("aux power -> always\n"); 112 outp->present = true; 113 } 114 nvkm_output_dp_train(&outp->base, 0, true); 115 } else { 116 if (outp->present) { 117 nvkm_i2c(port)->release_pad(port); 118 DBG("aux power -> demand\n"); 119 outp->present = false; 120 } 121 atomic_set(&outp->lt.done, 0); 122 } 123} 124 125static void 126nvkm_output_dp_detect(struct nvkm_output_dp *outp) 127{ 128 struct nvkm_i2c_port *port = outp->base.edid; 129 int ret = nvkm_i2c(port)->acquire_pad(port, 0); 130 if (ret == 0) { 131 ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV, 132 outp->dpcd, sizeof(outp->dpcd)); 133 nvkm_output_dp_enable(outp, ret == 0); 134 nvkm_i2c(port)->release_pad(port); 135 } 136} 137 138static int 139nvkm_output_dp_hpd(struct nvkm_notify *notify) 140{ 141 struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd); 142 struct nvkm_output_dp *outp; 143 struct nvkm_disp *disp = nvkm_disp(conn); 144 const struct nvkm_i2c_ntfy_rep *line = notify->data; 145 struct nvif_notify_conn_rep_v0 rep = {}; 146 147 list_for_each_entry(outp, &disp->outp, base.head) { 148 if (outp->base.conn == conn && 149 outp->info.type == DCB_OUTPUT_DP) { 150 DBG("HPD: %d\n", line->mask); 151 nvkm_output_dp_detect(outp); 152 153 if (line->mask & NVKM_I2C_UNPLUG) 154 rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; 155 if (line->mask & NVKM_I2C_PLUG) 156 rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; 157 158 nvkm_event_send(&disp->hpd, rep.mask, conn->index, 159 &rep, sizeof(rep)); 160 return NVKM_NOTIFY_KEEP; 161 } 162 } 163 164 WARN_ON(1); 165 return NVKM_NOTIFY_DROP; 166} 167 168static int 169nvkm_output_dp_irq(struct nvkm_notify *notify) 170{ 171 struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq); 172 struct nvkm_disp *disp = nvkm_disp(outp); 173 const struct nvkm_i2c_ntfy_rep *line = notify->data; 174 struct nvif_notify_conn_rep_v0 rep = { 175 .mask = NVIF_NOTIFY_CONN_V0_IRQ, 176 }; 177 int index = outp->base.info.connector; 178 179 DBG("IRQ: %d\n", line->mask); 180 nvkm_output_dp_train(&outp->base, 0, true); 181 182 nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep)); 183 return NVKM_NOTIFY_DROP; 184} 185 186int 187_nvkm_output_dp_fini(struct nvkm_object *object, bool suspend) 188{ 189 struct nvkm_output_dp *outp = (void *)object; 190 nvkm_notify_put(&outp->irq); 191 nvkm_output_dp_enable(outp, false); 192 return nvkm_output_fini(&outp->base, suspend); 193} 194 195int 196_nvkm_output_dp_init(struct nvkm_object *object) 197{ 198 struct nvkm_output_dp *outp = (void *)object; 199 nvkm_output_dp_detect(outp); 200 return nvkm_output_init(&outp->base); 201} 202 203void 204_nvkm_output_dp_dtor(struct nvkm_object *object) 205{ 206 struct nvkm_output_dp *outp = (void *)object; 207 nvkm_notify_fini(&outp->irq); 208 nvkm_output_destroy(&outp->base); 209} 210 211int 212nvkm_output_dp_create_(struct nvkm_object *parent, 213 struct nvkm_object *engine, 214 struct nvkm_oclass *oclass, 215 struct dcb_output *info, int index, 216 int length, void **pobject) 217{ 218 struct nvkm_bios *bios = nvkm_bios(parent); 219 struct nvkm_i2c *i2c = nvkm_i2c(parent); 220 struct nvkm_output_dp *outp; 221 u8 hdr, cnt, len; 222 u32 data; 223 int ret; 224 225 ret = nvkm_output_create_(parent, engine, oclass, info, index, 226 length, pobject); 227 outp = *pobject; 228 if (ret) 229 return ret; 230 231 nvkm_notify_fini(&outp->base.conn->hpd); 232 233 /* access to the aux channel is not optional... */ 234 if (!outp->base.edid) { 235 ERR("aux channel not found\n"); 236 return -ENODEV; 237 } 238 239 /* nor is the bios data for this output... */ 240 data = nvbios_dpout_match(bios, outp->base.info.hasht, 241 outp->base.info.hashm, &outp->version, 242 &hdr, &cnt, &len, &outp->info); 243 if (!data) { 244 ERR("no bios dp data\n"); 245 return -ENODEV; 246 } 247 248 DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len); 249 250 /* link training */ 251 INIT_WORK(&outp->lt.work, nvkm_dp_train); 252 init_waitqueue_head(&outp->lt.wait); 253 atomic_set(&outp->lt.done, 0); 254 255 /* link maintenance */ 256 ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true, 257 &(struct nvkm_i2c_ntfy_req) { 258 .mask = NVKM_I2C_IRQ, 259 .port = outp->base.edid->index, 260 }, 261 sizeof(struct nvkm_i2c_ntfy_req), 262 sizeof(struct nvkm_i2c_ntfy_rep), 263 &outp->irq); 264 if (ret) { 265 ERR("error monitoring aux irq event: %d\n", ret); 266 return ret; 267 } 268 269 /* hotplug detect, replaces gpio-based mechanism with aux events */ 270 ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true, 271 &(struct nvkm_i2c_ntfy_req) { 272 .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, 273 .port = outp->base.edid->index, 274 }, 275 sizeof(struct nvkm_i2c_ntfy_req), 276 sizeof(struct nvkm_i2c_ntfy_rep), 277 &outp->base.conn->hpd); 278 if (ret) { 279 ERR("error monitoring aux hpd events: %d\n", ret); 280 return ret; 281 } 282 283 return 0; 284} 285 286int 287_nvkm_output_dp_ctor(struct nvkm_object *parent, 288 struct nvkm_object *engine, 289 struct nvkm_oclass *oclass, void *info, u32 index, 290 struct nvkm_object **pobject) 291{ 292 struct nvkm_output_dp *outp; 293 int ret; 294 295 ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); 296 *pobject = nv_object(outp); 297 if (ret) 298 return ret; 299 300 return 0; 301} 302