Commit afd05583 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: das1800: fix analog input sample munging

The analog input samples are 2's complement when the inputs are configured
for bipolar ranges and offset binary when they are unipolar ranges. For
bipolar ranges the sample needs to be munged to the offset binary format
that comedi uses.

The (*insn_read) does the munging correctly but the async command support
incorrectly munges the data for both bipolar and unipolar ranges when
reading the remaining samples from the fifo in das1800_handle_fifo_not_empty().

Add a (*munge) function to the analog input subdevice so that the samples
are correctly munged when they are added to the async buffer by
comedi_buf_write_samples(). Add a flag to the private data and set it in
the (*do_cmd) so that the munging is only done for bipolar ranges.

Clarify the code by using the core helper functions to check the range and
do the munging.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d375278d
...@@ -107,7 +107,6 @@ Unipolar and bipolar ranges cannot be mixed in the channel/gain list. ...@@ -107,7 +107,6 @@ Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
/* misc. defines */ /* misc. defines */
#define DAS1800_SIZE 16 /* uses 16 io addresses */ #define DAS1800_SIZE 16 /* uses 16 io addresses */
#define FIFO_SIZE 1024 /* 1024 sample fifo */ #define FIFO_SIZE 1024 /* 1024 sample fifo */
#define UNIPOLAR 0x4 /* bit that determines whether input range is uni/bipolar */
#define DMA_BUF_SIZE 0x1ff00 /* size in bytes of dma buffers */ #define DMA_BUF_SIZE 0x1ff00 /* size in bytes of dma buffers */
/* Registers for the das1800 */ /* Registers for the das1800 */
...@@ -429,6 +428,7 @@ struct das1800_private { ...@@ -429,6 +428,7 @@ struct das1800_private {
unsigned long iobase2; /* secondary io address used for analog out on 'ao' boards */ unsigned long iobase2; /* secondary io address used for analog out on 'ao' boards */
unsigned short ao_update_bits; /* remembers the last write to the unsigned short ao_update_bits; /* remembers the last write to the
* 'update' dac */ * 'update' dac */
bool ai_is_unipolar;
}; };
/* analog out range for 'ao' boards */ /* analog out range for 'ao' boards */
...@@ -441,29 +441,21 @@ static const struct comedi_lrange range_ao_2 = { ...@@ -441,29 +441,21 @@ static const struct comedi_lrange range_ao_2 = {
}; };
*/ */
static inline uint16_t munge_bipolar_sample(const struct comedi_device *dev, static void das1800_ai_munge(struct comedi_device *dev,
uint16_t sample) struct comedi_subdevice *s,
{ void *data, unsigned int num_bytes,
const struct das1800_board *board = dev->board_ptr; unsigned int start_chan_index)
sample += 1 << (board->resolution - 1);
return sample;
}
static void munge_data(struct comedi_device *dev, uint16_t *array,
unsigned int num_elements)
{ {
struct das1800_private *devpriv = dev->private;
unsigned short *array = data;
unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
unsigned int i; unsigned int i;
int unipolar;
/* see if card is using a unipolar or bipolar range so we can munge data correctly */ if (devpriv->ai_is_unipolar)
unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; return;
/* convert to unsigned type if we are in a bipolar mode */ for (i = 0; i < num_samples; i++)
if (!unipolar) { array[i] = comedi_offset_munge(s, array[i]);
for (i = 0; i < num_elements; i++)
array[i] = munge_bipolar_sample(dev, array[i]);
}
} }
static void das1800_handle_fifo_half_full(struct comedi_device *dev, static void das1800_handle_fifo_half_full(struct comedi_device *dev,
...@@ -473,7 +465,6 @@ static void das1800_handle_fifo_half_full(struct comedi_device *dev, ...@@ -473,7 +465,6 @@ static void das1800_handle_fifo_half_full(struct comedi_device *dev,
unsigned int nsamples = comedi_nsamples_left(s, FIFO_SIZE / 2); unsigned int nsamples = comedi_nsamples_left(s, FIFO_SIZE / 2);
insw(dev->iobase + DAS1800_FIFO, devpriv->fifo_buf, nsamples); insw(dev->iobase + DAS1800_FIFO, devpriv->fifo_buf, nsamples);
munge_data(dev, devpriv->fifo_buf, nsamples);
comedi_buf_write_samples(s, devpriv->fifo_buf, nsamples); comedi_buf_write_samples(s, devpriv->fifo_buf, nsamples);
} }
...@@ -482,14 +473,9 @@ static void das1800_handle_fifo_not_empty(struct comedi_device *dev, ...@@ -482,14 +473,9 @@ static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
{ {
struct comedi_cmd *cmd = &s->async->cmd; struct comedi_cmd *cmd = &s->async->cmd;
unsigned short dpnt; unsigned short dpnt;
int unipolar;
unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
while (inb(dev->iobase + DAS1800_STATUS) & FNE) { while (inb(dev->iobase + DAS1800_STATUS) & FNE) {
dpnt = inw(dev->iobase + DAS1800_FIFO); dpnt = inw(dev->iobase + DAS1800_FIFO);
/* convert to unsigned type */
dpnt = munge_bipolar_sample(dev, dpnt);
comedi_buf_write_samples(s, &dpnt, 1); comedi_buf_write_samples(s, &dpnt, 1);
if (cmd->stop_src == TRIG_COUNT && if (cmd->stop_src == TRIG_COUNT &&
...@@ -511,7 +497,6 @@ static void das1800_flush_dma_channel(struct comedi_device *dev, ...@@ -511,7 +497,6 @@ static void das1800_flush_dma_channel(struct comedi_device *dev,
nsamples = comedi_bytes_to_samples(s, nbytes); nsamples = comedi_bytes_to_samples(s, nbytes);
nsamples = comedi_nsamples_left(s, nsamples); nsamples = comedi_nsamples_left(s, nsamples);
munge_data(dev, desc->virt_addr, nsamples);
comedi_buf_write_samples(s, desc->virt_addr, nsamples); comedi_buf_write_samples(s, desc->virt_addr, nsamples);
} }
...@@ -707,13 +692,14 @@ static int das1800_ai_check_chanlist(struct comedi_device *dev, ...@@ -707,13 +692,14 @@ static int das1800_ai_check_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_cmd *cmd) struct comedi_cmd *cmd)
{ {
unsigned int unipolar0 = CR_RANGE(cmd->chanlist[0]) & UNIPOLAR; unsigned int range = CR_RANGE(cmd->chanlist[0]);
bool unipolar0 = comedi_range_is_unipolar(s, range);
int i; int i;
for (i = 1; i < cmd->chanlist_len; i++) { for (i = 1; i < cmd->chanlist_len; i++) {
unsigned int unipolar = CR_RANGE(cmd->chanlist[i]) & UNIPOLAR; range = CR_RANGE(cmd->chanlist[i]);
if (unipolar != unipolar0) { if (unipolar0 != comedi_range_is_unipolar(s, range)) {
dev_dbg(dev->class_dev, dev_dbg(dev->class_dev,
"unipolar and bipolar ranges cannot be mixed in the chanlist\n"); "unipolar and bipolar ranges cannot be mixed in the chanlist\n");
return -EINVAL; return -EINVAL;
...@@ -852,23 +838,24 @@ static int control_a_bits(const struct comedi_cmd *cmd) ...@@ -852,23 +838,24 @@ static int control_a_bits(const struct comedi_cmd *cmd)
} }
/* returns appropriate bits for control register c, depending on command */ /* returns appropriate bits for control register c, depending on command */
static int control_c_bits(const struct comedi_cmd *cmd) static int control_c_bits(struct comedi_subdevice *s,
const struct comedi_cmd *cmd)
{ {
unsigned int range = CR_RANGE(cmd->chanlist[0]);
unsigned int aref = CR_AREF(cmd->chanlist[0]);
int control_c; int control_c;
int aref;
/* set clock source to internal or external, select analog reference, /* set clock source to internal or external, select analog reference,
* select unipolar / bipolar * select unipolar / bipolar
*/ */
aref = CR_AREF(cmd->chanlist[0]);
control_c = UQEN; /* enable upper qram addresses */ control_c = UQEN; /* enable upper qram addresses */
if (aref != AREF_DIFF) if (aref != AREF_DIFF)
control_c |= SD; control_c |= SD;
if (aref == AREF_COMMON) if (aref == AREF_COMMON)
control_c |= CMEN; control_c |= CMEN;
/* if a unipolar range was selected */ if (comedi_range_is_unipolar(s, range))
if (CR_RANGE(cmd->chanlist[0]) & UNIPOLAR)
control_c |= UB; control_c |= UB;
switch (cmd->scan_begin_src) { switch (cmd->scan_begin_src) {
case TRIG_FOLLOW: /* not in burst mode */ case TRIG_FOLLOW: /* not in burst mode */
switch (cmd->convert_src) { switch (cmd->convert_src) {
...@@ -994,6 +981,7 @@ static int das1800_ai_do_cmd(struct comedi_device *dev, ...@@ -994,6 +981,7 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
int control_a, control_c; int control_a, control_c;
struct comedi_async *async = s->async; struct comedi_async *async = s->async;
const struct comedi_cmd *cmd = &async->cmd; const struct comedi_cmd *cmd = &async->cmd;
unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
/* disable dma on CMDF_WAKE_EOS, or CMDF_PRIORITY /* disable dma on CMDF_WAKE_EOS, or CMDF_PRIORITY
* (because dma in handler is unsafe at hard real-time priority) */ * (because dma in handler is unsafe at hard real-time priority) */
...@@ -1012,9 +1000,11 @@ static int das1800_ai_do_cmd(struct comedi_device *dev, ...@@ -1012,9 +1000,11 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
das1800_cancel(dev, s); das1800_cancel(dev, s);
devpriv->ai_is_unipolar = comedi_range_is_unipolar(s, range0);
/* determine proper bits for control registers */ /* determine proper bits for control registers */
control_a = control_a_bits(cmd); control_a = control_a_bits(cmd);
control_c = control_c_bits(cmd); control_c = control_c_bits(s, cmd);
/* setup card and start */ /* setup card and start */
program_chanlist(dev, cmd); program_chanlist(dev, cmd);
...@@ -1052,23 +1042,24 @@ static int das1800_ai_rinsn(struct comedi_device *dev, ...@@ -1052,23 +1042,24 @@ static int das1800_ai_rinsn(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data) struct comedi_insn *insn, unsigned int *data)
{ {
const struct das1800_board *board = dev->board_ptr; unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int range = CR_RANGE(insn->chanspec);
unsigned int aref = CR_AREF(insn->chanspec);
bool is_unipolar = comedi_range_is_unipolar(s, range);
int i, n; int i, n;
int chan, range, aref, chan_range; int chan_range;
int timeout = 1000; int timeout = 1000;
unsigned short dpnt; unsigned short dpnt;
int conv_flags = 0; int conv_flags = 0;
unsigned long irq_flags; unsigned long irq_flags;
/* set up analog reference and unipolar / bipolar mode */ /* set up analog reference and unipolar / bipolar mode */
aref = CR_AREF(insn->chanspec);
conv_flags |= UQEN; conv_flags |= UQEN;
if (aref != AREF_DIFF) if (aref != AREF_DIFF)
conv_flags |= SD; conv_flags |= SD;
if (aref == AREF_COMMON) if (aref == AREF_COMMON)
conv_flags |= CMEN; conv_flags |= CMEN;
/* if a unipolar range was selected */ if (is_unipolar)
if (CR_RANGE(insn->chanspec) & UNIPOLAR)
conv_flags |= UB; conv_flags |= UB;
outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */ outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */
...@@ -1076,10 +1067,7 @@ static int das1800_ai_rinsn(struct comedi_device *dev, ...@@ -1076,10 +1067,7 @@ static int das1800_ai_rinsn(struct comedi_device *dev,
outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */
outb(FFEN, dev->iobase + DAS1800_CONTROL_A); outb(FFEN, dev->iobase + DAS1800_CONTROL_A);
chan = CR_CHAN(insn->chanspec); chan_range = chan | ((range & 0x3) << 8);
/* mask of unipolar/bipolar bit from range */
range = CR_RANGE(insn->chanspec) & 0x3;
chan_range = chan | (range << 8);
spin_lock_irqsave(&dev->spinlock, irq_flags); spin_lock_irqsave(&dev->spinlock, irq_flags);
outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */ outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /* set QRAM address start */ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /* set QRAM address start */
...@@ -1100,9 +1088,8 @@ static int das1800_ai_rinsn(struct comedi_device *dev, ...@@ -1100,9 +1088,8 @@ static int das1800_ai_rinsn(struct comedi_device *dev,
goto exit; goto exit;
} }
dpnt = inw(dev->iobase + DAS1800_FIFO); dpnt = inw(dev->iobase + DAS1800_FIFO);
/* shift data to offset binary for bipolar ranges */ if (!is_unipolar)
if ((conv_flags & UB) == 0) dpnt = comedi_offset_munge(s, dpnt);
dpnt += 1 << (board->resolution - 1);
data[n] = dpnt; data[n] = dpnt;
} }
exit: exit:
...@@ -1384,6 +1371,7 @@ static int das1800_attach(struct comedi_device *dev, ...@@ -1384,6 +1371,7 @@ static int das1800_attach(struct comedi_device *dev,
s->do_cmdtest = das1800_ai_do_cmdtest; s->do_cmdtest = das1800_ai_do_cmdtest;
s->poll = das1800_ai_poll; s->poll = das1800_ai_poll;
s->cancel = das1800_cancel; s->cancel = das1800_cancel;
s->munge = das1800_ai_munge;
} }
/* analog out */ /* analog out */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment