/*
* Copyright 2015 Red Hat Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*
* Authors : Dave Airlie
*/
#include <drm/radeon_drm.h>
#include "radeon.h"
#include "nid.h"
#define AUX_RX_ERROR_FLAGS (AUX_SW_RX_OVERFLOW | \
AUX_SW_RX_HPD_DISCON | \
AUX_SW_RX_PARTIAL_BYTE | \
AUX_SW_NON_AUX_MODE | \
AUX_SW_RX_SYNC_INVALID_L | \
AUX_SW_RX_SYNC_INVALID_H | \
AUX_SW_RX_INVALID_START | \
AUX_SW_RX_RECV_NO_DET | \
AUX_SW_RX_RECV_INVALID_H | \
AUX_SW_RX_RECV_INVALID_V)
#define AUX_SW_REPLY_GET_BYTE_COUNT(x) (((x) >> 24 ) & 0 x1f)
#define BARE_ADDRESS_SIZE 3
static const u32 aux_offset[] =
{
0 x6200 - 0 x6200,
0 x6250 - 0 x6200,
0 x62a0 - 0 x6200,
0 x6300 - 0 x6200,
0 x6350 - 0 x6200,
0 x63a0 - 0 x6200,
};
ssize_t
radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
struct radeon_i2c_chan *chan =
container_of(aux, struct radeon_i2c_chan, aux);
struct drm_device *dev = chan->dev;
struct radeon_device *rdev = dev->dev_private;
int ret = 0 , i;
uint32_t tmp, ack = 0 ;
int instance = chan->rec.i2c_id & 0 xf;
u8 byte;
u8 *buf = msg->buffer;
int retry_count = 0 ;
int bytes;
int msize;
bool is_write = false ;
if (WARN_ON(msg->size > 16 ))
return -E2BIG;
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
is_write = true ;
break ;
case DP_AUX_NATIVE_READ:
case DP_AUX_I2C_READ:
break ;
default :
return -EINVAL;
}
/* work out two sizes required */
msize = 0 ;
bytes = BARE_ADDRESS_SIZE;
if (msg->size) {
msize = msg->size - 1 ;
bytes++;
if (is_write)
bytes += msg->size;
}
mutex_lock(&chan->mutex);
/* switch the pad to aux mode */
tmp = RREG32(chan->rec.mask_clk_reg);
tmp |= (1 << 16 );
WREG32(chan->rec.mask_clk_reg, tmp);
/* setup AUX control register with correct HPD pin */
tmp = RREG32(AUX_CONTROL + aux_offset[instance]);
tmp &= AUX_HPD_SEL(0 x7);
tmp |= AUX_HPD_SEL(chan->rec.hpd);
tmp |= AUX_EN | AUX_LS_READ_EN;
WREG32(AUX_CONTROL + aux_offset[instance], tmp);
/* atombios appears to write this twice lets copy it */
WREG32(AUX_SW_CONTROL + aux_offset[instance],
AUX_SW_WR_BYTES(bytes));
WREG32(AUX_SW_CONTROL + aux_offset[instance],
AUX_SW_WR_BYTES(bytes));
/* write the data header into the registers */
/* request, address, msg size */
byte = (msg->request << 4 ) | ((msg->address >> 16 ) & 0 xf);
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_MASK(byte) | AUX_SW_AUTOINCREMENT_DISABLE);
byte = (msg->address >> 8 ) & 0 xff;
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_MASK(byte));
byte = msg->address & 0 xff;
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_MASK(byte));
byte = msize;
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_MASK(byte));
/* if we are writing - write the msg buffer */
if (is_write) {
for (i = 0 ; i < msg->size; i++) {
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_MASK(buf[i]));
}
}
/* clear the ACK */
WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK);
/* write the size and GO bits */
WREG32(AUX_SW_CONTROL + aux_offset[instance],
AUX_SW_WR_BYTES(bytes) | AUX_SW_GO);
/* poll the status registers - TODO irq support */
do {
tmp = RREG32(AUX_SW_STATUS + aux_offset[instance]);
if (tmp & AUX_SW_DONE) {
break ;
}
usleep_range(100 , 200 );
} while (retry_count++ < 1000 );
if (retry_count >= 1000 ) {
dev_err(rdev->dev, "auxch hw never signalled completion, error %08x\n" , tmp);
ret = -EIO;
goto done;
}
if (tmp & AUX_SW_RX_TIMEOUT) {
ret = -ETIMEDOUT;
goto done;
}
if (tmp & AUX_RX_ERROR_FLAGS) {
drm_dbg_kms_ratelimited(dev, "dp_aux_ch flags not zero: %08x\n" , tmp);
ret = -EIO;
goto done;
}
bytes = AUX_SW_REPLY_GET_BYTE_COUNT(tmp);
if (bytes) {
WREG32(AUX_SW_DATA + aux_offset[instance],
AUX_SW_DATA_RW | AUX_SW_AUTOINCREMENT_DISABLE);
tmp = RREG32(AUX_SW_DATA + aux_offset[instance]);
ack = (tmp >> 8 ) & 0 xff;
for (i = 0 ; i < bytes - 1 ; i++) {
tmp = RREG32(AUX_SW_DATA + aux_offset[instance]);
if (buf)
buf[i] = (tmp >> 8 ) & 0 xff;
}
if (buf)
ret = bytes - 1 ;
}
WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK);
if (is_write)
ret = msg->size;
done:
mutex_unlock(&chan->mutex);
if (ret >= 0 )
msg->reply = ack >> 4 ;
return ret;
}
Messung V0.5 in Prozent C=92 H=91 G=91
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland