linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 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 "nv50.h"
  25#include "outpdp.h"
  26
  27#include <subdev/timer.h>
  28
  29static inline u32
  30g94_sor_soff(struct nvkm_output_dp *outp)
  31{
  32        return (ffs(outp->base.info.or) - 1) * 0x800;
  33}
  34
  35static inline u32
  36g94_sor_loff(struct nvkm_output_dp *outp)
  37{
  38        return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
  39}
  40
  41/*******************************************************************************
  42 * TMDS/LVDS
  43 ******************************************************************************/
  44static const struct nvkm_output_func
  45g94_sor_output_func = {
  46};
  47
  48int
  49g94_sor_output_new(struct nvkm_disp *disp, int index,
  50                   struct dcb_output *dcbE, struct nvkm_output **poutp)
  51{
  52        return nvkm_output_new_(&g94_sor_output_func, disp,
  53                                index, dcbE, poutp);
  54}
  55
  56/*******************************************************************************
  57 * DisplayPort
  58 ******************************************************************************/
  59u32
  60g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
  61{
  62        static const u8 gm100[] = { 0, 8, 16, 24 };
  63        static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
  64        static const u8   g94[] = { 16, 8, 0, 24 };
  65        if (device->chipset >= 0x110)
  66                return gm100[lane];
  67        if (device->chipset == 0xaf)
  68                return mcp89[lane];
  69        return g94[lane];
  70}
  71
  72static int
  73g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
  74{
  75        struct nvkm_device *device = outp->base.disp->engine.subdev.device;
  76        const u32 loff = g94_sor_loff(outp);
  77        nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
  78        return 0;
  79}
  80
  81int
  82g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
  83{
  84        struct nvkm_device *device = outp->base.disp->engine.subdev.device;
  85        const u32 soff = g94_sor_soff(outp);
  86        const u32 loff = g94_sor_loff(outp);
  87        u32 mask = 0, i;
  88
  89        for (i = 0; i < nr; i++)
  90                mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3);
  91
  92        nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
  93        nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
  94        nvkm_msec(device, 2000,
  95                if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
  96                        break;
  97        );
  98        return 0;
  99}
 100
 101static int
 102g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
 103{
 104        struct nvkm_device *device = outp->base.disp->engine.subdev.device;
 105        const u32 soff = g94_sor_soff(outp);
 106        const u32 loff = g94_sor_loff(outp);
 107        u32 dpctrl = 0x00000000;
 108        u32 clksor = 0x00000000;
 109
 110        dpctrl |= ((1 << nr) - 1) << 16;
 111        if (ef)
 112                dpctrl |= 0x00004000;
 113        if (bw > 0x06)
 114                clksor |= 0x00040000;
 115
 116        nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor);
 117        nvkm_mask(device, 0x61c10c + loff, 0x001f4000, dpctrl);
 118        return 0;
 119}
 120
 121static int
 122g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
 123{
 124        struct nvkm_device *device = outp->base.disp->engine.subdev.device;
 125        struct nvkm_bios *bios = device->bios;
 126        const u32 shift = g94_sor_dp_lane_map(device, ln);
 127        const u32 loff = g94_sor_loff(outp);
 128        u32 addr, data[3];
 129        u8  ver, hdr, cnt, len;
 130        struct nvbios_dpout info;
 131        struct nvbios_dpcfg ocfg;
 132
 133        addr = nvbios_dpout_match(bios, outp->base.info.hasht,
 134                                        outp->base.info.hashm,
 135                                  &ver, &hdr, &cnt, &len, &info);
 136        if (!addr)
 137                return -ENODEV;
 138
 139        addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
 140                                  &ver, &hdr, &cnt, &len, &ocfg);
 141        if (!addr)
 142                return -EINVAL;
 143
 144        data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
 145        data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
 146        data[2] = nvkm_rd32(device, 0x61c130 + loff);
 147        if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
 148                data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
 149        nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
 150        nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
 151        nvkm_wr32(device, 0x61c130 + loff, data[2]);
 152        return 0;
 153}
 154
 155static const struct nvkm_output_dp_func
 156g94_sor_dp_func = {
 157        .pattern = g94_sor_dp_pattern,
 158        .lnk_pwr = g94_sor_dp_lnk_pwr,
 159        .lnk_ctl = g94_sor_dp_lnk_ctl,
 160        .drv_ctl = g94_sor_dp_drv_ctl,
 161};
 162
 163int
 164g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
 165               struct nvkm_output **poutp)
 166{
 167        return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
 168}
 169