18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Samsung LSI S5C73M3 8M pixel camera driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
68c2ecf20Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com>
78c2ecf20Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/sizes.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/media.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "s5c73m3.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const struct of_device_id s5c73m3_spi_ids[] = {
238c2ecf20Sopenharmony_ci	{ .compatible = "samsung,s5c73m3" },
248c2ecf20Sopenharmony_ci	{ }
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5c73m3_spi_ids);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cienum spi_direction {
298c2ecf20Sopenharmony_ci	SPI_DIR_RX,
308c2ecf20Sopenharmony_ci	SPI_DIR_TX
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int spi_xmit(struct spi_device *spi_dev, void *addr, const int len,
348c2ecf20Sopenharmony_ci							enum spi_direction dir)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct spi_message msg;
378c2ecf20Sopenharmony_ci	int r;
388c2ecf20Sopenharmony_ci	struct spi_transfer xfer = {
398c2ecf20Sopenharmony_ci		.len	= len,
408c2ecf20Sopenharmony_ci	};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (dir == SPI_DIR_TX)
438c2ecf20Sopenharmony_ci		xfer.tx_buf = addr;
448c2ecf20Sopenharmony_ci	else
458c2ecf20Sopenharmony_ci		xfer.rx_buf = addr;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (spi_dev == NULL) {
488c2ecf20Sopenharmony_ci		pr_err("SPI device is uninitialized\n");
498c2ecf20Sopenharmony_ci		return -ENODEV;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	spi_message_init(&msg);
538c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	r = spi_sync(spi_dev, &msg);
568c2ecf20Sopenharmony_ci	if (r < 0)
578c2ecf20Sopenharmony_ci		dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return r;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciint s5c73m3_spi_write(struct s5c73m3 *state, const void *addr,
638c2ecf20Sopenharmony_ci		      const unsigned int len, const unsigned int tx_size)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct spi_device *spi_dev = state->spi_dev;
668c2ecf20Sopenharmony_ci	u32 count = len / tx_size;
678c2ecf20Sopenharmony_ci	u32 extra = len % tx_size;
688c2ecf20Sopenharmony_ci	unsigned int i, j = 0;
698c2ecf20Sopenharmony_ci	u8 padding[32];
708c2ecf20Sopenharmony_ci	int r = 0;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	memset(padding, 0, sizeof(padding));
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
758c2ecf20Sopenharmony_ci		r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX);
768c2ecf20Sopenharmony_ci		if (r < 0)
778c2ecf20Sopenharmony_ci			return r;
788c2ecf20Sopenharmony_ci		j += tx_size;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (extra > 0) {
828c2ecf20Sopenharmony_ci		r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX);
838c2ecf20Sopenharmony_ci		if (r < 0)
848c2ecf20Sopenharmony_ci			return r;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciint s5c73m3_spi_read(struct s5c73m3 *state, void *addr,
918c2ecf20Sopenharmony_ci		     const unsigned int len, const unsigned int tx_size)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct spi_device *spi_dev = state->spi_dev;
948c2ecf20Sopenharmony_ci	u32 count = len / tx_size;
958c2ecf20Sopenharmony_ci	u32 extra = len % tx_size;
968c2ecf20Sopenharmony_ci	unsigned int i, j = 0;
978c2ecf20Sopenharmony_ci	int r = 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1008c2ecf20Sopenharmony_ci		r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX);
1018c2ecf20Sopenharmony_ci		if (r < 0)
1028c2ecf20Sopenharmony_ci			return r;
1038c2ecf20Sopenharmony_ci		j += tx_size;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (extra > 0)
1078c2ecf20Sopenharmony_ci		return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int s5c73m3_spi_probe(struct spi_device *spi)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	int r;
1158c2ecf20Sopenharmony_ci	struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3,
1168c2ecf20Sopenharmony_ci					     spidrv.driver);
1178c2ecf20Sopenharmony_ci	spi->bits_per_word = 32;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	r = spi_setup(spi);
1208c2ecf20Sopenharmony_ci	if (r < 0) {
1218c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "spi_setup() failed\n");
1228c2ecf20Sopenharmony_ci		return r;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	mutex_lock(&state->lock);
1268c2ecf20Sopenharmony_ci	state->spi_dev = spi;
1278c2ecf20Sopenharmony_ci	mutex_unlock(&state->lock);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n");
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int s5c73m3_spi_remove(struct spi_device *spi)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ciint s5c73m3_register_spi_driver(struct s5c73m3 *state)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct spi_driver *spidrv = &state->spidrv;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	spidrv->remove = s5c73m3_spi_remove;
1438c2ecf20Sopenharmony_ci	spidrv->probe = s5c73m3_spi_probe;
1448c2ecf20Sopenharmony_ci	spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
1458c2ecf20Sopenharmony_ci	spidrv->driver.of_match_table = s5c73m3_spi_ids;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return spi_register_driver(spidrv);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_civoid s5c73m3_unregister_spi_driver(struct s5c73m3 *state)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	spi_unregister_driver(&state->spidrv);
1538c2ecf20Sopenharmony_ci}
154