5757#include <sound/pcm_drm_eld.h>
5858#include <sound/pcm_params.h>
5959#include <sound/soc.h>
60+ #include "media/cec.h"
6061#include "vc4_drv.h"
6162#include "vc4_regs.h"
6263
64+ #define HSM_CLOCK_FREQ 163682864
65+ #define CEC_CLOCK_FREQ 40000
66+ #define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
67+
6368/* HDMI audio information */
6469struct vc4_hdmi_audio {
6570 struct snd_soc_card card ;
@@ -85,6 +90,11 @@ struct vc4_hdmi {
8590 int hpd_gpio ;
8691 bool hpd_active_low ;
8792
93+ struct cec_adapter * cec_adap ;
94+ struct cec_msg cec_rx_msg ;
95+ bool cec_tx_ok ;
96+ bool cec_irq_was_rx ;
97+
8898 struct clk * pixel_clock ;
8999 struct clk * hsm_clock ;
90100};
@@ -149,6 +159,23 @@ static const struct {
149159 HDMI_REG (VC4_HDMI_VERTB1 ),
150160 HDMI_REG (VC4_HDMI_TX_PHY_RESET_CTL ),
151161 HDMI_REG (VC4_HDMI_TX_PHY_CTL0 ),
162+
163+ HDMI_REG (VC4_HDMI_CEC_CNTRL_1 ),
164+ HDMI_REG (VC4_HDMI_CEC_CNTRL_2 ),
165+ HDMI_REG (VC4_HDMI_CEC_CNTRL_3 ),
166+ HDMI_REG (VC4_HDMI_CEC_CNTRL_4 ),
167+ HDMI_REG (VC4_HDMI_CEC_CNTRL_5 ),
168+ HDMI_REG (VC4_HDMI_CPU_STATUS ),
169+ HDMI_REG (VC4_HDMI_CPU_MASK_STATUS ),
170+
171+ HDMI_REG (VC4_HDMI_CEC_RX_DATA_1 ),
172+ HDMI_REG (VC4_HDMI_CEC_RX_DATA_2 ),
173+ HDMI_REG (VC4_HDMI_CEC_RX_DATA_3 ),
174+ HDMI_REG (VC4_HDMI_CEC_RX_DATA_4 ),
175+ HDMI_REG (VC4_HDMI_CEC_TX_DATA_1 ),
176+ HDMI_REG (VC4_HDMI_CEC_TX_DATA_2 ),
177+ HDMI_REG (VC4_HDMI_CEC_TX_DATA_3 ),
178+ HDMI_REG (VC4_HDMI_CEC_TX_DATA_4 ),
152179};
153180
154181static const struct {
@@ -216,17 +243,17 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
216243 if (gpio_get_value_cansleep (vc4 -> hdmi -> hpd_gpio ) ^
217244 vc4 -> hdmi -> hpd_active_low )
218245 return connector_status_connected ;
219- else
220- return connector_status_disconnected ;
246+ cec_phys_addr_invalidate ( vc4 -> hdmi -> cec_adap );
247+ return connector_status_disconnected ;
221248 }
222249
223250 if (drm_probe_ddc (vc4 -> hdmi -> ddc ))
224251 return connector_status_connected ;
225252
226253 if (HDMI_READ (VC4_HDMI_HOTPLUG ) & VC4_HDMI_HOTPLUG_CONNECTED )
227254 return connector_status_connected ;
228- else
229- return connector_status_disconnected ;
255+ cec_phys_addr_invalidate ( vc4 -> hdmi -> cec_adap );
256+ return connector_status_disconnected ;
230257}
231258
232259static void vc4_hdmi_connector_destroy (struct drm_connector * connector )
@@ -247,6 +274,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
247274 struct edid * edid ;
248275
249276 edid = drm_get_edid (connector , vc4 -> hdmi -> ddc );
277+ cec_s_phys_addr_from_edid (vc4 -> hdmi -> cec_adap , edid );
250278 if (!edid )
251279 return - ENODEV ;
252280
@@ -1121,6 +1149,159 @@ static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi)
11211149 snd_soc_unregister_codec (dev );
11221150}
11231151
1152+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
1153+ static irqreturn_t vc4_cec_irq_handler_thread (int irq , void * priv )
1154+ {
1155+ struct vc4_dev * vc4 = priv ;
1156+ struct vc4_hdmi * hdmi = vc4 -> hdmi ;
1157+
1158+ if (hdmi -> cec_irq_was_rx ) {
1159+ if (hdmi -> cec_rx_msg .len )
1160+ cec_received_msg (hdmi -> cec_adap , & hdmi -> cec_rx_msg );
1161+ } else if (hdmi -> cec_tx_ok ) {
1162+ cec_transmit_done (hdmi -> cec_adap , CEC_TX_STATUS_OK ,
1163+ 0 , 0 , 0 , 0 );
1164+ } else {
1165+ /*
1166+ * This CEC implementation makes 1 retry, so if we
1167+ * get a NACK, then that means it made 2 attempts.
1168+ */
1169+ cec_transmit_done (hdmi -> cec_adap , CEC_TX_STATUS_NACK ,
1170+ 0 , 2 , 0 , 0 );
1171+ }
1172+ return IRQ_HANDLED ;
1173+ }
1174+
1175+ static void vc4_cec_read_msg (struct vc4_dev * vc4 , u32 cntrl1 )
1176+ {
1177+ struct cec_msg * msg = & vc4 -> hdmi -> cec_rx_msg ;
1178+ unsigned int i ;
1179+
1180+ msg -> len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK ) >>
1181+ VC4_HDMI_CEC_REC_WRD_CNT_SHIFT );
1182+ for (i = 0 ; i < msg -> len ; i += 4 ) {
1183+ u32 val = HDMI_READ (VC4_HDMI_CEC_RX_DATA_1 + i );
1184+
1185+ msg -> msg [i ] = val & 0xff ;
1186+ msg -> msg [i + 1 ] = (val >> 8 ) & 0xff ;
1187+ msg -> msg [i + 2 ] = (val >> 16 ) & 0xff ;
1188+ msg -> msg [i + 3 ] = (val >> 24 ) & 0xff ;
1189+ }
1190+ }
1191+
1192+ static irqreturn_t vc4_cec_irq_handler (int irq , void * priv )
1193+ {
1194+ struct vc4_dev * vc4 = priv ;
1195+ struct vc4_hdmi * hdmi = vc4 -> hdmi ;
1196+ u32 stat = HDMI_READ (VC4_HDMI_CPU_STATUS );
1197+ u32 cntrl1 , cntrl5 ;
1198+
1199+ if (!(stat & VC4_HDMI_CPU_CEC ))
1200+ return IRQ_NONE ;
1201+ hdmi -> cec_rx_msg .len = 0 ;
1202+ cntrl1 = HDMI_READ (VC4_HDMI_CEC_CNTRL_1 );
1203+ cntrl5 = HDMI_READ (VC4_HDMI_CEC_CNTRL_5 );
1204+ hdmi -> cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT ;
1205+ if (hdmi -> cec_irq_was_rx ) {
1206+ vc4_cec_read_msg (vc4 , cntrl1 );
1207+ cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF ;
1208+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 , cntrl1 );
1209+ cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF ;
1210+ } else {
1211+ hdmi -> cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD ;
1212+ cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN ;
1213+ }
1214+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 , cntrl1 );
1215+ HDMI_WRITE (VC4_HDMI_CPU_CLEAR , VC4_HDMI_CPU_CEC );
1216+
1217+ return IRQ_WAKE_THREAD ;
1218+ }
1219+
1220+ static int vc4_hdmi_cec_adap_enable (struct cec_adapter * adap , bool enable )
1221+ {
1222+ struct vc4_dev * vc4 = cec_get_drvdata (adap );
1223+ /* clock period in microseconds */
1224+ const u32 usecs = 1000000 / CEC_CLOCK_FREQ ;
1225+ u32 val = HDMI_READ (VC4_HDMI_CEC_CNTRL_5 );
1226+
1227+ val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
1228+ VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
1229+ VC4_HDMI_CEC_CNT_TO_4500_US_MASK );
1230+ val |= ((4700 / usecs ) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT ) |
1231+ ((4500 / usecs ) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT );
1232+
1233+ if (enable ) {
1234+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_5 , val |
1235+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET );
1236+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_5 , val );
1237+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_2 ,
1238+ ((1500 / usecs ) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT ) |
1239+ ((1300 / usecs ) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT ) |
1240+ ((800 / usecs ) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT ) |
1241+ ((600 / usecs ) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT ) |
1242+ ((400 / usecs ) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT ));
1243+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_3 ,
1244+ ((2750 / usecs ) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT ) |
1245+ ((2400 / usecs ) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT ) |
1246+ ((2050 / usecs ) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT ) |
1247+ ((1700 / usecs ) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT ));
1248+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_4 ,
1249+ ((4300 / usecs ) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT ) |
1250+ ((3900 / usecs ) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT ) |
1251+ ((3600 / usecs ) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT ) |
1252+ ((3500 / usecs ) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT ));
1253+
1254+ HDMI_WRITE (VC4_HDMI_CPU_MASK_CLEAR , VC4_HDMI_CPU_CEC );
1255+ } else {
1256+ HDMI_WRITE (VC4_HDMI_CPU_MASK_SET , VC4_HDMI_CPU_CEC );
1257+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_5 , val |
1258+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET );
1259+ }
1260+ return 0 ;
1261+ }
1262+
1263+ static int vc4_hdmi_cec_adap_log_addr (struct cec_adapter * adap , u8 log_addr )
1264+ {
1265+ struct vc4_dev * vc4 = cec_get_drvdata (adap );
1266+
1267+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 ,
1268+ (HDMI_READ (VC4_HDMI_CEC_CNTRL_1 ) & ~VC4_HDMI_CEC_ADDR_MASK ) |
1269+ (log_addr & 0xf ) << VC4_HDMI_CEC_ADDR_SHIFT );
1270+ return 0 ;
1271+ }
1272+
1273+ static int vc4_hdmi_cec_adap_transmit (struct cec_adapter * adap , u8 attempts ,
1274+ u32 signal_free_time , struct cec_msg * msg )
1275+ {
1276+ struct vc4_dev * vc4 = cec_get_drvdata (adap );
1277+ u32 val ;
1278+ unsigned int i ;
1279+
1280+ for (i = 0 ; i < msg -> len ; i += 4 )
1281+ HDMI_WRITE (VC4_HDMI_CEC_TX_DATA_1 + i ,
1282+ (msg -> msg [i ]) |
1283+ (msg -> msg [i + 1 ] << 8 ) |
1284+ (msg -> msg [i + 2 ] << 16 ) |
1285+ (msg -> msg [i + 3 ] << 24 ));
1286+
1287+ val = HDMI_READ (VC4_HDMI_CEC_CNTRL_1 );
1288+ val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN ;
1289+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 , val );
1290+ val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK ;
1291+ val |= (msg -> len - 1 ) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT ;
1292+ val |= VC4_HDMI_CEC_START_XMIT_BEGIN ;
1293+
1294+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 , val );
1295+ return 0 ;
1296+ }
1297+
1298+ static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
1299+ .adap_enable = vc4_hdmi_cec_adap_enable ,
1300+ .adap_log_addr = vc4_hdmi_cec_adap_log_addr ,
1301+ .adap_transmit = vc4_hdmi_cec_adap_transmit ,
1302+ };
1303+ #endif
1304+
11241305static int vc4_hdmi_bind (struct device * dev , struct device * master , void * data )
11251306{
11261307 struct platform_device * pdev = to_platform_device (dev );
@@ -1180,7 +1361,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
11801361 * needs to be a bit higher than the pixel clock rate
11811362 * (generally 148.5Mhz).
11821363 */
1183- ret = clk_set_rate (hdmi -> hsm_clock , 163682864 );
1364+ ret = clk_set_rate (hdmi -> hsm_clock , HSM_CLOCK_FREQ );
11841365 if (ret ) {
11851366 DRM_ERROR ("Failed to set HSM clock rate: %d\n" , ret );
11861367 goto err_put_i2c ;
@@ -1231,13 +1412,50 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
12311412 ret = PTR_ERR (hdmi -> connector );
12321413 goto err_destroy_encoder ;
12331414 }
1415+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
1416+ hdmi -> cec_adap = cec_allocate_adapter (& vc4_hdmi_cec_adap_ops ,
1417+ vc4 , "vc4" ,
1418+ CEC_CAP_TRANSMIT |
1419+ CEC_CAP_LOG_ADDRS |
1420+ CEC_CAP_PASSTHROUGH |
1421+ CEC_CAP_RC , 1 );
1422+ ret = PTR_ERR_OR_ZERO (hdmi -> cec_adap );
1423+ if (ret < 0 )
1424+ goto err_destroy_conn ;
1425+ HDMI_WRITE (VC4_HDMI_CPU_MASK_SET , 0xffffffff );
1426+ value = HDMI_READ (VC4_HDMI_CEC_CNTRL_1 );
1427+ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK ;
1428+ /*
1429+ * Set the logical address to Unregistered and set the clock
1430+ * divider: the hsm_clock rate and this divider setting will
1431+ * give a 40 kHz CEC clock.
1432+ */
1433+ value |= VC4_HDMI_CEC_ADDR_MASK |
1434+ (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT );
1435+ HDMI_WRITE (VC4_HDMI_CEC_CNTRL_1 , value );
1436+ ret = devm_request_threaded_irq (dev , platform_get_irq (pdev , 0 ),
1437+ vc4_cec_irq_handler ,
1438+ vc4_cec_irq_handler_thread , 0 ,
1439+ "vc4 hdmi cec" , vc4 );
1440+ if (ret )
1441+ goto err_delete_cec_adap ;
1442+ ret = cec_register_adapter (hdmi -> cec_adap , dev );
1443+ if (ret < 0 )
1444+ goto err_delete_cec_adap ;
1445+ #endif
12341446
12351447 ret = vc4_hdmi_audio_init (hdmi );
12361448 if (ret )
12371449 goto err_destroy_encoder ;
12381450
12391451 return 0 ;
12401452
1453+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
1454+ err_delete_cec_adap :
1455+ cec_delete_adapter (hdmi -> cec_adap );
1456+ err_destroy_conn :
1457+ vc4_hdmi_connector_destroy (hdmi -> connector );
1458+ #endif
12411459err_destroy_encoder :
12421460 vc4_hdmi_encoder_destroy (hdmi -> encoder );
12431461err_unprepare_hsm :
@@ -1257,7 +1475,7 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
12571475 struct vc4_hdmi * hdmi = vc4 -> hdmi ;
12581476
12591477 vc4_hdmi_audio_cleanup (hdmi );
1260-
1478+ cec_unregister_adapter ( hdmi -> cec_adap );
12611479 vc4_hdmi_connector_destroy (hdmi -> connector );
12621480 vc4_hdmi_encoder_destroy (hdmi -> encoder );
12631481
0 commit comments