linux/drivers/gpu/drm/nouveau/nv04_tv.c
<<
>>
Prefs
   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 "drmP.h"
  28#include "nouveau_drv.h"
  29#include "nouveau_encoder.h"
  30#include "nouveau_connector.h"
  31#include "nouveau_crtc.h"
  32#include "nouveau_hw.h"
  33#include "drm_crtc_helper.h"
  34
  35#include "i2c/ch7006.h"
  36
  37static struct i2c_board_info nv04_tv_encoder_info[] = {
  38        {
  39                I2C_BOARD_INFO("ch7006", 0x75),
  40                .platform_data = &(struct ch7006_encoder_params) {
  41                        CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
  42                        0, 0, 0,
  43                        CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
  44                        CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
  45                }
  46        },
  47        { }
  48};
  49
  50int nv04_tv_identify(struct drm_device *dev, int i2c_index)
  51{
  52        return nouveau_i2c_identify(dev, "TV encoder", nv04_tv_encoder_info,
  53                                    NULL, i2c_index);
  54}
  55
  56
  57#define PLLSEL_TV_CRTC1_MASK                            \
  58        (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1          \
  59         | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
  60#define PLLSEL_TV_CRTC2_MASK                            \
  61        (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2          \
  62         | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
  63
  64static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
  65{
  66        struct drm_device *dev = encoder->dev;
  67        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
  68        struct drm_nouveau_private *dev_priv = dev->dev_private;
  69        struct nv04_mode_state *state = &dev_priv->mode_reg;
  70        uint8_t crtc1A;
  71
  72        NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
  73                mode, nv_encoder->dcb->index);
  74
  75        state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
  76
  77        if (mode == DRM_MODE_DPMS_ON) {
  78                int head = nouveau_crtc(encoder->crtc)->index;
  79                crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
  80
  81                state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK :
  82                                        PLLSEL_TV_CRTC1_MASK;
  83
  84                /* Inhibit hsync */
  85                crtc1A |= 0x80;
  86
  87                NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
  88        }
  89
  90        NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
  91
  92        get_slave_funcs(encoder)->dpms(encoder, mode);
  93}
  94
  95static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
  96{
  97        struct drm_nouveau_private *dev_priv = dev->dev_private;
  98        struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
  99
 100        state->tv_setup = 0;
 101
 102        if (bind)
 103                state->CRTC[NV_CIO_CRE_49] |= 0x10;
 104        else
 105                state->CRTC[NV_CIO_CRE_49] &= ~0x10;
 106
 107        NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
 108                       state->CRTC[NV_CIO_CRE_LCD__INDEX]);
 109        NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
 110                       state->CRTC[NV_CIO_CRE_49]);
 111        NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
 112                      state->tv_setup);
 113}
 114
 115static void nv04_tv_prepare(struct drm_encoder *encoder)
 116{
 117        struct drm_device *dev = encoder->dev;
 118        int head = nouveau_crtc(encoder->crtc)->index;
 119        struct drm_encoder_helper_funcs *helper = encoder->helper_private;
 120
 121        helper->dpms(encoder, DRM_MODE_DPMS_OFF);
 122
 123        nv04_dfp_disable(dev, head);
 124
 125        if (nv_two_heads(dev))
 126                nv04_tv_bind(dev, head ^ 1, false);
 127
 128        nv04_tv_bind(dev, head, true);
 129}
 130
 131static void nv04_tv_mode_set(struct drm_encoder *encoder,
 132                             struct drm_display_mode *mode,
 133                             struct drm_display_mode *adjusted_mode)
 134{
 135        struct drm_device *dev = encoder->dev;
 136        struct drm_nouveau_private *dev_priv = dev->dev_private;
 137        struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
 138        struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
 139
 140        regp->tv_htotal = adjusted_mode->htotal;
 141        regp->tv_vtotal = adjusted_mode->vtotal;
 142
 143        /* These delay the TV signals with respect to the VGA port,
 144         * they might be useful if we ever allow a CRTC to drive
 145         * multiple outputs.
 146         */
 147        regp->tv_hskew = 1;
 148        regp->tv_hsync_delay = 1;
 149        regp->tv_hsync_delay2 = 64;
 150        regp->tv_vskew = 1;
 151        regp->tv_vsync_delay = 1;
 152
 153        get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode);
 154}
 155
 156static void nv04_tv_commit(struct drm_encoder *encoder)
 157{
 158        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
 159        struct drm_device *dev = encoder->dev;
 160        struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
 161        struct drm_encoder_helper_funcs *helper = encoder->helper_private;
 162
 163        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 164
 165        NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
 166                      drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
 167                      '@' + ffs(nv_encoder->dcb->or));
 168}
 169
 170static void nv04_tv_destroy(struct drm_encoder *encoder)
 171{
 172        get_slave_funcs(encoder)->destroy(encoder);
 173        drm_encoder_cleanup(encoder);
 174
 175        kfree(encoder->helper_private);
 176        kfree(nouveau_encoder(encoder));
 177}
 178
 179static const struct drm_encoder_funcs nv04_tv_funcs = {
 180        .destroy = nv04_tv_destroy,
 181};
 182
 183int
 184nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
 185{
 186        struct nouveau_encoder *nv_encoder;
 187        struct drm_encoder *encoder;
 188        struct drm_device *dev = connector->dev;
 189        struct drm_encoder_helper_funcs *hfuncs;
 190        struct drm_encoder_slave_funcs *sfuncs;
 191        struct nouveau_i2c_chan *i2c =
 192                nouveau_i2c_find(dev, entry->i2c_index);
 193        int type, ret;
 194
 195        /* Ensure that we can talk to this encoder */
 196        type = nv04_tv_identify(dev, entry->i2c_index);
 197        if (type < 0)
 198                return type;
 199
 200        /* Allocate the necessary memory */
 201        nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
 202        if (!nv_encoder)
 203                return -ENOMEM;
 204
 205        hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL);
 206        if (!hfuncs) {
 207                ret = -ENOMEM;
 208                goto fail_free;
 209        }
 210
 211        /* Initialize the common members */
 212        encoder = to_drm_encoder(nv_encoder);
 213
 214        drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC);
 215        drm_encoder_helper_add(encoder, hfuncs);
 216
 217        encoder->possible_crtcs = entry->heads;
 218        encoder->possible_clones = 0;
 219        nv_encoder->dcb = entry;
 220        nv_encoder->or = ffs(entry->or) - 1;
 221
 222        /* Run the slave-specific initialization */
 223        ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
 224                                   &i2c->adapter, &nv04_tv_encoder_info[type]);
 225        if (ret < 0)
 226                goto fail_cleanup;
 227
 228        /* Fill the function pointers */
 229        sfuncs = get_slave_funcs(encoder);
 230
 231        *hfuncs = (struct drm_encoder_helper_funcs) {
 232                .dpms = nv04_tv_dpms,
 233                .save = sfuncs->save,
 234                .restore = sfuncs->restore,
 235                .mode_fixup = sfuncs->mode_fixup,
 236                .prepare = nv04_tv_prepare,
 237                .commit = nv04_tv_commit,
 238                .mode_set = nv04_tv_mode_set,
 239                .detect = sfuncs->detect,
 240        };
 241
 242        /* Attach it to the specified connector. */
 243        sfuncs->create_resources(encoder, connector);
 244        drm_mode_connector_attach_encoder(connector, encoder);
 245
 246        return 0;
 247
 248fail_cleanup:
 249        drm_encoder_cleanup(encoder);
 250        kfree(hfuncs);
 251fail_free:
 252        kfree(nv_encoder);
 253        return ret;
 254}
 255