[ARM] tegra: hdmi nvhdcp driver

Device /dev/nvhdcpX is used to manage NVHDCP on framebuffer /dev/fbX.
These devices are created on hdmi driver initialition when it is
attached to dc. Currently only one nvhdcp device may be created. An ioctl
interface is in video/nvhdcp.h

Check for repeaters and store repeater info. userspace application
queries this status to authenticate the connection. When authentication
fails, auto-renegotiate every 1.75 seconds. Give up after 5 failed attempts,
reset after hotplug or policy change.

use TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND in tegra_dc_out.flags in board
panel configuration to select a different default policy at probe. Currently
only TEGRA_DC_OUT_NVHDCP_POLICY_ALWAYS_ON is supported.

Change-Id: I0db66fc86096b98d2604544061721d291523de75
Reviewed-by: Jon Mayo <jmayo@nvidia.com>
Tested-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-by: Phillip Smith <psmith@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Signed-off-by: Erik Gilling <konkers@android.com>
This commit is contained in:
Jon Mayo
2011-01-03 17:26:25 -08:00
committed by Erik Gilling
parent bb2a3184bc
commit cf030d95bb
8 changed files with 1421 additions and 8 deletions

View File

@@ -73,9 +73,13 @@ struct tegra_dc_out {
int (*disable)(void);
};
#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1)
#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1)
#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1)
/* bits for tegra_dc_out.flags */
#define TEGRA_DC_OUT_HOTPLUG_HIGH (0 << 1)
#define TEGRA_DC_OUT_HOTPLUG_LOW (1 << 1)
#define TEGRA_DC_OUT_HOTPLUG_MASK (1 << 1)
#define TEGRA_DC_OUT_NVHDCP_POLICY_ALWAYS_ON (0 << 2)
#define TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND (1 << 2)
#define TEGRA_DC_OUT_NVHDCP_POLICY_MASK (1 << 2)
#define TEGRA_DC_ALIGN_MSB 0
#define TEGRA_DC_ALIGN_LSB 1

View File

@@ -1,4 +1,5 @@
obj-y += dc.o
obj-y += rgb.o
obj-y += hdmi.o
obj-y += edid.o
obj-y += nvhdcp.o
obj-y += edid.o

View File

@@ -31,11 +31,14 @@
#include <mach/fb.h>
#include <mach/nvhost.h>
#include <video/tegrafb.h>
#include "dc_reg.h"
#include "dc_priv.h"
#include "hdmi_reg.h"
#include "hdmi.h"
#include "edid.h"
#include "nvhdcp.h"
/* datasheet claims this will always be 216MHz */
#define HDMI_AUDIOCLK_FREQ 216000000
@@ -45,6 +48,7 @@
struct tegra_dc_hdmi_data {
struct tegra_dc *dc;
struct tegra_edid *edid;
struct tegra_nvhdcp *nvhdcp;
struct delayed_work work;
struct resource *base_res;
@@ -203,13 +207,13 @@ static const struct tegra_hdmi_audio_config
}
static inline unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi,
unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi,
unsigned long reg)
{
return readl(hdmi->base + reg * 4);
}
static inline void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi,
void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi,
unsigned long val, unsigned long reg)
{
writel(val, hdmi->base + reg * 4);
@@ -441,12 +445,12 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc)
int err;
if (!tegra_dc_hdmi_hpd(dc))
return false;
goto fail;
err = tegra_edid_get_monspecs(hdmi->edid, &specs);
if (err < 0) {
dev_err(&dc->ndev->dev, "error reading edid\n");
return false;
goto fail;
}
/* monitors like to lie about these but they are still useful for
@@ -461,6 +465,10 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc)
tegra_fb_update_monspecs(dc->fb, &specs, tegra_dc_hdmi_mode_filter);
dev_info(&dc->ndev->dev, "display detected\n");
return true;
fail:
tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0);
return false;
}
@@ -502,6 +510,7 @@ static void tegra_dc_hdmi_suspend(struct tegra_dc *dc)
unsigned long flags;
spin_lock_irqsave(&hdmi->suspend_lock, flags);
tegra_nvhdcp_suspend(hdmi->nvhdcp);
hdmi->suspended = true;
spin_unlock_irqrestore(&hdmi->suspend_lock, flags);
}
@@ -597,6 +606,14 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
goto err_free_irq;
}
hdmi->nvhdcp = tegra_nvhdcp_create(hdmi, dc->ndev->id,
dc->out->dcc_bus);
if (IS_ERR_OR_NULL(hdmi->nvhdcp)) {
dev_err(&dc->ndev->dev, "hdmi: can't create nvhdcp\n");
err = PTR_ERR(hdmi->nvhdcp);
goto err_edid_destroy;
}
INIT_DELAYED_WORK(&hdmi->work, tegra_dc_hdmi_detect_worker);
hdmi->dc = dc;
@@ -613,8 +630,18 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
tegra_dc_set_outdata(dc, hdmi);
/* boards can select default content protection policy */
if (dc->out->flags & TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND) {
tegra_nvhdcp_set_policy(hdmi->nvhdcp,
TEGRA_NVHDCP_POLICY_ON_DEMAND);
} else {
tegra_nvhdcp_set_policy(hdmi->nvhdcp,
TEGRA_NVHDCP_POLICY_ALWAYS_ON);
}
return 0;
err_edid_destroy:
tegra_edid_destroy(hdmi->edid);
err_free_irq:
free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
err_put_clock:
@@ -645,6 +672,7 @@ static void tegra_dc_hdmi_destroy(struct tegra_dc *dc)
clk_put(hdmi->disp1_clk);
clk_put(hdmi->disp2_clk);
tegra_edid_destroy(hdmi->edid);
tegra_nvhdcp_destroy(hdmi->nvhdcp);
kfree(hdmi);
@@ -1096,15 +1124,21 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc)
tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
if (!hdmi->dvi)
tegra_nvhdcp_set_plug(hdmi->nvhdcp, 1);
}
static void tegra_dc_hdmi_disable(struct tegra_dc *dc)
{
struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc);
tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0);
tegra_periph_reset_assert(hdmi->clk);
clk_disable(hdmi->clk);
}
struct tegra_dc_out_ops tegra_dc_hdmi_ops = {
.init = tegra_dc_hdmi_init,
.destroy = tegra_dc_hdmi_destroy,

View File

@@ -180,4 +180,10 @@ struct hdmi_audio_infoframe {
#define HDMI_AUDIO_CXT_HE_AAC_V2 0x2
#define HDMI_AUDIO_CXT_MPEG_SURROUND 0x3
struct tegra_dc_hdmi_data;
unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi,
unsigned long reg);
void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi,
unsigned long val, unsigned long reg);
#endif

View File

@@ -57,16 +57,29 @@
#define HDMI_NV_PDISP_RG_HDCP_AKSV_MSB 0x08
#define HDMI_NV_PDISP_RG_HDCP_AKSV_LSB 0x09
#define HDMI_NV_PDISP_RG_HDCP_BKSV_MSB 0x0a
#define REPEATER (1 << 31)
#define HDMI_NV_PDISP_RG_HDCP_BKSV_LSB 0x0b
#define HDMI_NV_PDISP_RG_HDCP_CKSV_MSB 0x0c
#define HDMI_NV_PDISP_RG_HDCP_CKSV_LSB 0x0d
#define HDMI_NV_PDISP_RG_HDCP_DKSV_MSB 0x0e
#define HDMI_NV_PDISP_RG_HDCP_DKSV_LSB 0x0f
#define HDMI_NV_PDISP_RG_HDCP_CTRL 0x10
#define HDCP_RUN_YES (1 << 0)
#define CRYPT_ENABLED (1 << 1)
#define ONEONE_ENABLED (1 << 3)
#define AN_VALID (1 << 8)
#define R0_VALID (1 << 9)
#define SPRIME_VALID (1 << 10)
#define MPRIME_VALID (1 << 11)
#define SROM_ERR (1 << 13)
#define HDMI_NV_PDISP_RG_HDCP_CMODE 0x11
#define TMDS0_LINK0 (1 << 4)
#define READ_S (1 << 0)
#define READ_M (2 << 0)
#define HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB 0x12
#define HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB 0x13
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB 0x14
#define STATUS_CS (1 << 6)
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2 0x15
#define HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1 0x16
#define HDMI_NV_PDISP_RG_HDCP_RI 0x17
@@ -417,6 +430,11 @@
#define PE_CURRENT3(x) (((x) & 0xf) << 24)
#define HDMI_NV_PDISP_KEY_CTRL 0x9a
#define LOCAL_KEYS (1 << 0)
#define AUTOINC (1 << 1)
#define WRITE16 (1 << 4)
#define PKEY_REQUEST_RELOAD_TRIGGER (1 << 5)
#define PKEY_LOADED (1 << 6)
#define HDMI_NV_PDISP_KEY_DEBUG0 0x9b
#define HDMI_NV_PDISP_KEY_DEBUG1 0x9c
#define HDMI_NV_PDISP_KEY_DEBUG2 0x9d

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
* drivers/video/tegra/dc/nvhdcp.h
*
* Copyright (c) 2010-2011, NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __DRIVERS_VIDEO_TEGRA_DC_NVHDCP_H
#define __DRIVERS_VIDEO_TEGRA_DC_NVHDCP_H
#include <video/nvhdcp.h>
struct tegra_nvhdcp;
void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd);
int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol);
void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp);
struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi,
int id, int bus);
void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp);
#endif

91
include/video/nvhdcp.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* include/video/nvhdcp.h
*
* Copyright (c) 2010-2011, NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _LINUX_NVHDCP_H_
#define _LINUX_NVHDCP_H_
#include <linux/fb.h>
#include <linux/types.h>
#include <asm/ioctl.h>
/* maximum receivers and repeaters connected at a time */
#define TEGRA_NVHDCP_MAX_DEVS 127
/* values for value_flags */
#define TEGRA_NVHDCP_FLAG_AN 0x0001
#define TEGRA_NVHDCP_FLAG_AKSV 0x0002
#define TEGRA_NVHDCP_FLAG_BKSV 0x0004
#define TEGRA_NVHDCP_FLAG_BSTATUS 0x0008 /* repeater status */
#define TEGRA_NVHDCP_FLAG_CN 0x0010 /* c_n */
#define TEGRA_NVHDCP_FLAG_CKSV 0x0020 /* c_ksv */
#define TEGRA_NVHDCP_FLAG_DKSV 0x0040 /* d_ksv */
#define TEGRA_NVHDCP_FLAG_KP 0x0080 /* k_prime */
#define TEGRA_NVHDCP_FLAG_S 0x0100 /* hdcp_status */
#define TEGRA_NVHDCP_FLAG_CS 0x0200 /* connection state */
#define TEGRA_NVHDCP_FLAG_V 0x0400
#define TEGRA_NVHDCP_FLAG_MP 0x0800
#define TEGRA_NVHDCP_FLAG_BKSVLIST 0x1000
/* values for packet_results */
#define TEGRA_NVHDCP_RESULT_SUCCESS 0
#define TEGRA_NVHDCP_RESULT_UNSUCCESSFUL 1
#define TEGRA_NVHDCP_RESULT_PENDING 0x103
#define TEGRA_NVHDCP_RESULT_LINK_FAILED 0xc0000013
/* TODO: replace with -EINVAL */
#define TEGRA_NVHDCP_RESULT_INVALID_PARAMETER 0xc000000d
#define TEGRA_NVHDCP_RESULT_INVALID_PARAMETER_MIX 0xc0000030
/* TODO: replace with -ENOMEM */
#define TEGRA_NVHDCP_RESULT_NO_MEMORY 0xc0000017
struct tegra_nvhdcp_packet {
__u32 value_flags; // (IN/OUT)
__u32 packet_results; // (OUT)
__u64 c_n; // (IN) upstream exchange number
__u64 c_ksv; // (IN)
__u32 b_status; // (OUT) link/repeater status
__u64 hdcp_status; // (OUT) READ_S
__u64 cs; // (OUT) Connection State
__u64 k_prime; // (OUT)
__u64 a_n; // (OUT)
__u64 a_ksv; // (OUT)
__u64 b_ksv; // (OUT)
__u64 d_ksv; // (OUT)
__u8 v_prime[20]; // (OUT) 160-bit
__u64 m_prime; // (OUT)
// (OUT) Valid KSVs in the bKsvList. Maximum is 127 devices
__u32 num_bksv_list;
// (OUT) Up to 127 receivers & repeaters
__u64 bksv_list[TEGRA_NVHDCP_MAX_DEVS];
};
/* parameters to TEGRAIO_NVHDCP_SET_POLICY */
#define TEGRA_NVHDCP_POLICY_ON_DEMAND 0
#define TEGRA_NVHDCP_POLICY_ALWAYS_ON 1
/* ioctls */
#define TEGRAIO_NVHDCP_ON _IO('F', 0x70)
#define TEGRAIO_NVHDCP_OFF _IO('F', 0x71)
#define TEGRAIO_NVHDCP_SET_POLICY _IOW('F', 0x72, __u32)
#define TEGRAIO_NVHDCP_READ_M _IOWR('F', 0x73, struct tegra_nvhdcp_packet)
#define TEGRAIO_NVHDCP_READ_S _IOWR('F', 0x74, struct tegra_nvhdcp_packet)
#define TEGRAIO_NVHDCP_RENEGOTIATE _IO('F', 0x75)
#endif