]> Git Repo - linux.git/blob - drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c
Merge tag 'for-5.13/libata-2021-04-27' of git://git.kernel.dk/linux-block
[linux.git] / drivers / gpu / drm / bridge / analogix / analogix-i2c-dptx.c
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Copyright(c) 2016, Analogix Semiconductor.
4  *
5  * Based on anx7808 driver obtained from chromeos with copyright:
6  * Copyright(c) 2013, Google Inc.
7  */
8 #include <linux/regmap.h>
9
10 #include <drm/drm.h>
11 #include <drm/drm_dp_helper.h>
12 #include <drm/drm_print.h>
13
14 #include "analogix-i2c-dptx.h"
15
16 #define AUX_WAIT_TIMEOUT_MS     15
17 #define AUX_CH_BUFFER_SIZE      16
18
19 static int anx_i2c_dp_clear_bits(struct regmap *map, u8 reg, u8 mask)
20 {
21         return regmap_update_bits(map, reg, mask, 0);
22 }
23
24 static bool anx_dp_aux_op_finished(struct regmap *map_dptx)
25 {
26         unsigned int value;
27         int err;
28
29         err = regmap_read(map_dptx, SP_DP_AUX_CH_CTRL2_REG, &value);
30         if (err < 0)
31                 return false;
32
33         return (value & SP_AUX_EN) == 0;
34 }
35
36 static int anx_dp_aux_wait(struct regmap *map_dptx)
37 {
38         unsigned long timeout;
39         unsigned int status;
40         int err;
41
42         timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
43
44         while (!anx_dp_aux_op_finished(map_dptx)) {
45                 if (time_after(jiffies, timeout)) {
46                         if (!anx_dp_aux_op_finished(map_dptx)) {
47                                 DRM_ERROR("Timed out waiting AUX to finish\n");
48                                 return -ETIMEDOUT;
49                         }
50
51                         break;
52                 }
53
54                 usleep_range(1000, 2000);
55         }
56
57         /* Read the AUX channel access status */
58         err = regmap_read(map_dptx, SP_AUX_CH_STATUS_REG, &status);
59         if (err < 0) {
60                 DRM_ERROR("Failed to read from AUX channel: %d\n", err);
61                 return err;
62         }
63
64         if (status & SP_AUX_STATUS) {
65                 DRM_ERROR("Failed to wait for AUX channel (status: %02x)\n",
66                           status);
67                 return -ETIMEDOUT;
68         }
69
70         return 0;
71 }
72
73 static int anx_dp_aux_address(struct regmap *map_dptx, unsigned int addr)
74 {
75         int err;
76
77         err = regmap_write(map_dptx, SP_AUX_ADDR_7_0_REG, addr & 0xff);
78         if (err)
79                 return err;
80
81         err = regmap_write(map_dptx, SP_AUX_ADDR_15_8_REG,
82                            (addr & 0xff00) >> 8);
83         if (err)
84                 return err;
85
86         /*
87          * DP AUX CH Address Register #2, only update bits[3:0]
88          * [7:4] RESERVED
89          * [3:0] AUX_ADDR[19:16], Register control AUX CH address.
90          */
91         err = regmap_update_bits(map_dptx, SP_AUX_ADDR_19_16_REG,
92                                  SP_AUX_ADDR_19_16_MASK,
93                                  (addr & 0xf0000) >> 16);
94
95         if (err)
96                 return err;
97
98         return 0;
99 }
100
101 ssize_t anx_dp_aux_transfer(struct regmap *map_dptx,
102                                 struct drm_dp_aux_msg *msg)
103 {
104         u8 ctrl1 = msg->request;
105         u8 ctrl2 = SP_AUX_EN;
106         u8 *buffer = msg->buffer;
107         int err;
108
109         /* The DP AUX transmit and receive buffer has 16 bytes. */
110         if (WARN_ON(msg->size > AUX_CH_BUFFER_SIZE))
111                 return -E2BIG;
112
113         /* Zero-sized messages specify address-only transactions. */
114         if (msg->size < 1)
115                 ctrl2 |= SP_ADDR_ONLY;
116         else    /* For non-zero-sized set the length field. */
117                 ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT;
118
119         if ((msg->size > 0) && ((msg->request & DP_AUX_I2C_READ) == 0)) {
120                 /* When WRITE | MOT write values to data buffer */
121                 err = regmap_bulk_write(map_dptx,
122                                         SP_DP_BUF_DATA0_REG, buffer,
123                                         msg->size);
124                 if (err)
125                         return err;
126         }
127
128         /* Write address and request */
129         err = anx_dp_aux_address(map_dptx, msg->address);
130         if (err)
131                 return err;
132
133         err = regmap_write(map_dptx, SP_DP_AUX_CH_CTRL1_REG, ctrl1);
134         if (err)
135                 return err;
136
137         /* Start transaction */
138         err = regmap_update_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG,
139                                  SP_ADDR_ONLY | SP_AUX_EN, ctrl2);
140         if (err)
141                 return err;
142
143         err = anx_dp_aux_wait(map_dptx);
144         if (err)
145                 return err;
146
147         msg->reply = DP_AUX_I2C_REPLY_ACK;
148
149         if ((msg->size > 0) && (msg->request & DP_AUX_I2C_READ)) {
150                 /* Read values from data buffer */
151                 err = regmap_bulk_read(map_dptx,
152                                        SP_DP_BUF_DATA0_REG, buffer,
153                                        msg->size);
154                 if (err)
155                         return err;
156         }
157
158         err = anx_i2c_dp_clear_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG,
159                                     SP_ADDR_ONLY);
160         if (err)
161                 return err;
162
163         return msg->size;
164 }
165 EXPORT_SYMBOL_GPL(anx_dp_aux_transfer);
This page took 0.044618 seconds and 4 git commands to generate.