mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-17 11:00:43 +09:00
[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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1230
drivers/video/tegra/dc/nvhdcp.c
Normal file
1230
drivers/video/tegra/dc/nvhdcp.c
Normal file
File diff suppressed because it is too large
Load Diff
29
drivers/video/tegra/dc/nvhdcp.h
Normal file
29
drivers/video/tegra/dc/nvhdcp.h
Normal 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
91
include/video/nvhdcp.h
Normal 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
|
||||
Reference in New Issue
Block a user