Subversion Repositories Kolibri OS

Rev

Rev 6105 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2015 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: Dave Airlie
  23.  */
  24. #include <drm/drmP.h>
  25. #include <drm/radeon_drm.h>
  26. #include "radeon.h"
  27. #include "nid.h"
  28.  
  29. #define AUX_RX_ERROR_FLAGS (AUX_SW_RX_OVERFLOW |             \
  30.                             AUX_SW_RX_HPD_DISCON |           \
  31.                             AUX_SW_RX_PARTIAL_BYTE |         \
  32.                             AUX_SW_NON_AUX_MODE |            \
  33.                             AUX_SW_RX_SYNC_INVALID_L |       \
  34.                             AUX_SW_RX_SYNC_INVALID_H |       \
  35.                             AUX_SW_RX_INVALID_START |        \
  36.                             AUX_SW_RX_RECV_NO_DET |          \
  37.                             AUX_SW_RX_RECV_INVALID_H |       \
  38.                             AUX_SW_RX_RECV_INVALID_V)
  39.  
  40. #define AUX_SW_REPLY_GET_BYTE_COUNT(x) (((x) >> 24) & 0x1f)
  41.  
  42. #define BARE_ADDRESS_SIZE 3
  43.  
  44. static const u32 aux_offset[] =
  45. {
  46.         0x6200 - 0x6200,
  47.         0x6250 - 0x6200,
  48.         0x62a0 - 0x6200,
  49.         0x6300 - 0x6200,
  50.         0x6350 - 0x6200,
  51.         0x63a0 - 0x6200,
  52. };
  53.  
  54. ssize_t
  55. radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
  56. {
  57.         struct radeon_i2c_chan *chan =
  58.                 container_of(aux, struct radeon_i2c_chan, aux);
  59.         struct drm_device *dev = chan->dev;
  60.         struct radeon_device *rdev = dev->dev_private;
  61.         int ret = 0, i;
  62.         uint32_t tmp, ack = 0;
  63.         int instance = chan->rec.i2c_id & 0xf;
  64.         u8 byte;
  65.         u8 *buf = msg->buffer;
  66.         int retry_count = 0;
  67.         int bytes;
  68.         int msize;
  69.         bool is_write = false;
  70.  
  71.         if (WARN_ON(msg->size > 16))
  72.                 return -E2BIG;
  73.  
  74.         switch (msg->request & ~DP_AUX_I2C_MOT) {
  75.         case DP_AUX_NATIVE_WRITE:
  76.         case DP_AUX_I2C_WRITE:
  77.                 is_write = true;
  78.                 break;
  79.         case DP_AUX_NATIVE_READ:
  80.         case DP_AUX_I2C_READ:
  81.                 break;
  82.         default:
  83.                 return -EINVAL;
  84.         }
  85.  
  86.         /* work out two sizes required */
  87.         msize = 0;
  88.         bytes = BARE_ADDRESS_SIZE;
  89.         if (msg->size) {
  90.                 msize = msg->size - 1;
  91.                 bytes++;
  92.                 if (is_write)
  93.                         bytes += msg->size;
  94.         }
  95.  
  96.         mutex_lock(&chan->mutex);
  97.  
  98.         /* switch the pad to aux mode */
  99.         tmp = RREG32(chan->rec.mask_clk_reg);
  100.         tmp |= (1 << 16);
  101.         WREG32(chan->rec.mask_clk_reg, tmp);
  102.  
  103.         /* setup AUX control register with correct HPD pin */
  104.         tmp = RREG32(AUX_CONTROL + aux_offset[instance]);
  105.  
  106.         tmp &= AUX_HPD_SEL(0x7);
  107.         tmp |= AUX_HPD_SEL(chan->rec.hpd);
  108.         tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1);
  109.  
  110.         WREG32(AUX_CONTROL + aux_offset[instance], tmp);
  111.  
  112.         /* atombios appears to write this twice lets copy it */
  113.         WREG32(AUX_SW_CONTROL + aux_offset[instance],
  114.                AUX_SW_WR_BYTES(bytes));
  115.         WREG32(AUX_SW_CONTROL + aux_offset[instance],
  116.                AUX_SW_WR_BYTES(bytes));
  117.  
  118.         /* write the data header into the registers */
  119.         /* request, address, msg size */
  120.         byte = (msg->request << 4) | ((msg->address >> 16) & 0xf);
  121.         WREG32(AUX_SW_DATA + aux_offset[instance],
  122.                AUX_SW_DATA_MASK(byte) | AUX_SW_AUTOINCREMENT_DISABLE);
  123.  
  124.         byte = (msg->address >> 8) & 0xff;
  125.         WREG32(AUX_SW_DATA + aux_offset[instance],
  126.                AUX_SW_DATA_MASK(byte));
  127.  
  128.         byte = msg->address & 0xff;
  129.         WREG32(AUX_SW_DATA + aux_offset[instance],
  130.                AUX_SW_DATA_MASK(byte));
  131.  
  132.         byte = msize;
  133.         WREG32(AUX_SW_DATA + aux_offset[instance],
  134.                AUX_SW_DATA_MASK(byte));
  135.  
  136.         /* if we are writing - write the msg buffer */
  137.         if (is_write) {
  138.                 for (i = 0; i < msg->size; i++) {
  139.                         WREG32(AUX_SW_DATA + aux_offset[instance],
  140.                                AUX_SW_DATA_MASK(buf[i]));
  141.                 }
  142.         }
  143.  
  144.         /* clear the ACK */
  145.         WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK);
  146.  
  147.         /* write the size and GO bits */
  148.         WREG32(AUX_SW_CONTROL + aux_offset[instance],
  149.                AUX_SW_WR_BYTES(bytes) | AUX_SW_GO);
  150.  
  151.         /* poll the status registers - TODO irq support */
  152.         do {
  153.                 tmp = RREG32(AUX_SW_STATUS + aux_offset[instance]);
  154.                 if (tmp & AUX_SW_DONE) {
  155.                         break;
  156.                 }
  157.                 usleep_range(100, 200);
  158.         } while (retry_count++ < 1000);
  159.  
  160.         if (retry_count >= 1000) {
  161.                 DRM_ERROR("auxch hw never signalled completion, error %08x\n", tmp);
  162.                 ret = -EIO;
  163.                 goto done;
  164.         }
  165.  
  166.         if (tmp & AUX_SW_RX_TIMEOUT) {
  167.                 DRM_DEBUG_KMS("dp_aux_ch timed out\n");
  168.                 ret = -ETIMEDOUT;
  169.                 goto done;
  170.         }
  171.         if (tmp & AUX_RX_ERROR_FLAGS) {
  172.                 DRM_DEBUG_KMS("dp_aux_ch flags not zero: %08x\n", tmp);
  173.                 ret = -EIO;
  174.                 goto done;
  175.         }
  176.  
  177.         bytes = AUX_SW_REPLY_GET_BYTE_COUNT(tmp);
  178.         if (bytes) {
  179.                 WREG32(AUX_SW_DATA + aux_offset[instance],
  180.                        AUX_SW_DATA_RW | AUX_SW_AUTOINCREMENT_DISABLE);
  181.  
  182.                 tmp = RREG32(AUX_SW_DATA + aux_offset[instance]);
  183.                 ack = (tmp >> 8) & 0xff;
  184.  
  185.                 for (i = 0; i < bytes - 1; i++) {
  186.                         tmp = RREG32(AUX_SW_DATA + aux_offset[instance]);
  187.                         if (buf)
  188.                                 buf[i] = (tmp >> 8) & 0xff;
  189.                 }
  190.                 if (buf)
  191.                         ret = bytes - 1;
  192.         }
  193.  
  194.         WREG32(AUX_SW_INTERRUPT_CONTROL + aux_offset[instance], AUX_SW_DONE_ACK);
  195.  
  196.         if (is_write)
  197.                 ret = msg->size;
  198. done:
  199.         mutex_unlock(&chan->mutex);
  200.  
  201.         if (ret >= 0)
  202.                 msg->reply = ack >> 4;
  203.         return ret;
  204. }
  205.