1/* 2 * Copyright (C) 2009 Francisco Jerez. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26 27#include <drm/drmP.h> 28#include "nouveau_drm.h" 29#include "nouveau_reg.h" 30#include "nouveau_encoder.h" 31#include "nouveau_connector.h" 32#include "nouveau_crtc.h" 33#include "hw.h" 34#include <drm/drm_crtc_helper.h> 35 36#include <drm/i2c/ch7006.h> 37 38static struct nouveau_i2c_board_info nv04_tv_encoder_info[] = { 39 { 40 { 41 I2C_BOARD_INFO("ch7006", 0x75), 42 .platform_data = &(struct ch7006_encoder_params) { 43 CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, 44 0, 0, 0, 45 CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, 46 CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC 47 } 48 }, 49 0 50 }, 51 { } 52}; 53 54int nv04_tv_identify(struct drm_device *dev, int i2c_index) 55{ 56 struct nouveau_drm *drm = nouveau_drm(dev); 57 struct nouveau_i2c *i2c = nvkm_i2c(&drm->device); 58 59 return i2c->identify(i2c, i2c_index, "TV encoder", 60 nv04_tv_encoder_info, NULL, NULL); 61} 62 63 64#define PLLSEL_TV_CRTC1_MASK \ 65 (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ 66 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) 67#define PLLSEL_TV_CRTC2_MASK \ 68 (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ 69 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) 70 71static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) 72{ 73 struct drm_device *dev = encoder->dev; 74 struct nouveau_drm *drm = nouveau_drm(dev); 75 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 76 struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; 77 uint8_t crtc1A; 78 79 NV_DEBUG(drm, "Setting dpms mode %d on TV encoder (output %d)\n", 80 mode, nv_encoder->dcb->index); 81 82 state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK); 83 84 if (mode == DRM_MODE_DPMS_ON) { 85 int head = nouveau_crtc(encoder->crtc)->index; 86 crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX); 87 88 state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK : 89 PLLSEL_TV_CRTC1_MASK; 90 91 /* Inhibit hsync */ 92 crtc1A |= 0x80; 93 94 NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A); 95 } 96 97 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); 98 99 get_slave_funcs(encoder)->dpms(encoder, mode); 100} 101 102static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) 103{ 104 struct nv04_crtc_reg *state = &nv04_display(dev)->mode_reg.crtc_reg[head]; 105 106 state->tv_setup = 0; 107 108 if (bind) 109 state->CRTC[NV_CIO_CRE_49] |= 0x10; 110 else 111 state->CRTC[NV_CIO_CRE_49] &= ~0x10; 112 113 NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, 114 state->CRTC[NV_CIO_CRE_LCD__INDEX]); 115 NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49, 116 state->CRTC[NV_CIO_CRE_49]); 117 NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, 118 state->tv_setup); 119} 120 121static void nv04_tv_prepare(struct drm_encoder *encoder) 122{ 123 struct drm_device *dev = encoder->dev; 124 int head = nouveau_crtc(encoder->crtc)->index; 125 struct drm_encoder_helper_funcs *helper = encoder->helper_private; 126 127 helper->dpms(encoder, DRM_MODE_DPMS_OFF); 128 129 nv04_dfp_disable(dev, head); 130 131 if (nv_two_heads(dev)) 132 nv04_tv_bind(dev, head ^ 1, false); 133 134 nv04_tv_bind(dev, head, true); 135} 136 137static void nv04_tv_mode_set(struct drm_encoder *encoder, 138 struct drm_display_mode *mode, 139 struct drm_display_mode *adjusted_mode) 140{ 141 struct drm_device *dev = encoder->dev; 142 struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 143 struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 144 145 regp->tv_htotal = adjusted_mode->htotal; 146 regp->tv_vtotal = adjusted_mode->vtotal; 147 148 /* These delay the TV signals with respect to the VGA port, 149 * they might be useful if we ever allow a CRTC to drive 150 * multiple outputs. 151 */ 152 regp->tv_hskew = 1; 153 regp->tv_hsync_delay = 1; 154 regp->tv_hsync_delay2 = 64; 155 regp->tv_vskew = 1; 156 regp->tv_vsync_delay = 1; 157 158 get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); 159} 160 161static void nv04_tv_commit(struct drm_encoder *encoder) 162{ 163 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 164 struct drm_device *dev = encoder->dev; 165 struct nouveau_drm *drm = nouveau_drm(dev); 166 struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 167 struct drm_encoder_helper_funcs *helper = encoder->helper_private; 168 169 helper->dpms(encoder, DRM_MODE_DPMS_ON); 170 171 NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", 172 nouveau_encoder_connector_get(nv_encoder)->base.name, 173 nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); 174} 175 176static void nv04_tv_destroy(struct drm_encoder *encoder) 177{ 178 get_slave_funcs(encoder)->destroy(encoder); 179 drm_encoder_cleanup(encoder); 180 181 kfree(encoder->helper_private); 182 kfree(nouveau_encoder(encoder)); 183} 184 185static const struct drm_encoder_funcs nv04_tv_funcs = { 186 .destroy = nv04_tv_destroy, 187}; 188 189static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { 190 .dpms = nv04_tv_dpms, 191 .save = drm_i2c_encoder_save, 192 .restore = drm_i2c_encoder_restore, 193 .mode_fixup = drm_i2c_encoder_mode_fixup, 194 .prepare = nv04_tv_prepare, 195 .commit = nv04_tv_commit, 196 .mode_set = nv04_tv_mode_set, 197 .detect = drm_i2c_encoder_detect, 198}; 199 200int 201nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) 202{ 203 struct nouveau_encoder *nv_encoder; 204 struct drm_encoder *encoder; 205 struct drm_device *dev = connector->dev; 206 struct nouveau_drm *drm = nouveau_drm(dev); 207 struct nouveau_i2c *i2c = nvkm_i2c(&drm->device); 208 struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); 209 int type, ret; 210 211 /* Ensure that we can talk to this encoder */ 212 type = nv04_tv_identify(dev, entry->i2c_index); 213 if (type < 0) 214 return type; 215 216 /* Allocate the necessary memory */ 217 nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 218 if (!nv_encoder) 219 return -ENOMEM; 220 221 /* Initialize the common members */ 222 encoder = to_drm_encoder(nv_encoder); 223 224 drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); 225 drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); 226 227 encoder->possible_crtcs = entry->heads; 228 encoder->possible_clones = 0; 229 nv_encoder->dcb = entry; 230 nv_encoder->or = ffs(entry->or) - 1; 231 232 /* Run the slave-specific initialization */ 233 ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), 234 &port->adapter, 235 &nv04_tv_encoder_info[type].dev); 236 if (ret < 0) 237 goto fail_cleanup; 238 239 /* Attach it to the specified connector. */ 240 get_slave_funcs(encoder)->create_resources(encoder, connector); 241 drm_mode_connector_attach_encoder(connector, encoder); 242 243 return 0; 244 245fail_cleanup: 246 drm_encoder_cleanup(encoder); 247 kfree(nv_encoder); 248 return ret; 249} 250