Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions kernel-open/common/inc/nvkms-api-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -823,4 +823,15 @@ struct NvKmsHdmiVsifMetadata {
NvU8 payload[NVKMS_HDMI_VSIF_METADATA_MAX_PAYLOAD_SIZE];
};

/* One MST I2C write, read, or combined write/read transaction. */
#define NVKMS_MST_I2C_MAX_DATA 128
struct NvKmsMstI2cTransfer {
NvU8 writeAddress; /* 7-bit address */
NvU8 readAddress;
NvU16 writeSize;
NvU16 readSize;
NvU8 writeData[NVKMS_MST_I2C_MAX_DATA];
NvU8 readData[NVKMS_MST_I2C_MAX_DATA];
};

#endif /* NVKMS_API_TYPES_H */
5 changes: 5 additions & 0 deletions kernel-open/common/inc/nvkms-kapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,11 @@ struct NvKmsKapiFunctionsTable {
struct NvKmsKapiDevice *device,
const NvU32 head,
struct NvKmsKapiVblankIntrCallback *pCallback);

/* One bounded MST I2C transfer; serialized with modesetting and PM. */
NvBool (*mstI2cTransfer)(struct NvKmsKapiDevice *device,
NvKmsKapiDisplay display,
struct NvKmsMstI2cTransfer *transfer);
};

/** @} */
Expand Down
144 changes: 141 additions & 3 deletions kernel-open/nvidia-drm/nvidia-drm-connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,140 @@ nv_drm_connector_translate_dither_mode(enum nv_drm_dithering_mode mode,
}
#endif /* NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT */

#if IS_ENABLED(CONFIG_I2C)
/* Experimental until hardware hotplug/suspend validation is complete. */
static bool nv_drm_mst_ddc;
module_param_named(mst_ddc, nv_drm_mst_ddc, bool, 0444);
MODULE_PARM_DESC(mst_ddc, "Expose experimental per-display MST DDC adapters");

static int nv_drm_mst_ddc_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
struct nv_drm_connector *nv_connector =
container_of(adapter, struct nv_drm_connector, mst_ddc);
struct nv_drm_device *nv_dev = to_nv_device(nv_connector->base.dev);
struct NvKmsMstI2cTransfer transfer = { };
const unsigned int supported_i2c_flags = I2C_M_RD
#if defined(I2C_M_DMA_SAFE)
| I2C_M_DMA_SAFE
#endif
;
const struct i2c_msg *write = NULL;
struct i2c_msg *read = NULL;
int i;

if (num < 1 || num > 2) {
return -EOPNOTSUPP;
}
for (i = 0; i < num; i++) {
if ((msgs[i].flags & ~supported_i2c_flags) ||
msgs[i].addr > 0x7f || msgs[i].len == 0 ||
msgs[i].len > NVKMS_MST_I2C_MAX_DATA) {
return -EOPNOTSUPP;
}
}
if (num == 2) {
if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
return -EOPNOTSUPP;
}
write = &msgs[0];
read = &msgs[1];
} else if (msgs[0].flags & I2C_M_RD) {
read = &msgs[0];
} else {
write = &msgs[0];
}
if (write != NULL) {
transfer.writeAddress = write->addr;
transfer.writeSize = write->len;
memcpy(transfer.writeData, write->buf, write->len);
}
if (read != NULL) {
transfer.readAddress = read->addr;
transfer.readSize = read->len;
}
if (!nvKms->mstI2cTransfer(nv_dev->pDevice,
READ_ONCE(nv_connector->mst_ddc_display), &transfer)) {
return -EIO;
}
if (read != NULL) {
memcpy(read->buf, transfer.readData, read->len);
}
return num;
}

static u32 nv_drm_mst_ddc_functionality(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
}

static const struct i2c_algorithm nv_drm_mst_ddc_algo = {
.master_xfer = nv_drm_mst_ddc_xfer,
.functionality = nv_drm_mst_ddc_functionality,
};

static const struct i2c_adapter_quirks nv_drm_mst_ddc_quirks = {
.flags = I2C_AQ_COMB_WRITE_THEN_READ
#if defined(I2C_AQ_NO_ZERO_LEN)
| I2C_AQ_NO_ZERO_LEN
#endif
,
.max_num_msgs = 2,
.max_write_len = NVKMS_MST_I2C_MAX_DATA,
.max_read_len = NVKMS_MST_I2C_MAX_DATA,
.max_comb_1st_msg_len = NVKMS_MST_I2C_MAX_DATA,
.max_comb_2nd_msg_len = NVKMS_MST_I2C_MAX_DATA,
};

static void nv_drm_mst_ddc_init(struct nv_drm_connector *nv_connector,
NvU32 hDisplay)
{
struct drm_connector *connector = &nv_connector->base;
struct i2c_adapter *adapter = &nv_connector->mst_ddc;
int ret;

if (!nv_drm_mst_ddc || nv_connector->type != NVKMS_CONNECTOR_TYPE_DP ||
nv_connector->dpAddress[0] == '\0') {
return;
}

nv_connector->mst_ddc_display = hDisplay;
adapter->owner = THIS_MODULE;
adapter->algo = &nv_drm_mst_ddc_algo;
adapter->quirks = &nv_drm_mst_ddc_quirks;
adapter->dev.parent = connector->dev->dev;
/* Match the DRM MST adapter name recognized by existing DDC clients. */
snprintf(adapter->name, sizeof(adapter->name), "DPMST");
ret = i2c_add_adapter(adapter);
if (ret != 0) {
NV_DRM_LOG_ERR("Failed to register MST DDC adapter: %d", ret);
return;
}
nv_connector->mst_ddc_registered = true;
connector->ddc = adapter;
}

static void nv_drm_mst_ddc_fini(struct nv_drm_connector *nv_connector)
{
if (nv_connector->mst_ddc_registered) {
i2c_del_adapter(&nv_connector->mst_ddc);
nv_connector->mst_ddc_registered = false;
nv_connector->base.ddc = NULL;
}
}
#else
static void nv_drm_mst_ddc_init(struct nv_drm_connector *nv_connector,
NvU32 hDisplay) { }
static void nv_drm_mst_ddc_fini(struct nv_drm_connector *nv_connector) { }
#endif

static void nv_drm_connector_destroy(struct drm_connector *connector)
{
struct nv_drm_connector *nv_connector = to_nv_connector(connector);

drm_connector_unregister(connector);
nv_drm_mst_ddc_fini(nv_connector);

drm_connector_cleanup(connector);

Expand Down Expand Up @@ -706,7 +835,8 @@ static struct drm_connector*
nv_drm_connector_new(struct drm_device *dev,
NvU32 physicalIndex, NvKmsConnectorType type,
NvBool internal,
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH])
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH],
NvU32 hDisplay)
{
struct nv_drm_device *nv_dev = to_nv_device(dev);
struct nv_drm_connector *nv_connector = NULL;
Expand Down Expand Up @@ -818,6 +948,8 @@ nv_drm_connector_new(struct drm_device *dev,
NV_DRM_DITHERING_MODE_AUTO);
}

nv_drm_mst_ddc_init(nv_connector, hDisplay);

/* Register connector with DRM subsystem */

ret = drm_connector_register(&nv_connector->base);
Expand All @@ -833,6 +965,7 @@ nv_drm_connector_new(struct drm_device *dev,
return &nv_connector->base;

failed_connector_register:
nv_drm_mst_ddc_fini(nv_connector);
drm_connector_cleanup(&nv_connector->base);

failed_connector_init:
Expand All @@ -853,7 +986,8 @@ struct drm_connector*
nv_drm_get_connector(struct drm_device *dev,
NvU32 physicalIndex, NvKmsConnectorType type,
NvBool internal,
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH])
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH],
NvU32 hDisplay)
{
struct drm_connector *connector = NULL;
struct drm_connector_list_iter conn_iter;
Expand All @@ -868,6 +1002,10 @@ nv_drm_get_connector(struct drm_device *dev,
nv_connector->internal != internal);

if (strcmp(nv_connector->dpAddress, dpAddress) == 0) {
#if IS_ENABLED(CONFIG_I2C)
/* A reused connector can be associated with a new display. */
WRITE_ONCE(nv_connector->mst_ddc_display, hDisplay);
#endif
goto done;
}
}
Expand All @@ -880,7 +1018,7 @@ nv_drm_get_connector(struct drm_device *dev,
if (!connector) {
connector = nv_drm_connector_new(dev,
physicalIndex, type, internal,
dpAddress);
dpAddress, hDisplay);
}

return connector;
Expand Down
10 changes: 9 additions & 1 deletion kernel-open/nvidia-drm/nvidia-drm-connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#endif

#include <drm/drm_connector.h>
#include <linux/i2c.h>

#include "nvtypes.h"
#include "nvkms-api-types.h"
Expand Down Expand Up @@ -65,6 +66,12 @@ struct nv_drm_connector {
*/
struct nv_drm_crtc *modeset_permission_crtc;

#if IS_ENABLED(CONFIG_I2C)
struct i2c_adapter mst_ddc;
NvU32 mst_ddc_display;
bool mst_ddc_registered;
#endif

struct drm_connector base;
};

Expand Down Expand Up @@ -115,7 +122,8 @@ struct drm_connector*
nv_drm_get_connector(struct drm_device *dev,
NvU32 physicalIndex, NvKmsConnectorType type,
NvBool internal,
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH]);
char dpAddress[NVKMS_DP_ADDRESS_STRING_LENGTH],
NvU32 hDisplay);

bool nv_drm_connector_revoke_permissions(struct drm_device *dev,
struct nv_drm_connector *nv_connector);
Expand Down
2 changes: 1 addition & 1 deletion kernel-open/nvidia-drm/nvidia-drm-encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ nv_drm_add_encoder(struct drm_device *dev, NvKmsKapiDisplay hDisplay)
nv_drm_get_connector(dev,
connectorInfo->physicalIndex,
connectorInfo->type,
displayInfo->internal, displayInfo->dpAddress);
displayInfo->internal, displayInfo->dpAddress, hDisplay);

if (IS_ERR(connector)) {
ret = PTR_ERR(connector);
Expand Down
7 changes: 7 additions & 0 deletions src/common/displayport/inc/dp_connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ namespace DisplayPort
// the MOT bit. It is needed for some special cases where the MOT bit shouldn't
// be set but some customers need it to please their monitors.
//
// Start and poll under the caller's lock; wait outside that lock.
virtual bool startMstI2cTransfer(NvU64 id, unsigned writeAddress,
NvU8 *writeData, unsigned writeSize,
unsigned readAddress, unsigned readSize) = 0;
virtual bool pollMstI2cTransfer(NvU64 id, NvU8 *readData,
unsigned readSize, bool *complete) = 0;
virtual void cancelMstI2cTransfer(NvU64 id) = 0;
virtual bool getI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false) = 0;
virtual bool setI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false) = 0;

Expand Down
10 changes: 10 additions & 0 deletions src/common/displayport/inc/dp_deviceimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ namespace DisplayPort
void inferPathConstraints();


struct MstI2cRequest;
MstI2cRequest * mstI2cRequest;

DeviceImpl * parent; // Upstream parent device
DeviceImpl * children[16];
PortMap portMap;
Expand Down Expand Up @@ -461,6 +464,13 @@ namespace DisplayPort
// the MOT bit. It is needed for some special cases where the MOT bit shouldn't
// be set but some customers need it to please their monitors.
//
// Start and poll under the caller's lock; wait outside that lock.
virtual bool startMstI2cTransfer(NvU64 id, unsigned writeAddress,
NvU8 *writeData, unsigned writeSize,
unsigned readAddress, unsigned readSize);
virtual bool pollMstI2cTransfer(NvU64 id, NvU8 *readData,
unsigned readSize, bool *complete);
virtual void cancelMstI2cTransfer(NvU64 id);
virtual bool getI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false);
virtual bool setI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false);
virtual bool getRawEpr(unsigned * totalEpr, unsigned * freeEpr, rawEprState eprState);
Expand Down
Loading